子类化的 UISegmentedControl titleTextAttributes 并不总是适用

Subclassed UISegmentedControl titleTextAttributes not always applied

场景

我的子类 UISegmentedControl 中有一个特殊错误。我所做的只是将 UISegmentedControl 子类化以删除 selection/deselection 的默认指示器,并使用自定义 CALayer 作为相同的指示器。同时,我修改了titleTextAttributes来补充UI。

问题

当视图首次出现并且默认 selected 索引为 0 时,它看起来像这样:

当我尝试将 selectedSegmentIndex 更改为 1 时,它变为:

它应该看起来像这样(更改 selectedSegmentIndex 几次后更正):

这种 titleTextAttributes 不变的行为是随机的,但是 100% 发生这种情况的 2 种情况是:

我没有在 UISegmentedControl 官方文档中找到子类化注释。

示例项目HERE

注意: 如果您只使用 2 个片段,您将永远不会在 selection 上的索引 1 处看到片段的标题。

确实是一种奇怪的行为。

您可以解决它,在更改指示层框架后添加对 [self setNeedsLayout] 的显式调用。

//
//  RBLSegmentedControl.m
//  Rabbler
//
//  Created by Utkarsh Singh on 05/11/15.
//  Copyright © 2015 Neeraj  SIngh. All rights reserved.
//

#import "NPSegmentedControl.h"

@interface NPSegmentedControl ()

@property (nonatomic, retain) CALayer* selectedIndicationLayer;

@end

@implementation NPSegmentedControl

- (instancetype)initWithItems:(NSArray *)items {

    if (self = [super initWithItems:items]) {

        self.selectedIndicationLayer.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width / items.count, self.frame.size.height);
        [self addTarget:self action:@selector(segmentedControlDidChange) forControlEvents:UIControlEventValueChanged];

        NSDictionary* selectedAttributes = @{NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Light" size:15],
                                             NSForegroundColorAttributeName:[UIColor whiteColor]};
        NSDictionary* deselectedAttributes = @{NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Light" size:15],
                                               NSForegroundColorAttributeName:[UIColor redColor]};
        NSDictionary* highlightedAttributes = @{NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Light" size:15],
                                                NSForegroundColorAttributeName:[[UIColor redColor] colorWithAlphaComponent:0.6f]};

        [self setTitleTextAttributes:deselectedAttributes forState:UIControlStateNormal];
        [self setTitleTextAttributes:selectedAttributes forState:UIControlStateSelected];
        [self setTitleTextAttributes:highlightedAttributes forState:UIControlStateHighlighted];
    }

    return self;
}

- (void) segmentedControlDidChange {

    [self animateSegmentSelectionChange];

    if (self.delegate && [self.delegate respondsToSelector:@selector(segmentedControlDidChangeSelectedSegment:)]) {

        [self.delegate segmentedControlDidChangeSelectedSegment:self.selectedSegmentIndex];
    }
}

- (void) animateSegmentSelectionChange {

    [UIView animateWithDuration:0.25
                          delay:0
                        options:(UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionBeginFromCurrentState)
                     animations:^{

                         CGRect frame = self.selectedIndicationLayer.frame;
                         frame.origin.x = self.selectedSegmentIndex * [UIScreen mainScreen].bounds.size.width / self.numberOfSegments;
                         self.selectedIndicationLayer.frame = frame;

                         [self setNeedsLayout]; // <--

                     } completion:nil];
}

- (CALayer *) selectedIndicationLayer {

    if (!_selectedIndicationLayer) {

        _selectedIndicationLayer = [CALayer layer];
        _selectedIndicationLayer.backgroundColor = [[UIColor redColor] CGColor];
        _selectedIndicationLayer.cornerRadius = self.frame.size.height /2;
        _selectedIndicationLayer.borderColor = [UIColor whiteColor].CGColor;
        _selectedIndicationLayer.borderWidth = 1.5f;
        [self.layer insertSublayer:_selectedIndicationLayer atIndex:0];
    }

    return _selectedIndicationLayer;
}

- (void) setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex {

    [super setSelectedSegmentIndex:selectedSegmentIndex];

    [self animateSegmentSelectionChange];
}

- (void) setIndicatorColor:(UIColor *)indicatorColor {

    self.selectedIndicationLayer.backgroundColor = indicatorColor.CGColor;
}

@end

我把你的 selectedIndicationLayer 改成了这个,它解决了问题

- (CALayer *) selectedIndicationLayer {

    if (!_selectedIndicationLayer) {

        _selectedIndicationLayer = [CALayer layer];
        _selectedIndicationLayer.backgroundColor = [[UIColor redColor] CGColor];
        _selectedIndicationLayer.cornerRadius = self.frame.size.height /2;
        _selectedIndicationLayer.borderColor = [UIColor whiteColor].CGColor;
        _selectedIndicationLayer.borderWidth = 1.5f;
    }

    if ([self.layer.sublayers containsObject:_selectedIndicationLayer]) {
        [_selectedIndicationLayer removeFromSuperlayer];
    }
    [self.layer insertSublayer:_selectedIndicationLayer atIndex:0];

    return _selectedIndicationLayer;
}

只需编辑您的以下方法。

- (CALayer *) selectedIndicationLayer {

if (!_selectedIndicationLayer) {

    _selectedIndicationLayer = [CALayer layer];
    _selectedIndicationLayer.backgroundColor = [[UIColor redColor] CGColor];
    _selectedIndicationLayer.cornerRadius = self.frame.size.height /2;
    _selectedIndicationLayer.borderColor = [UIColor whiteColor].CGColor;
    _selectedIndicationLayer.borderWidth = 1.5f;
}

[self.layer insertSublayer:_selectedIndicationLayer atIndex:0];

return _selectedIndicationLayer;
}