顯示具有 iOS UI 標籤的文章。 顯示所有文章
顯示具有 iOS UI 標籤的文章。 顯示所有文章

2013年2月13日 星期三

假如時光倒流,UI元件如何縮放?

autolayout的世界裡,UI元件的位置和大小由constraint決定。然而某些特別的UI元件,像是UILabel, UIButton等,它們的大小卻深受它們所呈現的內容影響。接下來,彼得潘將以信樂團的名曲”假如”,說明UILabel的寬度如何地被歌詞的長短而左右。

1. 加入顯示假如歌詞的UILabel,”加長”按鈕和”縮短”按鈕。


調整UILabel的寬度和位置,使其剛好有三個constraint,距離上邊界,左邊界,右邊界都是標準間距的constraint。



2. 設定label,button和View Controller的連結。(ViewController.h)

@property (weak, nonatomic) IBOutlet UILabel *label;
- (IBAction)lengthenButPressed:(id)sender;
- (IBAction)shortenButPressed:(id)sender;

加長button的touch up inside event連結到lengthenButPressed:,縮短button的touch up inside event連結到shortenButPressed:。

3. 定義加長歌詞的lengthenButPressed:和縮短歌詞的shortenButPressed:。(ViewController.m)

- (IBAction)lengthenButPressed:(id)sender {
    self.label.text = @"假如時光倒流 我能做什麼 找妳沒說的 卻想要的";
}

- (IBAction)shortenButPressed:(id)sender {
    
    self.label.text = @"假如時光倒流";
}

執行結果:



由於被leading和trailing 2個constraint控制著,為了維持和左邊界,右邊界20 points的間距,label的寬度比歌詞佔據的空間寬上許多。當我們點選”加長”,歌詞內容增加了,但是label的寬度卻不為所動,造成我們只看到”找你沒說的 卻”,到底是卻什麼呢?是卻想念的?還是卻深愛的?別再猜了,其實有個神奇的方法,可以讓label擺脫constraint的束縛,增加寬度。



切換到label的Size Inspector頁面,將焦點放在它的Content Compression Resistance Priority區塊。此屬性控制著label是否可以隨著內容變多而增加它的尺寸。預設的水平(Horizontal) priority是750,然而控制著label和右邊界 20 points間距的constraint預設的priority卻是大於750的1000,所以label輸了,無法拉長它的寬度。


讓我們偷偷地將此constraint的priority從1000降為500。現在label的Horizontal Content Compression Resistance Priority 750遠大於500,因此現在它做主,它是老大,可以自由的拉長,不受constraint的限制。




執行結果:

label順利地拉長,終於可以看到完整的歌詞了,原來答案是”卻想要的”呀! 


讓我們回過頭來看看縮短歌詞的例子。點選縮短按鈕後,歌詞變短了。原來就算時光倒留,還是改變不了label臃腫的事實呀。


有沒有什麼靈丹妙藥可以讓label減肥,剛剛好寬度就等於呈現的文字內容呢?其實是有的,答案就藏在label的Content Hugging Priority裡。此屬性控制著label是否可以隨著內容變少而縮小它的尺寸。預設的水平(Horizontal) priority是250,然而控制著label和右邊界 20 points間距的constraint目前卻是大於250的500,所以label輸了,無法縮短它的寬度。



讓我們再一次地將constraint的priority調降,讓它變為100。現在label的Horizontal Content Hugging Priority 250遠大於100,因此現在它做主,它是老大,可以自由的縮短,不再受constraint的限制。



執行結果:

當歌詞縮短,時光倒留,label真的變苗條了(變短)
  



最後,如果嫌看歌詞不過癮,不如好好來聽聽信樂團演唱的假如吧。


2012年11月18日 星期日

消滅status bar怨念產生的黑洞 ~ 以LUCY的美麗魔力 !

iPhone畫面的最上方,有個幾乎無時無刻存在的status bar,顯示著時間,電量,訊號強度等重要資訊。許多遊戲類的App會隱藏status bar,因為它們邪惡地想讓我們沉迷遊戲,忘了時間。而非遊戲類的App一般會顯示status bar,讓我們在操作App時仍然能夠知道電量,時間等重要資訊,而不用離開App查詢。

有沒有兩全其美的方法呢? 當然有,古人有點石成金,彼得潘有點螢幕殺status bar,感謝和彼得潘同月同日生的超人氣Model LUCY的合作,接下來我們將示範如何觸碰螢幕控制status bar的存亡,並且清除status bar怨念產生的黑洞。

一. 在畫面上加入按鈕,顯示"正在聞著新鮮空氣的LUCY"




- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
 
     
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 320, [UIScreen mainScreen].bounds.size.height)];
    button.imageView.contentMode = UIViewContentModeScaleAspectFill;
    [button setImage:[UIImage imageNamed:@"lucy.jpg"] forState:UIControlStateNormal];
    [self.view addSubview:button];
    [button addTarget:self action:@selector(buttonPressed:)
                     forControlEvents:UIControlEventTouchUpInside];
 
 
}
說明:
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 320, [UIScreen mainScreen].bounds.size.height)];
設定button的高度為螢幕的高度,換句話說,在iPhone 4s時,是480 points,在iPhone 5時,是568 points。

二. 在按鈕被觸碰時,控制status bar的隱藏和現身

-(void)buttonPressed:(id)sender
{

    if([UIApplication sharedApplication].statusBarHidden)
    {
        [UIApplication sharedApplication].statusBarHidden = NO;
    }
    else
    {
        [UIApplication sharedApplication].statusBarHidden = YES;

    }
}
說明:
當status bar顯示時,點選按鈕叫它滾開,不想再看到它。
當status bar隱藏,而我們又開始思念它時,點選按鈕可以再把它追回來,讓它出現。

執行App:

status bar就像個聽話的女朋友,我們不想看到她時,點一下畫面她乖乖消失。我們想念她時,點一下畫面,她馬上出現。但是當她不在時,卻有個怪異的地方,原本她佔據的空間,變成一長條黑色的區塊(約20 points的高度)。看來一定是她可怕的怨念,留下那破壞畫面美感的黑洞。




三. 黑洞怨念形成的原因

讓我們來查查事情的真相,研究黑洞形成的原因吧。請加入以下的程式碼

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    NSLog(@"view %@", self.view);

}

當我們再一次執行App時,發現Log印出
view <UIView: 0x7125f50; frame = (0 20; 320 548); autoresize = RM+BM; layer = <CALayer: 0x7126530>>
謎底解開了,原來view controller的view一開始的起始y座標就在20,難怪status bar消失後,會留下高度20 points的黑洞。

四. 用LUCY佔滿螢幕的美麗消滅黑洞

還好status bar的怨念不像七夜怪談的貞子那麼可怕,不需要大法師,只要LUCY的美麗就能消滅它。LUCY的這張照片如此漂亮,值得佔滿整個螢幕。我們只要在剛剛的viewDidLoad method裡,將view controller的wantsFullScreenLayout設為YES,即可讓view controller的view佔滿螢幕,換句話說,起始點將是(0, 0),而不是(0, 20)

    self.wantsFullScreenLayout = YES;

執行App:

我們可以清楚看到,這次LUCY是完完全全佔滿螢幕的,所以status bar是重疊地覆蓋在她的上方。





也因為LUCY多了20 points的展示空間,所以這次她的鞋子沒有再不小心被切到了。若是回頭看看Log印出的訊息,可發現此時view controller的view果然從(0, 0)開始。
view <UIView: 0x7435ce0; frame = (0 0; 320 568); autoresize = RM+BM; layer = <CALayer: 0x74362c0>>



LUCY簡介:
粉絲團: Love LUCY Love 
生日:  2月7日 (和彼得潘一樣,好日子)
科系: 輔大心理系 ( 對人心有深入了解) 
星座: 水瓶座 ( 好星座,不是只有天秤座才出帥哥美女呀 )
職業: Model,代言/平面/網拍/廣告/主持/展場/活動
聯絡: lucy.chiayun@gmail.com



2012年10月24日 星期三

幫天然呆寶寶發呆,隱藏顯示煩人時間的status bar

iOS App在執行時,最上方往往有一條顯示時間,電量的status bar。大部分的情況下,這是個貼心的設計,讓我們可以時時刻刻看到時間,不會因為沉迷於App忘了重要的約會。然而有些App,比方遊戲,往往心懷不軌地想要我們沉迷於遊戲裡,因此偷偷地把status bar藏了起來。在最新的Xcode 4.5,將App的status bar設為隱藏更容易了。接下來彼得潘將以插畫家Hana Mini繪製的可愛角色,天然呆寶寶為例,為大家說明隱藏status bar的小小技巧。

                                                    愛上了夏天螢火蟲的天然呆


可愛的天然呆寶寶常常放空、發呆,反應慢,喜歡簡單的生活。不過最近他常常無法安心發呆,因為要隨時注意時間,晚上6點準時接送女朋友。為了讓他能自在地發呆,讓我們幫他把顯示煩人時間的status bar移除掉吧,偶爾也該讓女朋友學習自己坐計程車回家。



