2012年12月21日 星期五

iOS內如何寫一個有background image的label

給自己一個任務: 看能否寫出一個簡單的計算機程式. 在App store上面找幾個範例, 決定參考底下這個程式的layout:

所以我想,就先來implement上面的結果區吧!

用PhoneView把程式的bundle打開,看到一個background image file,長得像底下這樣子:


影像的長寬是 615 x 205,與上方顯示區的大小不一樣 !

所以我想,應該是想辦法放一個UILabel,然後設定他的background image是這張圖,iOS應該會自動調整影像的大小,可是我還要注意不要讓iOS把影像的邊緣給弄糊掉了,iOS內應該有類似Android Nine-Patch的觀念才對。

所以去網路上找了一些code,寫成了這個樣子:
    
    // Stretch the image with 1x1 caps on each side
    //
    UIImage* imageBackground = 
       [[UIImage imageNamed:@"Rez/iPhone/bg-display.png"]             
           stretchableImageWithLeftCapWidth:1 topCapHeight:1];
    
   _resultLabel.backgroundColor
       [UIColor colorWithPatternImage:imageBackground];
    
可是這個並不work!當圖片大小與Label不一致時,colorWithPatternImage並不會自動調整圖片大小,而是會做pattern fill,所以無法達成我要的效果。

到StackOverflow上面查了一下,有人suggest改用UITextField。由於UITextField可以指定backgroundImage,所以可以自動scale圖片。唯一要注意的只要把UITextField設定成是readonly就行了。

所以我做了第二次嘗試:

    UIImage* imageBackground = ..  

    _resultText.borderStyle = UITextBorderStyleNone;
    _resultText.background = imageBackground;

這樣子看到的效果是像這個樣子:


背景影像的scale以及邊緣的處理都是正確的,可是文字太靠右邊了,我需要一些padding!

再次查詢,這時候發現問題越來越大了,有的作法是subclass TextField,然後自定他文字的bounding box,有的作法是在TextField的右緣再放入一個空的view,我則是先試看看這樣子的作法:
  • 在TextField的下方放一個UIView,設定UIView的背景為底圖,
  • 把TextField改成是透明的,然後position TextField來產生padding的效果
這時候又發現,UIView也是沒有backgroundImage property,只有backgroundColor(跟UILabel一樣),所以圖片還是只能用pattern fill的方式擺放!看來還是必須想辦法自己寫code來scale background image。以下是相關的程式碼:

    UIImage* imageBackground = ..  

    // resize image to fit _resultTextBackground
    //
    CGRect rect = _resultTextBackground.bounds;
    UIImage* imageScaled = 
      [self imageWithImage:imageBackground scaledToSize:rect.size];

    _resultTextBackground.backgroundColor = 
      [UIColor colorWithPatternImage:imageScaled];
    
    _resultText.borderStyle = UITextBorderStyleNone;
    [_resultText setBackgroundColor:[UIColor clearColor]];
    
    const int cxPadding = 4;
    const int cyPadding = 4;
    _resultText.bounds = CGRectMake(cxPadding, cyPadding, 
       rect.size.width - cxPadding * 2, rect.size.height - cyPadding * 2);

另外

- (UIImage*)imageWithImage:(UIImage*)image
              scaledToSize:(CGSize)newSize;
{
   UIGraphicsBeginImageContext( newSize );
   [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
   UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
   UIGraphicsEndImageContext();

   return newImage;
}

這樣子的效果就對了

只是我開始想到其他的問題:
  1. imageWithImage這個是個common function,從xcode的角度該用什麼方式來建構library呢?or 這個API可以用category的方式加到UIImage class內 ?
  2. 如果我必須自己scale image的話,那也許直接用UILabel也可以做的出來?
  3. 這code看起來已經有點亂了,也許要試著把這組control寫成一個獨立的元件來用?
問題1跟3是很基礎的架構性的問題,希望下次可以找到好的方法!!


沒有留言:

張貼留言