drawRect 没有在 UI 元素上绘制

drawRect is not drawing over a UI element

TL/DR 版本:我试图在 UISlider 上方画一条线,而不是在它下方。

我正在覆盖 UISliderdrawRect 方法以便在其上画线。由于某些原因,滑块进度条与我画的线重叠,看起来像这样:

你几乎看不到标记,因此我需要以不同的方式绘制。这是我为绘制所述线条而编写的代码:

- (void)drawRect:(CGRect)rect {

    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
    CGContextSetLineWidth(context, 20);

    for (NSNumber *x in self.lines) {
        CGContextMoveToPoint(context, x.floatValue + 175, 45);
        CGContextAddLineToPoint(context, x.floatValue + 175, rect.size.height - 50);
        CGContextStrokePath(context);
    }
}

我认为这会很好,但不行。所以,我在这个网站上做了一些研究,我可以得出的结论是我需要覆盖 layoutSubViews.

完美,所以我在方法的开头添加了 [super layoutSubviews];; 像这样:

- (void)drawRect:(CGRect)rect {
    [super layoutSubviews];
    ... // all other code
}

没有帮助。从代码中可以看出,我将线条颜色设置为 [UIColor whiteColor],但它看起来很难看,即使我覆盖了 layoutSubViews。我希望它看起来像这样:

注意-对于任何试图这样做或类似的人,如下 rmaddy 所述:

"Overriding the drawRect: method of a control provided by Apple is a really bad idea. It's fragile and likely to break in some iOS update."

我这样做是为了快速修补,如果您不需要,最好不要这样做。

UI 元素是您的视图的子视图。 drawRect 填充视图本身的内容,但其子视图绘制在视图之上。如果要在 over 视图及其子视图上绘制,则需要将自己的子视图添加到滑块,或者将整个滑块嵌入到单独的父视图中,该父视图有自己的子视图滑块顶部的子视图。

您可以在调试器中查看滑块的视图层次结构,使用 recursiveDescription 方法:

(lldb) po [slider recursiveDescription]
<UISlider: 0x7fd7e3e43b60; frame = (0 0; 100 100); opaque = NO; layer = <CALayer: 0x7fd7e3e42460>; value: 0.000000>
   | <UIView: 0x7fd7e7077e20; frame = (16 49; 82 2); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fd7e7077f30>>
   |    | <UIImageView: 0x7fd7e70765e0; frame = (-14 0; 96 2); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fd7e7076310>>
   | <UIImageView: 0x7fd7e7077b40; frame = (2 49; 14 2); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fd7e70767d0>>
   | <UIImageView: 0x7fd7e3d1d880; frame = (0 34; 31 31); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fd7e3d1d9c0>>
   |    | <UIImageView: 0x7fd7e3d1d400; frame = (-13 -6.5; 57 43.5); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fd7e3d1ba50>>

您可以在调试器中隐藏和取消隐藏视图,以查看哪些视图负责滑块中的哪些内容。两个点高的视图之一是滑块拇指后面的蓝色条。另一个将是滑块拇指前面的栏。 31x31 图像视图是拇指。您使用 drawRect: 绘制的任何内容都将进入顶级滑块视图 本身 (在示例中,地址为 0x7fd7e3e43b60 的视图)。然后所有的子视图都在上面绘制。

刚好你画的画在UISlider里面的最小和最大轨道图像视图后面。你可以这样做,这样你就可以创建一个单独的视图,假设是 LineView,然后将它添加到你的 UISlider,将这个子视图带到最顶部的 zIndex 并对该视图进行线条绘制。查看我已经完成的实现,以进一步了解如何实现。

@interface LineView: UIView

@property (nonatomic, strong) NSArray *lines;

@end

@implementation LineView

- (void)setLines:(NSArray *)lines
 {
    _lines = lines;
    [self setNeedsDisplay];
 }

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        self.opaque = YES;
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}

// disable the touch as UISlider has thumb view as a separate view which actually needs to handle touch events
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    return NO;
}

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    const CGFloat lineWidth = 5;
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
    CGContextSetLineWidth(context, lineWidth);

    for (NSNumber *x in self.lines) {
        CGContextMoveToPoint(context, x.floatValue , self.bounds.size.height);
        CGContextAddLineToPoint(context, x.floatValue + 20, self.bounds.size.height);

        CGContextStrokePath(context);
    }
}


@end


@interface MySlider : UISlider

  @property (nonatomic, strong) UIView *lineView;
  @property (nonatomic, strong) NSArray *lines;

@end

@implementation MySlider

- (void)setLines:(NSArray *)lines
{
  self.lineView.lines = lines;
}

- (NSArray *)lines
{
  return self.lineView.lines;
}

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        self.lineView = [[LineView alloc] initWithFrame:self.bounds];
        [self addSubview:self.lineView];
    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    [self bringSubviewToFront:self.lineView];
}

@end

并从外部接口设置。

mySlider.lines = @[@0, @50, @100, @160, @180, @250];

这是结果,