更加快速地設(shè)置Frame

由于現(xiàn)在手頭上的項(xiàng)目是基于frame開發(fā)的,沒(méi)有xib或者storyboard,沒(méi)有使用自動(dòng)布局,所以排布界面時(shí)總是顯得很繁瑣。

令人蛋疼的frame布局

老代碼對(duì)界面的坐標(biāo)尺寸設(shè)置都是通過(guò)下面的方式:

...
UIView * mainView = [[UIView alloc] initWithFrame:CGRectMake(0, self.height, self.width, MAIN_HEIGHT)];
[mainView setBackgroundColor:[UIColor whiteColor]];
[self addSubview:mainView];
self.mainView = mainView;

UIView * opertionMenu = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.width, 45)];
[opertionMenu setBackgroundColor:[UIColor whiteColor]];
[mainView addSubview:opertionMenu];

UIView * line = [[UIView alloc] initWithFrame:CGRectMake(0, 44, self.width, 1)];
[line setBackgroundColor:[UIColor colorWithHex:0xe9e9e9]];
[opertionMenu addSubview:line];
...

這些坐標(biāo)設(shè)置工作都是在初始化,也就是init系列方法中完成的。這樣做的弊端很明顯,復(fù)用性很差,如果還是按照這種方式的話,每擴(kuò)展一種界面類型,就要新增一個(gè)init方法。久而久之,冗余代碼會(huì)越來(lái)越多,新增特性想重用這塊控件的話,需要做比較多的無(wú)用功。

由于上面代碼很多針對(duì)坐標(biāo)尺寸的設(shè)置都是在init系列方法中完成的,所以很少有單獨(dú)修改frame中的某個(gè)成員的操作,不過(guò)舊代碼還是提供了快速設(shè)置某個(gè)成員的方法:

@interface UIView (Size)
...
- (CGFloat)width;
- (CGFloat)height;
- (CGFloat)minX;
- (CGFloat)midX;
- (CGFloat)maxX;
- (CGFloat)minY;
- (CGFloat)midY;
- (CGFloat)maxY;
...
@end

很明顯地看出,這種分類命名是有問(wèn)題的(這也是直接導(dǎo)致我后續(xù)沒(méi)有引入masonry的誘因之一,舊代碼中過(guò)多的使用了以上分類,和簡(jiǎn)易的masonry用法即沒(méi)有mas前綴的方法產(chǎn)生的沖突過(guò)多)。

于是我新增了一些快速設(shè)置的坐標(biāo)的分類方法:

@interface UIView (Size)
...
@property (assign, nonatomic) CGFloat bq_y;
@property (assign, nonatomic) CGFloat bq_x;
@property (assign, nonatomic) CGFloat bq_width;
@property (assign, nonatomic) CGFloat bq_height;
@property (assign, nonatomic) CGFloat bq_centerX;
@property (assign, nonatomic) CGFloat bq_centerY;
@property (assign, nonatomic) CGPoint bq_origin;
@property (assign, nonatomic) CGSize bq_size;
@property (assign, nonatomic) CGFloat bq_maxX;
@property (assign, nonatomic) CGFloat bq_maxY;
@property (assign, nonatomic, readonly) CGPoint bq_subCenter;
@property (assign, nonatomic, readonly) CGFloat bq_subCenterX;
@property (assign, nonatomic, readonly) CGFloat bq_subCenterY;
- (instancetype)bq_sizeToFit;
...
@end

于是代碼中就多了另外一種設(shè)置frame的書寫格式:

- (void)adjustSubviewFrame {
    ...
    if (!_timeLabel.hidden) {
        [_timeLabel sizeToFit];
        CGFloat timeLabelLRMargin = 5;
        CGFloat timeLabelTopMaigin = BQMessageCommonMargin;
        _timeLabel.bq_size = CGSizeMake(_timeLabel.bq_width + 2 * timeLabelLRMargin, BQMessageTimeHeight);
        _timeLabel.center = CGPointMake(self.contentView.bq_centerX, timeLabelTopMaigin + _timeLabel.bq_height * 0.5);
    } else {
        _timeLabel.frame = CGRectZero;
    }
    CGFloat headImageEdgeMargin = BQMessageCommonMargin;
    CGFloat headImageViewX = _isSelf ? self.contentView.bq_width - _headIconImageView.bq_width - headImageEdgeMargin : headImageEdgeMargin;
    CGFloat headImageViewY = CGRectGetMaxY(_timeLabel.frame) + headImageEdgeMargin;
    _headIconImageView.bq_origin = CGPointMake(headImageViewX, headImageViewY);
    if (!_identityButton.hidden) {
        CGFloat identifyButtonTopMargin = BQMessageHeaderBottomMargin;
        CGFloat identifyButtonY = CGRectGetMaxY(_headIconImageView.frame) + identifyButtonTopMargin;
        _identityButton.bq_y = identifyButtonY;
        _identityButton.bq_centerX = _headIconImageView.bq_centerX;
    }
    CGFloat nameLabelToHeadImageTopMargin = 0;
    if (!_nameLabel.hidden) {
        [_nameLabel sizeToFit];
        CGFloat nameLabelX = CGRectGetMaxX(_headIconImageView.frame) + headImageEdgeMargin;
        CGFloat nameLabelY = _headIconImageView.bq_y + nameLabelToHeadImageTopMargin;
        _nameLabel.bq_origin = CGPointMake(nameLabelX, nameLabelY);
    }
    ...
}

