简体   繁体   English

在 iOS 的表格视图单元格中更改披露指示器附件视图的颜色/视图的最佳方法是什么?

[英]Which is the best way to change the color/view of disclosure indicator accessory view in a table view cell in iOS?

I need to change the color of the disclosureIndicatorView accessory in a UITableViewCell .我需要改变的颜色disclosureIndicatorView在附件UITableViewCell I think there are two ways to get this done, but I'm not able to figure out which one's the optimum.我认为有两种方法可以完成这项工作,但我无法确定哪种方法是最佳的。 So here is what I think I can do.所以这就是我认为我能做的。

There is a property of UITableViewCell - accessoryView . UITableViewCell有一个属性—— accessoryView UITableViewCell So I can use setAccessoryView:(UIView *)view and pass view as the UIImageView holding the image that I want.所以我可以使用setAccessoryView:(UIView *)view并将视图作为UIImageView传递给我想要的图像。

I have written an utility class which creates the content view (stuff like background color, adding other stuff, etc) for my cell and I add this content view to the cell in UITableViewDelegate .我编写了一个实用程序类,它为我的单元格创建内容视图(如背景颜色、添加其他内容等),并将此内容视图添加到UITableViewDelegate的单元格。 The other option is to draw a UIImage overriding the drawRect method of CustomContentView utility class.另一种选择是绘制一个UIImage覆盖CustomContentView实用程序类的drawRect方法。

Performing option 1 - I can get the things done the apple way.执行选项 1 - 我可以用苹果的方式完成工作。 Just give them the view and they do the rest.只给他们意见,他们做剩下的。 But I guess adding a new UIView object to every row might turn out to be a heavy object allocation and decreasing the frame rate.但我想向每一行添加一个新的UIView对象可能会导致大量的对象分配并降低帧速率。 As compared to just a UIImage object in my contentView .与我的contentViewUIImage对象相比。 I believe UIImage is lighter than UIView .我相信UIImageUIView轻。

Please throw some light people and help me decide over it.请抛出一些轻松的人并帮助我决定它。

Great post on Cocoanetics that addresses this. Cocoanetics 上的好帖子解决了这个问题。 The UIControl class inherits the properties selected, enabled and highlighted Custom-Colored Disclosure Indicators UIControl 类继承了选择、启用和突出显示的属性Custom-Colored Disclosure Indicators

If you're interested in drawing the indicator, instead of using an image file, here's code I worked out to do so:如果您对绘制指标感兴趣,而不是使用图像文件,这里是我编写的代码:

// (x,y) is the tip of the arrow
CGFloat x = CGRectGetMaxX(self.bounds) - RIGHT_MARGIN;
CGFloat y = CGRectGetMidY(self.bounds);
const CGFloat R = 4.5;
CGContextRef ctxt = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(ctxt, x-R, y-R);
CGContextAddLineToPoint(ctxt, x, y);
CGContextAddLineToPoint(ctxt, x-R, y+R);
CGContextSetLineCap(ctxt, kCGLineCapSquare);
CGContextSetLineJoin(ctxt, kCGLineJoinMiter);
CGContextSetLineWidth(ctxt, 3);
// If the cell is highlighted (blue background) draw in white; otherwise gray
if (CONTROL_IS_HIGHLIGHTED) {
    CGContextSetRGBStrokeColor(ctxt, 1, 1, 1, 1);
} else {
    CGContextSetRGBStrokeColor(ctxt, 0.5, 0.5, 0.5, 1);
}
CGContextStrokePath(ctxt);

If you make a custom UIView subclass, do the above in the drawRect: method, and use that as your accessory view, you'll be able to make the color anything you want.如果您创建自定义 UIView 子类,请在 drawRect: 方法中执行上述操作,并将其用作辅助视图,您将能够制作任何您想要的颜色。

