如何在iOS的表格视图单元格中改变披露指示器附件视图的颜色/视图?

22
我需要改变UITableViewCelldisclosureIndicatorView附件的颜色。 我认为有两种方法可以完成这个任务,但我无法确定哪一种是最佳的。所以这是我认为我能做的。 UITableViewCell有一个属性 - accessoryView。因此,我可以使用setAccessoryView:(UIView *)view方法,并将视图作为包含我想要的图像的UIImageView传递。
我编写了一个实用程序类,该类创建单元格的内容视图(例如背景颜色、添加其他内容等),然后在UITableViewDelegate中将此内容视图添加到单元格中。另一种选择是绘制一个UIImage,覆盖CustomContentView实用程序类的drawRect方法。
执行选项1 - 我可以按照apple的方式完成任务。只需给它们视图,他们就会自己处理剩下的事情。但是我想在每一行中添加一个新的UIView对象,可能会成为一个繁重的对象分配,并降低帧速率。与我的contentView中仅使用UIImage相比,我相信UIImage更轻量级。
请指点一下,帮我做出决定。

当然,这让人希望可以在所有行上共享UIImageView。 - Epsilon Prime
查看一个库。非常容易地更改所有 accessoryType 的颜色。MSCellAccessoryView - bitmapdata.com
这是一个老问题,但至今没有令人满意的答案。这里是您实际所要求的内容:https://dev59.com/0nI-5IYBdhLWcg3wcn3m#35427599 - gblazex
12个回答

29

24

如果您有兴趣绘制指示器,而不是使用图像文件,这里是我编写的代码:

// (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);
如果你创建了一个自定义的UIView子类,在drawRect:方法中执行上述操作,并将其用作你的附加视图,那么你就可以将颜色设置为任何你想要的颜色。
只要你正确地回收UITableViewCell实例,使用附加视图(自定义或UIImageView)不会成为主要的性能问题。

你能告诉我 CONTROL_IS_HIGHLIGHTED 是如何设置的吗?我的意思是,细胞怎么知道它被突出显示了? - cannyboy
如果你正在/在表视图单元格中绘制,我相信UITableViewCell有一个“highlighted”属性。 - benzado
我一直在尝试使用这段代码,但由于某种原因,我失去了两条线相交的尖锐点,并得到了一个暗淡的角度,有什么想法吗? - Anthony Main
CGContextSetLineJoin() 控制了该点发生的情况。也许您忘记了它?或者您只调用了一次,然后绘制了其他改变当前线连接设置的内容? - benzado
@VanDuTran 是的,这只是绘图代码,这些API都没有改变。 - benzado

12

这是一种在iOS 8+中有效的实现方式。 它完全符合要求:
将原始的Apple披露指示器的颜色更改为自定义颜色。
像这样使用它:

#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

:UITableView的单元格类别,用于设置右侧箭头的颜色。
@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

红色变成蓝色 - Vyachaslav Gerchicov

10
在 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
                }
            }
        }
    }
}

希望这能有所帮助。

对我来说很有效,我在委托函数willDisplayCell:forRowAtIndexPath中调用此方法。 - Ric Santos

5
使用UIImageView。这也可以让您在选择单元格时更改图像:
UIImageView* arrowView = [[UIImageView alloc] initWithImage:normalImage];
arrowView.highlightedImage = selectedImage;
cell.accessoryView = arrowView;
[arrowView release];

4

我猜每一行都添加一个新的UIView对象可能会导致大量的对象分配,降低帧率。与我的contentView中仅包含UIImage对象相比,我相信UIImage更轻。

直接绘制图像几乎肯定比添加子视图具有更好的性能。您必须确定是否需要这种额外的性能。我曾经在基本单元格上使用过一些附加视图来自定义披露指示器,性能良好。然而,如果您已经为内容矩形执行自定义绘制,则对于附加视图也可能并不那么困难。


2

benzado的解决方案很好,但它显示了黑色背景。您设置的UIView类(其中包含您放置其代码的drawRect函数)需要具有以下initWithFrame实现,以便披露绘制具有透明背景:

- (id)initWithFrame:(CGRect)frame {

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

当然,您可以将其设置为任何您想要的颜色...


1
虽然galambalazs的答案可以运行,但应该注意它是一种间接访问和更新苹果披露指示器私有实现的黑客方法。最好的情况下,它可能在未来的iOS版本中停止工作,并且在最坏的情况下会导致应用商店拒绝。在苹果公开直接设置颜色的属性之前,设置accessoryView仍然是批准的方法。

无论如何,这里是Swift实现他的答案,供那些需要的人使用:

注意:cell.disclosureIndicatorColor必须在设置cell.accessoryType = .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
    }
}

访问视图层次结构从未被苹果禁止。通常是私有API(不在文档中的方法、属性或类)会让你陷入麻烦。 - gblazex
这不仅仅是访问视图层次结构,而是通过层次结构间接地检索私有的UIButton属性并对其进行修改。 - ymerej

1
在iOS 13+中,披露指示器是通过非模板UIImage设置的,该UIImage由用户的深色模式偏好确定。由于图像是非模板化的,因此它不会尊重单元格的tintColor属性。换句话说,深色模式偏好具有最高优先级。如果您不想使用iOS衍生的披露指示器,则必须使用自定义图像。

0

作为对 @benzado 解决方案的贡献,我将他的代码进行了Swift化,如下所示:

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);
}

通过更改应用程序的颜色,对UITableCellView调用setNeedsDisplay()将更新颜色。我喜欢避免在单元视图中使用UIImage对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接