假設專案名稱是Test,讓我們切換到Target Test的Summary頁面,找到Status Bar區塊。看到了嗎? 就是那道光,"Hide during application launch"前方有個框框,將其勾選即可隱藏status bar。




執行App:

天然呆終於不再被status bar煩人的時間干擾,可以忘記晚上6點要接送女朋友,放心地放空發呆了 !

2012年10月11日 星期四

如女人心般難捉摸的圖片ContentMode

一張圖勝過千言萬語。多了美麗圖片加持的App,就是跟別人不一樣。在App裡想要展示這些美美的圖片,要嘛透過UIImageView,要嘛經由UIButton。然而圖片的比例往往跟我們想要顯示的大小有段落差,這時候就得依賴contentMode決定圖片的縮放方式了。接下來,彼得潘將介紹當UIButton遇上contentMode時,會產生哪些意想不到的惱人問題。(感謝彼得潘的朋友查爾斯提供可愛的狐狸圖片作為以下範例的主角。)





1. 在畫面上加入顯示狐狸圖片的UIButton

UIButton *imageButton = [[UIButton alloc] initWithFrame:CGRectMake(60, 80, 200, 200)];
 [imageButton setImage:[UIImage imageNamed:@"fox.png"] forState:UIControlStateNormal];
 [self.view addSubview:imageButton];

2. 在圖片上加上同樣200*200的半透明View,方便我們等會分析圖片的縮放。
 
UIView *whiteView = [[UIView alloc] initWithFrame:CGRectMake(60, 80, 200, 200)];
whiteView.backgroundColor = [UIColor colorWithWhite:1 alpha:0.3];
[self.view addSubview:whiteView];

執行App:

如圖所示,當利用setImage:forState:設定圖片時,預設的contentMode為UIViewContentModeScaleToFill,所以令人心疼地,可愛的小狐狸變形了。



讓我們將contentMode設為UIViewContentModeScaleAspectFit,救救小狐狸,讓牠維持比例縮放吧。

imageButton.imageView.contentMode = UIViewContentModeScaleAspectFit;


執行App:


如圖所示,圖片乖乖地待在我們限定的200*200範圍內維持比例縮放,不敢越雷池一步。(可從半透明的View辨別200*200所在範圍)



能不能貪心地希望小狐狸更清楚點,給小狐狸特寫呢?當然可以,只要將contentMode設為UIViewContentModeScaleAspectFill,即可讓小狐狸維持比例縮放,但又能勇敢地部分超出範圍,實現完全佔滿200*200範圍的目標。

imageButton.imageView.contentMode = UIViewContentModeScaleAspectFill;



執行App:



imageButton的imageView.clipsToBounds預設為YES,所以超出的部分看不到。相反的,如果是UIImageView物件,其clipsToBounds預設為NO,所以當contentMode設為UIViewContentModeScaleAspectFill時,我們將完整看到超出的部分。  




sometimes when we touch

除了clipsToBounds的差異外,UIButton和UIImageView最大的差別在於它可以觸碰,它是有感情,觸碰會有感應的,不像冰冷冷的UIImageView。如下圖所示,當sometimes when we touch發生時,仿彿有氣氛的夜晚來臨,圖片明顯變暗讓我們明確感覺正在觸碰中。




但在iOS 5時,當我們調整了UIButton的imageView.contentMode後,觸碰時卻會產生不正常的現象。如下圖所示,觸碰時圖片又回復為原來的UIViewContentModeScaleToFill。因此,在iOS 5時,為了解決這個問題,有個折衷的做法,那就是將UIButton的adjustsImageWhenHighlighted設為NO,如此雖然Touch時不會變暗,但至少圖片不會變形。




圖片尺寸問題

如果圖片的size較我們設定的UIButton尺寸小,當利用setImage:forState:設定圖片時,此時無論我們將UIButton的imageView設為哪一種contentMode,圖片都將頑固地維持原狀,不會放大。若是UIImageView物件,則不會有此問題。




特別感謝:

查爾斯的blog視說新語:

http://whereyou.pixnet.net/blog

查爾斯的FB視說新語:
https://www.facebook.com/EyeNews


2012年5月19日 星期六

縮放自如的熊大王 - ScrollView的縮放

愛麗絲夢遊仙境裡的Alice喝了神奇藥水後會在一瞬間放大縮小,距離這本書的出版已經一百多年了,放大縮小的藥水仍舊沒有發明,但是iPhone神奇的多點觸碰,已經搶先一步實現了縮放的功能。即便螢幕比我們的手掌還小,然而只要以小小的手指頭做縮放的手勢,即可實現圖片的縮放功能。透過內建的UIScrollView元件,圖片的縮放其實只是雕蟲小技,不足為外人道也。接下來就讓威風凜凜的森林熊大王以牠的特寫圖片為各位示範縮放的效果吧 。( 感謝藝術家朋友Jorinde Jankowski (張友鷦)提供可愛的熊大王)