An accessory view (custom or UIImageView won't be a major performance problem as long as you are properly recycling UITableViewCell instances.附件视图(自定义或 UIImageView 不会成为主要的性能问题,只要您正确回收 UITableViewCell 实例。

Here is an implementation that works in iOS 8+.这是一个适用于 iOS 8+ 的实现。 It does exactly what's asked for:它完全符合要求:
change the color of the original Apple disclosure indicator to a custom color.将原始 Apple 披露指示器的颜色更改为自定义颜色。
Use it like this:像这样使用它:

#import "UITableViewCell+DisclosureIndicatorColor.h"
// cell is a UITableViewCell
cell.disclosureIndicatorColor = [UIColor redColor]; // custom color
[cell updateDisclosureIndicatorColorToTintColor]; // or use global tint color

UITableViewCell+DisclosureIndicatorColor.h UITableViewCell+DisclosureIndicatorColor.h

@interface UITableViewCell (DisclosureIndicatorColor)
@property (nonatomic, strong) UIColor *disclosureIndicatorColor;
- (void)updateDisclosureIndicatorColorToTintColor;
@end

UITableViewCell+DisclosureIndicatorColor.m UITableViewCell+DisclosureIndicatorColor.m

@implementation UITableViewCell (DisclosureIndicatorColor)

- (void)updateDisclosureIndicatorColorToTintColor {
    [self setDisclosureIndicatorColor:self.window.tintColor];
}

- (void)setDisclosureIndicatorColor:(UIColor *)color {
    NSAssert(self.accessoryType == UITableViewCellAccessoryDisclosureIndicator,
        @"accessory type needs to be UITableViewCellAccessoryDisclosureIndicator");

    UIButton *arrowButton = [self arrowButton];
    UIImage *image = [arrowButton backgroundImageForState:UIControlStateNormal];
    image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
    arrowButton.tintColor = color;
    [arrowButton setBackgroundImage:image forState:UIControlStateNormal];
}

- (UIColor *)disclosureIndicatorColor {
    NSAssert(self.accessoryType == UITableViewCellAccessoryDisclosureIndicator, 
        @"accessory type needs to be UITableViewCellAccessoryDisclosureIndicator");

    UIButton *arrowButton = [self arrowButton];
    return arrowButton.tintColor;
}

- (UIButton *)arrowButton {
    for (UIView *view in self.subviews)
        if ([view isKindOfClass:[UIButton class]])
            return (UIButton *)view;
    return nil;
}

@end

In swift 3, I have adapted the solution from @galambalazs as a class extention as follows:在 swift 3 中,我将@galambalazs 的解决方案改编为类扩展,如下所示:

import UIKit

extension UITableViewCell {

    func setDisclosure(toColour: UIColor) -> () {
        for view in self.subviews {
            if let disclosure = view as? UIButton {
                if let image = disclosure.backgroundImage(for: .normal) {
                    let colouredImage = image.withRenderingMode(.alwaysTemplate);
                    disclosure.setImage(colouredImage, for: .normal)
                    disclosure.tintColor = toColour
                }
            }
        }
    }
}

Hope this helps some.希望这对一些人有所帮助。

Use an UIImageView.使用 UIImageView。 This will also allow you to change the image when the cell is selected:这也将允许您在选择单元格时更改图像:

UIImageView* arrowView = [[UIImageView alloc] initWithImage:normalImage];
arrowView.highlightedImage = selectedImage;
cell.accessoryView = arrowView;
[arrowView release];

But I guess adding a new UIView object to every row might turn out to be a heavy obj allocation and decreasing the frame rate.但我想向每一行添加一个新的 UIView 对象可能会导致大量的 obj 分配并降低帧速率。 As compared to just a UIImage object in my contentView.与我的 contentView 中的 UIImage 对象相比。 I believe UIImage is lighter than UIView.我相信 UIImage 比 UIView 轻。

Drawing an image directly will almost certainly have better performance than adding a subview.直接绘制图像几乎肯定会比添加子视图具有更好的性能。 You have to determine if that extra performance is necessary.您必须确定是否需要额外的性能。 I've used a few accessory views for custom disclosure indicators on basic cells and performance was fine.我在基本单元格上使用了一些辅助视图来自定义披露指标,性能很好。 However, if you're already doing custom drawing for the content rect, it might not be that difficult to do the accessory view also.但是,如果您已经在为内容矩形进行自定义绘图,那么制作附件视图也可能并不困难。

benzado's solution works fine, but it showed a black background. benzado 的解决方案工作正常,但它显示黑色背景。 In the UIView class that you setup (the one who's drawRect function you put in his code) needs to have the following initWithFrame implementation in order for the disclosure drawing to have a transparent background:在您设置的 UIView 类中(您在代码中放置的 drawRect 函数)需要具有以下 initWithFrame 实现,以便公开绘图具有透明背景:

- (id)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];
    if (self) {
        [self setBackgroundColor:[UIColor clearColor]];
        // Initialization code.
    }
    return self;
}

Naturally, you can set this to whatever color you want...当然,您可以将其设置为您想要的任何颜色......

While galambalazs' answer works, it should be noted that it is somewhat of a hack as it indirectly accesses and updates Apple's private implementation of the disclosure indicator.虽然 galambalazs 的回答有效,但应该注意的是,它在某种程度上是一种黑客行为,因为它间接访问和更新了 Apple 对披露指标的私有实现。 At best, it could stop working in future iOS releases, and at worst lead to App Store rejection.充其量,它可能会在未来的 iOS 版本中停止工作,最坏的情况是导致 App Store 被拒绝。 Setting the accessoryView is still the approved method until Apple exposes a property for directly setting the color.在 Apple 公开直接设置颜色的属性之前,设置accessoryView仍然是批准的方法。

Regardless, here is the Swift implementation of his answer for those who may want it:无论如何,这是他为可能需要的人提供的答案的 Swift 实现:

