歡迎光臨
每天分享高質量文章

iOS開發之控制元件封裝(又名擰螺絲):排序按鈕

作者:無夜之星辰

連結:https://www.jianshu.com/p/96e3f4482bb3


iu

前言

排序按鈕是實際開發中比較常見的一種控制元件,最近我也遇到了,鑒於只會擰螺絲的我有一段時間沒寫關於擰螺絲的文章了,為了找下存在感,今天簡單分享下。

雖然功能簡單,但是保證你看了不虧,尤其是對UI這塊比較薄弱的同學來說。

手把手教你擰螺絲


OK,先看圖:

4個排序按鈕.gif

簡單描述一下:


按鈕一共有三種狀態:非選中、選中升序、選中降序。


按鈕的三種狀態


點選按鈕時有兩種情況:


  • 按鈕原本處於非選中狀態,點選,切換到選中狀態,其狀態變為升序。

  • 按鈕原本就處於選中狀態,再點選一下,則切換其排序狀態(升變降、降變升)。


不同狀態對應不同的icon,如果沒有UI,可以去iconfont 找圖示,輸入關鍵詞如“上下箭頭”就可以找到你需要的icon。

基本思路

繼承UIButton,直接在button上放view,設定約束,根據按鈕的狀態設定對應的圖片。


PS:自定義按鈕最靈活的做法就是直接在button上放view(在不需要糾結記憶體和view層級的情況下),簡單粗暴、隨心所欲。

完整程式碼

.h檔案:

#import 

@interface CQSortButton : UIButton

/** 按鈕文字 */
@property (nonatomiccopyNSString *title;
/** 是否是升序 */
@property (nonatomicassignreadonlygetter=isAscending) BOOL ascending;

@end

.m檔案:

#import "CQSortButton.h"

@interface CQSortButton ()

/** 文字label */
@property (nonatomicstrongUILabel *cq_titleLabel;
/** 箭頭imageView */
@property (nonatomicstrongUIImageView *cq_arrowImageView;

@end

@implementation CQSortButton

#pragma mark - 構造方法

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self setupUI];
    }
    return self;
}

#pragma mark - UI搭建

- (void)setupUI {
    self.layer.borderColor = [UIColor blackColor].CGColor;
    self.layer.borderWidth = 1;

    // 文字和圖片的父view
    UIView *contentView = [[UIView alloc] init];
    [self addSubview:contentView];
    contentView.userInteractionEnabled = NO;
    [contentView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.bottom.centerX.mas_equalTo(self);
        make.left.mas_greaterThanOrEqualTo(self).mas_offset(3);
        make.right.mas_lessThanOrEqualTo(self).mas_offset(-3);
    }];

    // 文字
    self.cq_titleLabel = [[UILabel alloc] init];
    [contentView addSubview:self.cq_titleLabel];
    self.cq_titleLabel.font = [UIFont boldSystemFontOfSize:13];
    self.cq_titleLabel.adjustsFontSizeToFitWidth = YES;
    [self.cq_titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.bottom.left.mas_offset(0);
    }];

    // 圖片
    self.cq_arrowImageView = [[UIImageView alloc] init];
    [contentView addSubview:self.cq_arrowImageView];
    self.cq_arrowImageView.image = [UIImage imageNamed:@"up_down"];
    [self.cq_arrowImageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.size.mas_equalTo(CGSizeMake(2020));
        make.centerY.mas_equalTo(contentView);
        make.left.mas_equalTo(self.cq_titleLabel.mas_right);
        make.right.mas_equalTo(contentView);
    }];
}

#pragma mark - 賦值選中狀態

- (void)setSelected:(BOOL)selected {
    //// 註意:
    //// selected 表示你要賦值的狀態
    //// super.selected 表示當前處於的狀態

    if (selected) { // 即將設定成選中狀態
        if (super.selected) { // 如果原本就處於選中狀態
            // 那麼就切換篩選狀態
            _ascending = !_ascending;
            if (_ascending) {
                // 升序
                self.cq_arrowImageView.image = [UIImage imageNamed:@"red_arrow_up"];
            } else {
                // 降序
                self.cq_arrowImageView.image = [UIImage imageNamed:@"red_arrow_down"];
            }
        } else { // 如果之前不是選中狀態
            // 那麼設定成選中的預設排序狀態:升序
            _ascending = YES;
            self.cq_arrowImageView.image = [UIImage imageNamed:@"red_arrow_up"];
        }
    } else { // 即將設定成非選中狀態
        // 設定成非選中狀態的圖片
        self.cq_arrowImageView.image = [UIImage imageNamed:@"up_down"];
    }

    // 最後再賦值
    [super setSelected:selected];
}

#pragma mark - 賦值文字

- (void)setTitle:(NSString *)title {
    _title = title;
    self.cq_titleLabel.text = title;
}

@end

使用:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    NSArray *titleArray = @[@"同比"@"銷售額"@"?"@"文字有點多啊"];

    NSMutableArray *buttonArray = [NSMutableArray array];
    for (int i = 0; i 4; i++) {
        CQSortButton *button = [[CQSortButton alloc] init];
        [self.view addSubview:button];
        button.title = titleArray[i];
        button.tag = CQSortButtonBeginTag + i;
        [button addTarget:self action:@selector(sortButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
        [buttonArray addObject:button];
    }

    // 按鈕等寬依次排列
    [buttonArray mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:0 leadSpacing:0 tailSpacing:0];
    [buttonArray mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(100);
        make.height.mas_equalTo(40);
    }];
}

- (void)sortButtonClicked:(CQSortButton *)sender {
    for (int i = 0; i 4; i++) {
        CQSortButton *button = [self.view viewWithTag:(CQSortButtonBeginTag + i)];
        button.selected = (button.tag == sender.tag);
    }
    NSLog(@"第%ld個按鈕點選,狀態:%@", (long)(sender.tag-CQSortButtonBeginTag), sender.isAscending ? @"升序" : @"降序");
}

知識點及細節

1、如何讓兩個view整體居中並且不超出父view?


建立父view是關鍵。


先建立一個父view,這個父view居中於button,左右不設定固定約束,再將兩個view放在父view上,左邊的view與父view左對齊,右邊的view與父view右對齊,左邊的view與右邊的view水平方向約束確定,撐開父view:


// 文字和圖片的父view
UIView *contentView = [[UIView alloc] init];
[self addSubview:contentView];

[contentView addSubview:self.cq_titleLabel];
[contentView addSubview:self.cq_arrowImageView];

[self.cq_titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.bottom.left.mas_offset(0);
}];
[self.cq_arrowImageView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(2020));
    make.centerY.mas_equalTo(contentView);
    make.left.mas_equalTo(self.cq_titleLabel.mas_right);
    make.right.mas_equalTo(contentView);
}];

不超出父view(此處指button)用mas_greaterThanOrEqualTo和mas_lessThanOrEqualTo即可:


[contentView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.bottom.centerX.mas_equalTo(self);
    make.left.mas_greaterThanOrEqualTo(self).mas_offset(3);
    make.right.mas_lessThanOrEqualTo(self).mas_offset(-3);
}];

2、readonly的使用


/** 是否是升序 */
@property (nonatomicassignreadonlygetter=isAscending) BOOL ascending;

為什麼這裡要用readonly?


因為這個屬性的改變只能是透過內部(自身的.m)改變,而不能透過外部改變,或者說這個屬性只是用來反映按鈕的一個狀態,就像UIScrollView的decelerating屬性一樣,只是反映scrollView正在減速,不能透過呼叫scrollView.decelerating = YES讓它主動減速。

// returns YES if user isn't dragging (touch up) but scroll view is still moving
@property(nonatomic,readonly,getter=isDecelerating) BOOL decelerating;

總結一下就是readonly適用於只用來反映物件的狀態、特徵或特性的屬性。


你可以找幾個蘋果官方檔案裡的readonly屬性好好感受一下。


3、如何使用masonry等寬等間距排列控制元件?


用masory提供的mas_distributeViewsAlongAxis方法:


// 按鈕等寬依次排列[buttonArray mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:0 leadSpacing:0 tailSpacing:0];
[buttonArray mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.mas_equalTo(100);
    make.height.mas_equalTo(40);
}];


關於這個方法的更多使用可以參考這篇文章:


iOS Masonry 等間隔或等寬高排列多個控制元件
https://www.jianshu.com/p/25977be007f7,很實用的技能,建議熟練掌握。

需要註意的是呼叫mas_distributeViewsAlongAxis這個方法的陣列其元素個數必須bigger than one,否則沒有效果,masonry原始碼擷取:


- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing {    if (self.count 2) {        NSAssert(self.count>1,@"views to distribute need to bigger than one");        return;
    }
    ......
}


所以實際開發中如果你是獲取後臺的陣列來展示的話,務必先判斷陣列的count。


demo

https://github.com/CaiWanFeng/iOS_Storage


demo位置:


反思

工作兩年多,只會擰螺絲,現在的我,慌的一批。。。


所以:


編號310,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

Web開發

更多推薦18個技術類微信公眾號

涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

贊(0)

分享創造快樂