1. 建立Single View Application專案 ZoomScrollViewTest。

2.  在畫面上添加佔滿螢幕的Scroll View和Image View。


3. 設定圖片的縮放比例
 


於Scroll View的Attributes Inspector頁面,設定圖片的縮放比例。如上圖所示,我們將Max設為2,如此到時候圖片放大的極限將為Image View尺寸的2倍。Min設為0.5,圖片縮小的極限將為Image View尺寸的一半。

4. 設定ZoomScrollViewTestViewController為scroll view的delegate。


到時候要實現圖片的縮放,還需要controller化身代理人,實作某個宣告於UIScrollViewDelegate的method。

5.  將image view連結至ZoomScrollViewTestViewController,宣告property imageView。

 

6.   遵從UIScrollViewDelegate protocol。 (ZoomScrollViewTestViewController.h)


@interface ZoomScrollViewTestViewController : UIViewController
<UIScrollViewDelegate>

7. 定義UIScrollViewDelegate的viewForZoomingInScrollView: method。




說明:
回傳欲縮放的UI元件。就像Alice在童話故事裡被選定具有放大縮小的天命,我們也必須指定誰具有天命。在這裡我們指定熊大王具有縮放的能力。可以放大,熊大王才能夠保護森林,可以縮小,熊大王才可以躲避獵人的追補。若是沒有實作此method,或是回傳nil,到頭來將還是一場空,使用者的手指再怎麼於畫面上縮放,熊大王還是無動於衷。

執行App:

正常尺寸的熊大王


為了保護森林子民放大的熊大王

為了躲避獵人追補縮小的熊大王



ps: 如果想在模擬器上測試縮放,感受熊大王的威風也是ok的。只要按住option鍵,即可以滑鼠模擬縮放手勢。

2012年4月18日 星期三

難纏的兔子王 - UIViewContentModeScaleAspectFill的陷阱


彼得潘的畫家友人Jorinde Jankowski (張友鷦) 有天突發奇想,
將結在樹上的Apple換成了可愛的兔子。




不過彼得潘身為蘋果迷,
當然想將兔子換回原先的蘋果。
所謂擒賊先擒王,
樹中間的兔子是我們的首要目標。
接下來彼得潘將利用UIViewContentModeScaleAspectFill,
只顯示圖片的中間區塊,
排除中間的兔子王。


利用UIViewContentModeScaleAspectFill以及將frame設為300*150,
應該可以讓原本577 * 722圖片縮放至300* 375 ( 375 = 722*300/577.0)
並且以中心為準切除上半部和下半部各112.5。
然而程式執行後,
結局卻出乎意料



沒想到,
竟然顯示完整的圖片,
圖片的大小和位置,
完全不是設定的(10, 180, 300, 150),
更可惡的,
彼得潘的眼中釘,中間的兔子王還在。
為了找出問題,
彼得潘再加上一塊半透明的長方形,
此長方形的frame設定為和彼得潘想要的圖片frame一致。




如上圖所示,
彼得潘想要的圖片大小和位置,
其實是圖片的中間區塊。
實現願望其實很簡單,
只要加入以下程式碼即可 

    imageView.clipsToBounds = YES;

clipsToBounds的預設值是NO,
將其設為YES,
即可讓超出範圍的部分被活生生的割除!


若是從Storyboard建立的ImageView,
在將Mode設為Scale To Fill時,
顯示的預覽將是手術成功後的結果。


不過想不到彼得潘又上當了,
由於Clip Subviews沒有勾選,
實際執行時顯示的還是手術前的模樣,
兔子王還是得意地在樹上搖擺。
因此,
記得要勾選Clip Subviews,
它的效果等同於將clipsToBounds設為YES。






2012年4月13日 星期五

改頭換面的Tab Bar

感謝藝術家朋友Jorinde Jankowski (張友鷦)提供的可愛圖片,
彼得潘接下來將利用這些活靈活現的動物主角表演tab bar的換裝秀。

1. 修改tab bar顏色
    tabBarController.tabBar.tintColor = [UIColor brownColor];



2. 修改tab bar的背景圖片
     tabBarController.tabBar.backgroundImage = [UIImage imageNamed:@"bar.png"];
     image的高度決定了bar的高度。




     