Note: cell.disclosureIndicatorColor has to be set after cell.accessoryType = .DisclosureIndicator is set so that the disclosureIndicator button is available in the cell's subviews:注意: cell.disclosureIndicatorColor必须cell.accessoryType = .DisclosureIndicator设置设置,以便在单元格的子视图中可以使用disclosureIndicator按钮:

extension UITableViewCell {

    public var disclosureIndicatorColor: UIColor? {
        get {
            return arrowButton?.tintColor
        }
        set {
            var image = arrowButton?.backgroundImageForState(.Normal)
            image = image?.imageWithRenderingMode(.AlwaysTemplate)
            arrowButton?.tintColor = newValue
            arrowButton?.setBackgroundImage(image, forState: .Normal)
        }
    }

    public func updateDisclosureIndicatorColorToTintColor() {
        self.disclosureIndicatorColor = self.window?.tintColor
    }

    private var arrowButton: UIButton? {
        var buttonView: UIButton?
        self.subviews.forEach { (view) in
            if view is UIButton {
                buttonView = view as? UIButton
                return
            }
        }
        return buttonView
    }
}

In iOS 13+ the disclosure indicator is set via a non-templeted UIImage, which is determined by the user's dark mode preference.在 iOS 13+ 中,披露指示符是通过非模板 UIImage 设置的,这由用户的暗模式偏好决定。 Since the image is non-templated, it will not respect the cell's tintColor property.由于图像是非模板化的,它不会考虑单元格的 tintColor 属性。 In other words, the dark mode preference has top priority.换句话说,暗模式偏好具有最高优先级。 If you don't wan't to use the disclosure indicator derived by iOS, you will have to use a custom image.如果您不想使用由 iOS 派生的披露指示符,则必须使用自定义图像。

As a contribution to the solution of @benzado I swiftified his code as followed:作为对@benzado 解决方案的贡献,我将他的代码快速化如下:

override func drawRect(rect: CGRect) {

  super.drawRect(rect)

  let context = UIGraphicsGetCurrentContext();

  let right_margin : CGFloat = 15.0
  let length : CGFloat = 4.5;

  // (x,y) is the tip of the arrow
  let x = CGRectGetMaxX(self.bounds) - right_margin;
  let y = CGRectGetMidY(self.bounds);

  CGContextMoveToPoint(context, x - length, y - length);
  CGContextAddLineToPoint(context, x, y);
  CGContextAddLineToPoint(context, x - length, y + length);
  CGContextSetLineCap(context, .Round);
  CGContextSetLineJoin(context, .Miter);
  CGContextSetLineWidth(context, 2.5);

  if (self.highlighted)
  {
    CGContextSetStrokeColorWithColor(context, UIColor.appColorSelected().CGColor);
  }
  else
  {
    CGContextSetStrokeColorWithColor(context, UIColor.appColor().CGColor);
  }

  CGContextStrokePath(context);
}

With a change of the app color a call to setNeedsDisplay() on the UITableCellView will update the color.随着应用程序颜色的变化,调用 UITableCellView 上的 setNeedsDisplay() 将更新颜色。 I like to avoid the use of UIImage objects in cell views.我喜欢避免在单元格视图中使用 UIImage 对象。

A swift 3 version of the CocoaNetics solution CocoaNetics 解决方案的swift 3 版本

public class DisclosureIndicator: UIControl {

    public static func create(color: UIColor?, highlightedColor: UIColor?) -> DisclosureIndicator{
        let indicator = DisclosureIndicator(frame: CGRect(x: 0, y: 0, width: 11, height: 15))
        if let color = color { indicator.color = color }
        if let color = highlightedColor { indicator.highlightedColor = color }
        return indicator
    }

    public var color: UIColor = .black
    public var highlightedColor: UIColor = .white

    override public init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .clear
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        backgroundColor = .clear
    }

    override public func draw(_ rect: CGRect) {
        super.draw(rect)

        let context = UIGraphicsGetCurrentContext()!;

        // (x,y) is the tip of the arrow
        let x = self.bounds.maxX - 3.0;
        let y = self.bounds.midY;

        let length : CGFloat = 4.5;
        context.move(to: CGPoint(x: x - length, y: y - length))
        context.addLine(to: CGPoint(x: x, y: y))
        context.addLine(to: CGPoint(x: x - length, y: y + length))
        context.setLineCap(.round)
        context.setLineJoin(.miter)
        context.setLineWidth(3)

        context.setStrokeColor((isHighlighted ? highlightedColor : color).cgColor)

        context.strokePath()
    }

    override public var isHighlighted: Bool {
        get {
            return super.isHighlighted
        }
        set {
            super.isHighlighted = newValue
            setNeedsDisplay()
        }
    }
}

Change the tint colour of table view cell.更改表格视图单元格的色调颜色。 Check the screenshot to do the same via Storyboard.检查屏幕截图以通过 Storyboard 执行相同操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM