1. 程式人生 > >UIButton實現上圖下字,左圖右字等組合形式以及sizeToFit的簡單理解

UIButton實現上圖下字,左圖右字等組合形式以及sizeToFit的簡單理解

UI需求中很會遇到很多文字和圖片混排的效果,暴力做法就是圖片用Image,文字用Label分開來實現,然後組合,但是我們有UIButton,可以根據他的imageEdgeInsets和titleEdgeInsets來實現需要的效果

最終四種效果如上圖,最後還附帶了虛線的畫法

我們預設情況下給UIButton設定image和titile之後是這樣的


左邊圖片和右邊文字,預設居中,而且緊靠著

那麼UIButton暴露了兩個屬性來修改相對位置,需要注意的是

This property is used only for positioning the image during layout. The button does not use this property to determine intrinsicContentSize and sizeThatFits:

這句話的意思是這個屬性只是修改其相對位置,不會通過這個屬性來改變UIButton的內部大小的

下圖是更改後的兩個效果

                     

NSLog(@"wowowowoowowowowowowow%@",NSStringFromCGSize(self.button.intrinsicContentSize));
    2016-11-04 16:57:20.204 UIButtonEdge[6173:359806] wowowowoowowowowowowow{66, 21}

可以看出列印結果,根本影響不了其內在本質的大小,也就是UIButton就這麼大,無論你偏移去哪了,那麼這個時候你點選了“文字”是不會觸發事件的,你也可以看出,他的大小根本沒有改變

這樣不是我們要的最終效果啊,如果大小不進行相應的擴充,那麼不是白搞了,就好比外表變了,本質還是沒變,一樣不適用,看下面這句話

But there is a property that can help, and that's contentEdgeInsets. The docs for that say, in part:

The button uses this property to determine intrinsicContentSize and sizeThatFits:.

這句話就是讓我們根據其他屬性進一步進行content的擴充

最終我們要的效果就是

2016-11-04 17:02:30.546 UIButtonEdge[6198:366700] wowowowoowowowowowowow{166, 21}

列印結果和效果都出來了,這樣UI變了,UIButton的content也跟著變大了,點選事件正常執行,就能滿足我們的需求了

邏輯大概是這麼個邏輯,我們分解下,那麼首先你得明白Image和titile的相對位置

跟tableView的contentInset是類似的,如果只有title,那它上下左右都是相對於button的,image也是一樣;如果同時有image和label,那這時候image的上左下是相對於button,右邊是相對於label的;title的上右下是相對於button,左邊是相對於image的。下面四種用到的變數
CGFloat gap = 10.f;
    CGFloat labelWidth = self.button.titleLabel.bounds.size.width;
    CGFloat imageWidth = self.button.imageView.bounds.size.width;
    CGFloat imageHeight = self.button.imageView.bounds.size.height;
    CGFloat labelHeight = self.button.titleLabel.bounds.size.height;
    
    // 這裡都計算完畢了,很簡單,例如 imageWidth是A  labelWidth是B
    // 那麼image要居中就要X軸移動 (A+B)/2 - A/2
    // label要居中就要X軸移動 (A+B)/2 - B/2
    // Y軸移動就直接除以2 然後加上間隙就好了
    // 圖片中心對齊控制元件XY軸的偏移量
    CGFloat imageOffSetX = labelWidth / 2;
    CGFloat imageOffSetY = imageHeight / 2 + gap / 2;
    CGFloat labelOffSetX = imageWidth / 2;
    CGFloat labelOffSetY = labelHeight / 2 + gap / 2;


1.圖片在左邊,文字在右邊(預設的)


// 左圖右字
    CGFloat gap = 10.f;
    self.button.imageEdgeInsets = UIEdgeInsetsMake(0, -gap / 2, 0, gap / 2);
    self.button.titleEdgeInsets = UIEdgeInsetsMake(0, gap / 2, 0 , - gap / 2);
    self.button.contentEdgeInsets = UIEdgeInsetsMake(0, gap / 2, 0, gap / 2);


2.圖片在右邊,文字在左邊


// 右圖左字
    CGFloat gap = 10.f;
    self.button2.imageEdgeInsets = UIEdgeInsetsMake(0,labelWidth + gap / 2 , 0, -labelWidth - gap / 2);
    self.button2.titleEdgeInsets = UIEdgeInsetsMake(0, -imageWidth - gap / 2, 0, imageWidth+gap / 2);
    self.button2.contentEdgeInsets = UIEdgeInsetsMake(0, gap / 2, 0, gap / 2);


3.圖片在上面,文字在下面


 // 上圖下字
    // 讓UIButton能保證邊緣自適應 居中的時候需要
    // 當上下排布的時候,要根據edge來填充content大小
    CGFloat maxWidth = MAX(imageWidth,labelWidth); // 上下排布寬度肯定變小 獲取最大寬度的那個
    CGFloat changeWidth = imageWidth + labelWidth - maxWidth; // 橫向縮小的總寬度
    CGFloat maxHeight = MAX(imageHeight,labelHeight); // 獲取最大高度那個 (就是水平預設排布的時候的原始高度)
    CGFloat changeHeight = imageHeight + labelHeight + gap - maxHeight; // 總高度減去原始高度就是縱向寬大宗高度
    self.button4.imageEdgeInsets = UIEdgeInsetsMake(-imageOffSetY, imageOffSetX, imageOffSetY, -imageOffSetX);
    self.button4.titleEdgeInsets = UIEdgeInsetsMake(labelOffSetY, -labelOffSetX, -labelOffSetY, labelOffSetX);
    self.button4.contentEdgeInsets = UIEdgeInsetsMake(changeHeight - labelOffSetY, - changeWidth / 2, labelOffSetY, -changeWidth / 2);


4.文字在上面,圖片在下面


// 上字下圖
    // 讓UIButton能保證邊緣自適應 居中的時候需要
    // 當上下排布的時候,要根據edge來填充content大小
    CGFloat maxWidth = MAX(imageWidth,labelWidth); // 上下排布寬度肯定變小 獲取最大寬度的那個
    CGFloat changeWidth = imageWidth + labelWidth - maxWidth; // 橫向縮小的總寬度
    CGFloat maxHeight = MAX(imageHeight,labelHeight); // 獲取最大高度那個 (就是水平預設排布的時候的原始高度)
    CGFloat changeHeight = imageHeight + labelHeight + gap - maxHeight; // 總高度減去原始高度就是縱向寬大宗高度
    self.button6.imageEdgeInsets = UIEdgeInsetsMake(imageOffSetY, imageOffSetX, -imageOffSetY, -imageOffSetX);
    self.button6.titleEdgeInsets = UIEdgeInsetsMake(-labelOffSetY, -labelOffSetX, labelOffSetY, labelOffSetX);
    self.button6.contentEdgeInsets = UIEdgeInsetsMake(labelOffSetY, -changeWidth / 2, changeHeight - labelOffSetY, -changeWidth / 2);


注:這上面四種ContentEdgeInsets都是擴充或者縮小移動後的Button大小的,這樣保證了效果也保證了點選事

件,感覺沒必要再單獨封裝成Category了,直接呼叫也很簡單,需要的自行可以參考下

這裡涉及到contentInsets的變化,UIButton的這種情況需要手動更改,我們另一種實現(測試UILabel)

sizeThatFits 和 sizeToFit

- (CGSize)sizeThatFits:(CGSize)size;    // return 'best' size to fit given size. does not actually resize view. Default is return existing view size

- (void)sizeToFit;                      // calls sizeThatFits: with current view bounds and changes bounds size.

註釋的個人理解:

當我們呼叫sizeToFit的時候,會call sizeThatFits來計算出best size來適應內在控制元件的最小尺寸,而且該方法還會change bounds size,就是直接更改呼叫者的frame

而直接呼叫sizeThatFits只是計算出最適合內部控制元件的size返回,僅此而已,根本不會更改呼叫者的frame

直接看程式碼的列印來的直接點

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, 64, 0, 0)];
    label.backgroundColor = [UIColor redColor];
    label.font = [UIFont systemFontOfSize:18];
    label.text = @"呵呵呵呵";
    CGSize sizeThatFit = [label sizeThatFits:CGSizeMake(1000, 1000)];
    NSLog(@"sizeThatFit------> width = %lf,height = %lf",sizeThatFit.width,sizeThatFit.height);
    // sizeThatFit------> width = 73.500000,height = 21.500000
    
    NSLog(@"sizeThatFit之後的labelFrame ------> width = %lf,height = %lf",label.frame.size.width,label.frame.size.height);
    // sizeThatFit之後的labelFrame ------> width = 0.000000,height = 0.000000  沒有變化 只是返回值而已,不改變呼叫者frame
    [label sizeToFit];
    NSLog(@"sizeTofit之後的labelFrame ——----> width = %lf.height = %lf",label.frame.size.width,label.frame.size.height);
    
    //sizeTofit之後的labelFrame ——----> width = 73.500000.height = 21.500000 第一計算frame,第二根據sizeThatFit計算返回的frame改變呼叫者frame

11.11更新 (通過該方法獲取WebView的高度)

有時候需要根據不同的內容調整UIWebView的高度,以使UIWebView剛好裝下所有內容,不用拖動,後面也不會留白。有兩種方式可根據載入內容獲取UIWebView的合適高度,但都需要在網頁內容載入完成後才可以,即需要在webViewDidFinishLoad回撥中使用

- (void)webViewDidFinishLoad:(UIWebView *)webView

{
    
    CGRect frame = webView.frame;
    
    frame.size.height = 1;
    
    webView.frame = frame;
    
    CGSize fittingSize = [webView sizeThatFits:CGSizeZero];
    
    frame.size = fittingSize;
    
    webView.frame = frame;
    
}

sizeThatFits方法有個問題,如果當前UIView的大小比剛好合適的大小還大,則返回當前的大小,不會返回最合適的大小值,所以使用sizeThatFits前,先將UIWebView的高度設為最小,即1,然後再使用sizeThatFits就會返回剛好合適的大小。

另一種方法

- (void)webViewDidFinishLoad:(UIWebView *)webView

{    CGRect frame = webView.frame;
    
    NSString *fitHeight = [webview stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight;"];
    
    frame.size.height = [fitHeight floatValue];
    
    webView.frame = frame;
}

一個非常簡單易懂的Demo地址:點選開啟連結