把設(shè)置可變坐標(biāo)的代碼獨(dú)立出來(lái)放進(jìn)一個(gè)方法中增強(qiáng)的控件的復(fù)用性,但是也無(wú)形中增加了布局代碼代碼量,而且估計(jì)第二個(gè)人來(lái)看這個(gè)計(jì)算過(guò)程也會(huì)很頭大。

能不能采用鏈?zhǔn)降姆绞胶?jiǎn)化對(duì)界面進(jìn)行frame布局的代碼?于是我借鑒了masonry的實(shí)現(xiàn)方式,弄了一套快速設(shè)置frame的代碼。最終使用的效果:

UIView *v1 = [UIView new];
v1.tpc_quickAttribute
.referToView(self.view)
.addToView(self.view)
.alignOrigin(CGPointMake(10, 20))
.alignSize(CGSizeMake(-200, -400))
.backgroundColor([UIColor redColor]);

UIView *v2 = [UIView new];
v2.tpc_quickAttribute
.referToView(v1)
.addToView(v1)
.size(CGSizeMake(20, 20))
.alignCenter(pzero)
.backgroundColor([UIColor orangeColor]);

UIView *v3 = [UIView new];
v3.tpc_quickAttribute
.addToView(self.view)
.referToView(v1)
.alignSize(CGSizeMake(20, 20))
.referToView(self.view)
.alignRightToRight(20)
.alignBottomToBottom(20)
.backgroundColor([[UIColor grayColor] colorWithAlphaComponent:0.4]);

頓時(shí)感覺(jué)整個(gè)世界又變清新了。。。

實(shí)現(xiàn)的思路

首先,給UIView綁定一個(gè)布局實(shí)例:

<UIView+TPCQuickAttribute.m>

- (TPCQuickAttribute *)tpc_quickAttribute {
    TPCQuickAttribute *quickAttribute = objc_getAssociatedObject(self, _cmd);
    if (!quickAttribute) {
        quickAttribute = [[TPCQuickAttribute alloc] initWithView:self];
        objc_setAssociatedObject(self, _cmd, quickAttribute, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return quickAttribute;
}

這個(gè)TPCQuickAttribute就是進(jìn)行快速設(shè)置frame的關(guān)鍵:

<TPCQuickAttribute.h>

#if TPC_OPEN_LOG == 1
#ifdef DEBUG
#define TPCLayoutLog(s, ... ) NSLog( @"<%s:(%d)> %@", __FUNCTION__, __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] ) 
#else
#define TPCLayoutLog(s, ... )
#endif
#else
#define TPCLayoutLog(s, ... )
#endif
@interface TPCQuickAttribute : NSObject <TPCQuickProtcol>
- (instancetype)initWithView:(UIView *)view;
@property (weak, nonatomic, readonly) UIView *view;
@property (weak, nonatomic) UIView *referView;
@property (assign, nonatomic, readonly) BOOL referViewIsSuperview;

- (TPCQuickAttribute *(^)(UIView * view))referToView;
- (TPCQuickAttribute *(^)(UIView *))addToView;
- (void)end;
@end

這個(gè)類包含了要進(jìn)行布局的view對(duì)象,還有進(jìn)行參考的referView。

一旦設(shè)置好了這兩者,就可以進(jìn)行下一步操作了:

<TPCQuickAttribute+Frame.m>
...
- (TPCQuickAttribute *(^)(CGFloat))alignLeftToLeft {
    return ^TPCQuickAttribute *(CGFloat offset) {
        return self.x(self.referViewX + offset);
    };
}

- (TPCQuickAttribute *(^)(CGFloat))alignLeftToRight {
    return ^TPCQuickAttribute *(CGFloat offset) {
        return self.alignLeftToLeft(self.referView.frame.size.width + offset);
    };
}
...

上面代碼參考referView,對(duì)view的x坐標(biāo)進(jìn)行設(shè)置。對(duì)于這種鏈?zhǔn)降膶?shí)現(xiàn)原理,就是利用了返回的block是一個(gè)無(wú)名函數(shù),可以通過(guò)( )執(zhí)行,這樣對(duì)上面的方法進(jìn)行調(diào)用,產(chǎn)生以下效果:

xxx.alignLeftToLeft(0)

了解了block的使用方式就沒(méi)什么復(fù)雜的了。

既然可以快速設(shè)置frame,那么常用屬性呢?再增加一個(gè)分類:

<TPCQuickAttribute+Appearance.m>
...
- (TPCQuickAttribute *(^)(UIColor *))backgroundColor {
    return ^TPCQuickAttribute *(UIColor *backgroundColor) {
        self.view.backgroundColor = backgroundColor;
        return self;
    };
}

- (TPCQuickAttribute *(^)(UIViewContentMode))contentMode {
    return ^TPCQuickAttribute *(UIViewContentMode contentMode) {
        self.view.contentMode = contentMode;
        return self;
    };
}

...

同理,綜合上面的代碼,就可以鏈?zhǔn)降貙懗鲈O(shè)置frame的代碼了。

小結(jié)

當(dāng)然,現(xiàn)在有了自動(dòng)布局,一般界面已經(jīng)不需要使用frame進(jìn)行布局了,代碼的自動(dòng)布局可以使用masonry,我私下也喜歡用storyboard+xib的方式寫一些小demo。

所以以上代碼純屬玩票==。

代碼鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容