當(dāng)給UIButton設(shè)置title和image后,就相當(dāng)于UIButton是由一個UIButtonLabel + 一個UIImageView組成。但是它們默認(rèn)的格式是固定的,即左面是一個UIImage,右面是一個UIButtonLabel。現(xiàn)在如果我們想讓UIImage顯示在這個button的上面,讓UIButtonLabel顯示在這個button的下面。
我們可以完全自定義一個控件來實(shí)現(xiàn)上面所說的,也可以在UIButton的基礎(chǔ)上改變它的內(nèi)部子控件。這里采用第二種方法。
-
首先,如果想改變子控件的位置,那么最先想到的可能是拿到這個button,然后通過訪問它的imageView和titleLabel屬性。
我們可以首先對這個button打印一下看看內(nèi)部的結(jié)構(gòu):
NSLog(@"%@", self.button.subviews);
打印的結(jié)果是一個空數(shù)組!這是怎么回事呢?
實(shí)際上UIButton內(nèi)部的子控件采用的是懶加載,也就是說如果沒有使用到相應(yīng)的子控件,那么是不會加載的。
那么我們就重新給這兩個控件的frame賦值,這樣不僅用到了這兩個控件使其加載,也可以看看是否可以直接改變這兩個控件的frame以達(dá)到將這兩個控件重新排列的目的。
但是如果你這么做,你會發(fā)現(xiàn)實(shí)際顯示的這個button內(nèi)部并沒有改變,說明直接改變UIButton內(nèi)部控件的frame是無法達(dá)到重新排列的目的的。
接著將這個button內(nèi)部的子控件打印出來:
NSLog(@"%@", self.button.subviews);
會發(fā)現(xiàn)subviews這個數(shù)組里現(xiàn)在是一個UIImageView + 一個UIButtonLabel,現(xiàn)在它們有了值(因?yàn)榍懊嬗玫搅诉@兩個控件,所以進(jìn)行了懶加載)。
可是仔細(xì)觀察,會發(fā)現(xiàn)這兩個控件的frame明明是我們剛剛賦值的frame,但是顯示出來的為什么又不是按這個frame顯示的呢?
因?yàn)榇蛴〕鰜淼膄rame只是我們剛剛設(shè)置的,而UIButton在顯示的時候會根據(jù)它的UIImageView和UIButtonLabel里面的內(nèi)容重新計算大小,所以即使我們改變了子控件的frame,也無法真正更改子控件的位置和尺寸。
-
第二種思路是可以繼承UIButton,在原來的按鈕的基礎(chǔ)上進(jìn)行改變。
比如我們創(chuàng)建一個UIButton的子類CYLButton,在CYLButton的實(shí)現(xiàn)文件中實(shí)現(xiàn)下面的方法:
- (CGRect)titleRectForContentRect: (CGRect)contentRect // 控制label顯示在哪和大小 { return CGRectMake(0, contentRect.size.width, contentRect.size.width, contentRect.size.height - contentRect.size.width); } - (CGRect)imageRectForContentRect: (CGRect)contentRect // 控制image顯示在哪和大小 { return CGRectMake(0, 0, contentRect.size.width, contentRect.size.width); } // contentRect一般來代表UIButton的bounds.size // 我們也可以在initWithFrame:方法中設(shè)置UIButton的內(nèi)部控件的屬性 - (instancetype)initWithFrame: (CGRect)frame { if (self = [super initWithFrame: frame]) { self.titleLabel.backgroundColor = [UIColor blueColor]; self.titleLabel.textAlignment = NSTextAlignmentCenter; self.imageView.backgroundColor = [UIColor yellowColor]; } return self; }
可以看到這種方法可以滿足我們的要求。但是也有弊端,如果我們在其中一個方法中設(shè)置的某一些想在另一個方法中也用到,那么就不是很方便。
-
更好的方法是重寫
layoutSubviews
方法,因?yàn)檫@個方法可以很方便地調(diào)整子控件。- (void)layoutSubviews { [super layoutSubviews]; CGFloat imageW = self.bounds.size.width; CGFloat imageH = imageW; self.imageView.frame = CGRectMake(0, 0, imageW, imageH); CGFloat titleY = imageH; CGFloat titleW = imageW; CGFloat titleH = self.bounds.size.height - titleY; self.titleLabel.frame = CGRectMake(0, titleY, titleW, titleH); }
這樣做可能會很奇怪,因?yàn)閯偛旁谶@個類的外面我們也改變的是imageView和titleLabel的frame,可是毫無作用,而在
layoutSubviews
方法里同樣修改,為什么會起作用了呢?因?yàn)閯偛盼覀冊谕饷嫘薷淖涌丶膄rame,但是當(dāng)執(zhí)行到內(nèi)部的
layoutSubviews
方法的時候會重新將它們的frame設(shè)置為image和title對應(yīng)的大小。而現(xiàn)在我們直接在layoutSubviews
中修改它們的frame,相當(dāng)于覆蓋了之前將它們的frame設(shè)置為默認(rèn)大小這一步驟,所以現(xiàn)在是可以成功的,并且因?yàn)樵谝粋€方法中,是可以共享變量的。另外需要注意,如果繼承自UIButton(比如CYLButton),那么當(dāng)有了數(shù)據(jù)模型,想在CYLButton的setter方法中給子控件賦值的時候,不能直接這樣:
self.imageView = ... self.text = ...
因?yàn)閟elf(CYLButton)是繼承自UIButton,所以無論是image還是title都是分狀態(tài)的,所以需要這樣:
[self setImage...]; [self setTitle...];
所以說能不能直接修改,取決于這個屬性分不分狀態(tài)。如果分狀態(tài),那么就不能直接修改。
?