3. 修改被點選tab上icon的顏色
     tabBarController.tabBar.selectedImageTintColor = [UIColor orangeColor];
     未被點選tab上的icon顏色將是tab bar tintColor的相近顏色。



4. 與眾不同的tab
    想要隨心所欲,讓tab擁有獨一無二的外觀?
    很簡單,只要建立UIButton,
    再將此button貼到tabBar上,
    佔據原先tab所佔的空間,
    即可打造天馬行空的外觀,


    不過有一點別忘了,
    當按扭被點選時,
    需設定UITabBarController物件的selectedIndex才能切換至tab對應的頁面。

    



圖檔尺寸和命名

tab bar icon:
     format:  png
     name:  @2x for  high-resolution
     size:    60 * 60 for high-resolution     
                30 * 30 for low-resolution


2012年2月19日 星期日

viewDidLoad的致命陷阱,一點也不幽默的黑色隙縫


大權在握的view controller控制著畫面的呈現,
然而什麼該定義於viewDidLoad,
什麼又該定義於viewWillAppear:呢?
大部分的情況,viewDidLoad就好像一片歌手,
只會被呼叫一次,
在controller將畫面載入後被呼叫。
(有一種例外的情況發生於記憶體不足時,
 此時controller的viewDidUnload將被呼叫,
 釋放UI元件佔據的記憶體,
 因此之後當controller的畫面要顯示時,
 它的viewDidLoad將再度被呼叫。)
而viewWillAppear:則像劉德華一樣永遠那麼紅,
每當controller掌控的畫面欲顯示於螢幕上時,
它就會被呼叫。

今天下午,
微笑女孩蘇森力幫彼得潘畫了一個很可愛的背景圖片,
有著星星,月亮以及可愛的女孩。





由於背景圖片不會變化,
於viewDidLoad將它加入看起來滿合理的,
想不到顯示的結果卻令人失望。

- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
    [self configureView];
    
    NSLog(@"viewDidLoad view %@", self.view);
    
    self.view.backgroundColor = [UIColor blackColor];
    
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.frame];
    
    imageView.image = [UIImage imageNamed:@"star.jpg"];
    
    imageView.contentMode = UIViewContentModeScaleAspectFill;
    
    [self.view addSubview:imageView];
}

執行結果:


背景圖片並沒有佔滿畫面,
圖片和上方的bar間有一塊如同黑色幽默般的空隙。

這是因為在viewDidLoad和viewWillAppear:被呼叫時,self.view.frame其實是不同的。

012-02-19 23:34:29.814 Test[5142:f803] viewDidLoad view <UIView: 0x6a703a0; frame = (0 20; 768 1004); autoresize = W+H; layer = <CALayer: 0x6a7c0d0>>
2012-02-19 23:34:30.126 Test[5142:f803] viewWillAppear view <UIView: 0x6a703a0; frame = (0 0; 768 960); autoresize = W+H; layer = <CALayer: 0x6a7c0d0>>


我們可以發現,當viewWillAppear:被呼叫時,view才被調整至正確的位置和大小。 (960 = 1024 -20 - 44, 20是status bar的高度,44是navigation bar的高度) 所以當初我們將imageView的frame設為和self.view一樣時,其實有2個問題。

1. y軸offset偏移20 pixel,成為黑色空隙的原凶。
2. 圖片的高度比較大,1004比960整整大了44 pixel

解決第一個問題很簡單,


UIImageView *imageView = [[UIImageView alloc]   initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];


只要傳入0,即可讓Y軸不再偏移20 pixel。

至於高度的問題,
則需啟動UI元件自動縮放的天性

imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

UIViewAutoresizingFlexibleHeight將讓subview的高度隨著superview的高度變化。這就好似當父親縮水時,兒子為了不讓父親發現這殘酷事實,也跟著縮水。可謂最高明的孝順之道呀!


執行結果:





終於,微笑女孩蘇森力的畫作完美地成為App的背景,相信圖畫中的女孩也該會心的一笑吧。


















2011年4月10日 星期日

2011年3月29日 星期二

UITextField: 按下鍵盤上的return後,讓鍵盤消失

關鍵:  UIControlEventEditingDidEndOnExit和resignFirstResponder


ex:
          Enter鍵按下時觸發的method

    -(void)textFieldDone:(UITextField*)textField
    {
         [textField resignFirstResponder];
    }


          建立TextField

    textField = [[UITextField alloc] initWithFrame:
                              CGRectMake(5, 5,  
                                         image.size.width,
                                         image.size.height)];
    [imageView addSubview:textField];
    [textField addTarget:self  
          action:@selector(textFieldDone:)  
         forControlEvents:UIControlEventEditingDidEndOnExit];
    [textField release];