2014年8月15日 星期五

Pass Value by Block programmatically using Object-C language

【說明】

先前我們利用自訂代理人的方式將值回傳,參考這篇,現在我們要利用Block的方式將值回傳。

使用Block的3個步驟:
1.  宣告Block。
2.  實作Block。
3.  呼叫Block。

Block可以想成是匿名的Function,Block的最大好處就是可以在別的method內實作,取得method內的區域變數。


【專案開發步驟】

建立專案:

請參考這篇。

設計使用者介面:

請參考這篇。

修改DetailViewController.h檔案:

撰寫使用Block的步驟1,宣告Block,如下所示,將Block宣告在@interface區,讓其他的Class也可以使用。

@property (copy, nonatomic) void (^passVale)(NSString*);
有宣告Property代表可以getter跟setter,一定要使用copy。

修改DetailViewController.m檔案:

撰寫使用Block的步驟3,呼叫Block,如下所示。

- (IBAction)click:(UIButton *)sender {
    self.passVale(self.nameField.text);
    [self.navigationController popToRootViewControllerAnimated:YES];
}
當DetailViewController接收到按鈕按下後會執行click這個function,呼叫passValue這個function,並傳送TextField內的文字。NavigationController傳送message,請求回到Root View Controller。

修改MainViewController.m檔案:

撰寫使用Block的步驟2,實作Block,如下所示。

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    DetailViewController *detailViewController = segue.destinationViewController;
    detailViewController.passVale = ^(NSString *name) {
        self.nameLabel.text = name;
    };
}
利用prepareForSegue抓到DetailViewController的指標。實作Block的method,會傳NSString過來,此時使用UILabel去接收它即可。


【執行結果】

    

    


【專案範例】

2014年8月14日 星期四

Custom Delegate programmatically using Swift and Object-C language

【說明】

此份筆記紀錄如何自訂Delegate,並且使用代理人機制。

情境:當被NavigationController包著時,A ViewController要接收B ViewController所回傳的資料。B ViewController是擁有代理人機制的Controller,A ViewController要成為B ViewController的代理人。

B ViewController要成為擁有代理人機制必須有三步驟:
1.  建立自訂的Protocol
2.  建立Delegate實體變數
3.  當事件發生時,發送message給代理人

A ViewController要成為B ViewController的代理人必須有三步驟:
1.  指定代理人是誰
2.  代理人必須採納Protocol
3.  代理人必須實作method


【專案開發步驟】

建立專案:

使用Single View Application模板建立一個名為CustomDelegate的專案,使用iPhone裝置。

設計使用者介面:

替View加入NavigationController,並完成介面的設計,並將會用到的元件與對應的ViewController做連結,如下圖所示。

設計使用者介面
建立並修改B ViewController:

新增Class,並取名為DetailViewController,並與Storyboard內的ViewController做連結,如下圖所示。

<Swift>
宣告Storyboard內的ViewController的Class是誰_Swift
<Object-C>
宣告Storyboard內的ViewController的Class是誰_Object-C

擁有代理人機制步驟1,建立自訂的Protocol。

<Swift>
在DetailViewController.swift內新增程式碼,程式碼必須撰寫在現有的Class外,如下所示。

protocol DetailViewControllerDelegate {
    func detailViewControllerWithName(name: NSString)
}

class DetailViewController: UIViewController {
}

<Object-C>
在DetailViewController.h內新增程式碼,程式碼必須撰寫在@interface外,如下所示。

@protocol DetailViewControllerDelegate
-(void)detailViewControllerWithName:(NSString*)name;
@end

@interface DetailViewController : UIViewController
@end

擁有代理人機制步驟2,建立Delegate實體變數,如下所示。

<Swift>
在DetailViewController.swift內新增程式碼,程式碼必須撰寫在現有的Class內,如下所示。

class DetailViewController: UIViewController {
    var delegate: DetailViewControllerDelegate?
}

<Object-C>
在DetailViewController.h內新增程式碼,程式碼必須撰寫在@interface內,如下所示。

@interface DetailViewController : UIViewController
@property(nonatomic,weak)id<DetailViewControllerDelegate> delegate;
@end

擁有代理人機制步驟3,當事件發生時,傳送message給代理人。

<Swift>
在DetailViewController.swift內新增程式碼,程式碼必須撰寫在現有的Class內,如下所示。

@IBAction func click(sender: UIButton) {
    navigationController.popToRootViewControllerAnimated(true)
    delegate?.detailViewControllerWithName(nameField.text)
}

<Object-C>
在DetailViewController.m內新增程式碼,程式碼必須撰寫在@implemtation內,如下所示。

- (IBAction)click:(UIButton *)sender {
    [self.navigationController popToRootViewControllerAnimated:YES];
    [self.delegate detailViewControllerWithName:self.nameField.text];
}

修改A ViewController:

將Class名改為MainViewController,並與Storyboard內的ViewController做連結,如下圖所示。

<Swift>
宣告Storyboard內的ViewController的Class是誰_Swift
<Object-C>
宣告Storyboard內的ViewController的Class是誰_Object-C

成為代理人步驟1,指定代理人是誰。
利用prepareForSegue這個method得到DetailViewController這個Class指標。

<Swift>
在MainViewController.swift內新增程式碼,程式碼必須撰寫在現有的Class內,如下所示。

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
    let detailViewController: DetailViewController = segue.destinationViewController as DetailViewController
    detailViewController.delegate = self
}

<Object-C>
在MainViewController.m內新增程式碼,程式碼必須撰寫在@implemtation內,且需#import "DetailViewController.h",如下所示。

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    DetailViewController *detailViewController = segue.destinationViewController;
    detailViewController.delegate = self;
}

成為代理人步驟2,代理人必須採納Protocol。

<Swift>
在MainViewController.swift內新增程式碼,如下所示。

class MainViewController: UIViewController, DetailViewControllerDelegate {
}

<Object-C>
在MainViewController.m內新增程式碼,如下所示。

@interface MainViewController ()<DetailViewControllerDelegate>
@end

成為代理人步驟3,代理人必須實作method。
實作我們剛剛在自訂的Protocol內所宣告的method。

<Swift>
func detailViewControllerWithName(name: NSString) {
    nameLabel.text = name
}

<Object-C>
-(void)detailViewControllerWithName:(NSString *)name {
    self.nameLabel.text = name;
}
當DetailViewController內的Done被按下後會觸發click的function,會執行detailViewControllerWithName這個method,並會將nameField的文字傳送給Delegate。當代理人實作這個method時就會收到從DetailViewController傳過來的文字,並用name作為代表。所以我們只要把傳過來的文字顯示在我們的Label上即可。


【執行結果】

    
    


【專案範例】

2014年8月13日 星期三

UIScrollView and Page Control programmatically using Swift and Object-C language

【說明】

此份筆記紀錄如何用Storyboard實現UIScrollView,並用程式碼控制UIScrollView與Page Control之間的互動。


【專案開發步驟】

建立專案:

使用Single View Application模板建立一個名為ScrollView的專案,使用iPhone裝置。

設計使用者介面:

替View加入NavigationController,並將title設為ScrollView,如下圖所示。

設計使用者介面
加入ScrollView:

拖曳ScrollView物件至ViewController內,並水平垂直置中,如下圖。

加入ScrollView物件
設定ScrollView的內容:

將UIScrollView的Constraints上下左右都做設定,如下圖所示。

設定ScrollView的Constraints
拖曳一個View的物件到Storyboard內,並設定水平垂直置中,如下圖所示。

拖曳View物件至Storyboard
點選到該頁的ViewController,設定模擬的大小為960,如下圖所示。

設定頁面大小
將方才所額外加入的View物件添加背景顏色,方便我們辨別,如下圖所示。

更改View的背景顏色
再新增兩個與前一個加入的View相同大小的View至頁面內,將拉長的頁面填滿,並更換頁面的背景顏色方便識別,如下圖所示。

新增View物件
設定左邊View的Constraints為上下左右與寬跟高,如下圖所示。

設定左邊View的Constraints
設定中間View的Constraints為上下左右與寬,如下圖所示。

設定中間View的Constraints
設定右邊View的Constraints為上下左右與寬,如下圖所示。

設定右邊View的Constraints
設定完畢後即可將模擬的大小調回320,如下圖所示。

調整模擬頁面的大小
點選該頁面的ViewController,到屬性裡面將「Adjust Scroll View Insets」取消勾選,如下圖所示。

取消勾選項目
點選ScrollView物件,到屬性裡面將「Paging Enabled」勾選,如下圖所示。

勾選項目
加入Page Control:

拖曳一個Page Control物件至View裡面,一定要放在ScrollView下面,否則會被蓋住,如下圖所示。

加入ScrollView
調整ScrollView至適當的位置,如下圖所示。

調整Page Control位置
撰寫UIScrollView與Page Control的互動:

建立實體變數名稱,並與Storyboard做連接,如下所示。

<Swift>
@IBOutlet var scrollView: UIScrollView!
@IBOutlet var pageControl: UIPageControl!

<Object-C>
@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
@property (strong, nonatomic) IBOutlet UIPageControl *pageControl;

設定UIScrollView的代理人是自己,如下所示。

<Swift>
scrollView.delegate = self

<Object-C>
self.scrollView.delegate = self;

代理人必須採納Protocol,如下所示。

<Swift>
class ModalViewController: UIViewController, UIScrollViewDelegate {

    @IBOutlet var scrollView: UIScrollView!
    @IBOutlet var pageControl: UIPageControl!
}

<Object-C>
@interface ViewController () <UIScrollViewDelegate>

@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
@property (strong, nonatomic) IBOutlet UIPageControl *pageControl;


@end

實作Delegate的method,如下所示。

<Swift>
func scrollViewDidEndDecelerating(scrollView: UIScrollView!) {


}

<Object-C>
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
   

}

將以下程式碼放至Delegate的method內,如下所示。

<Swift>
var stopPoint: CGPoint = scrollView.contentOffset
var stopIndex: NSNumber = (stopPoint.x)/320
pageControl.currentPage = stopIndex

<Object-C>
CGPoint stopPoint = scrollView.contentOffset;
int stopIndex = (int)stopPoint.x/320;
self.pageControl.currentPage = stopIndex;
利用stopPoint得到scrollView的偏移量。利用偏移量除320得知Index,stopIndex應為0,1,2。最後設定Page control現在正確的頁面是在哪一個。


【執行結果】

    


【專案範例】

2014年8月12日 星期二

Add a xib or Storyboard to rootViewController

【說明】

此份筆記紀錄如何加入xib或是Storyboard到rootViewController內。


【片段程式碼】

若要加入xib為rootViewController,則可以使用以下的程式碼。

<Swift>

let loginViewController = LoginViewController(nibName: "LoginViewController", bundle: nil)
window!.rootViewController = loginViewController

<Object-C>

LoginViewController *loginViewController = [[LoginViewController alloc] initWithNibName:@"LoginViewController" bundle:nil];
self.window.rootViewController = loginViewController;

若要加入Storyboard為rootViewController,則可以使用以下的程式碼。

<Swift>

let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
window!.rootViewController = storyboard.instantiateInitialViewController() as UIViewController

<Object-C>

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
self.window.rootViewController = storyboard.instantiateInitialViewController;


如此一來Storyboard與xib就可以混合著使用,有更多元的可能性出現。

2014年8月11日 星期一

Storyboard's equal heights

【說明】

此份筆記紀錄如何使用Storyboard固定Button的位置及寬度,但隨著螢幕的長度變化高會跟著改變。


【專案開發步驟】

建立專案:

使用Single View Application模板建立一個名為autoLayoutHeight的專案,使用iPhone裝置。

設計使用者介面:

拖曳三個Button至View內並設定不同的背景顏色,如下圖所示。

設計使用者介面
設定Constraints:

點選最上面的Button,設定對齊View的上方、左邊及右邊,並設定固定寬度,最後加入這四個Constraints,如下圖所示。

第一個按鈕的Constraints
點選中間按鈕,設定與第一個按鈕的距離,如下圖所示。

設定中間按鈕與第一個按鈕間的距離
點選中間按鈕,設定左右兩邊對齊第一個按鈕,如下圖所示。

設定第二個按鈕兩旁對齊第一個按鈕
點選第三個按鈕,重複第二個按鈕的設定動作,設定與前一個按鈕的距離,以及兩旁對齊前一個按鈕。

點選三個按鈕,設定Equal Heights,如下圖所示。

設定三個按鈕有相同的高度
最後點選第三個按鈕,設定對View下方的距離,如下圖所示。

設定按鈕與頁面下方的距離


【執行結果】

iPhone 4s模擬畫面
iPhone 5s模擬畫面

2014年8月6日 星期三

Storyboard's segue running by coding

【說明】

此份筆記在紀錄如何用寫程式的方式去執行Storyboard的Segue。

執行Segue的五大流程步驟:
Step 1:初始化目標頁面(XML)
Step 2:建立Segue的實體
Step 3:執行來源的prepareForSegue的method。
Step 4:執行Segue的performSegueWithIdentifier的method。
Step 5:釋放Segue的記憶體。

現在我們要將第4步驟抽出用寫程式的方式去呈現執行Segue的動畫,亦即是Push一個頁面到Navigation Controller。


【專案開發步驟】

建立專案:

使用Single View Application模板建立一個名為segueWithCoding的專案,使用iPhone裝置。

設計使用者介面:

將single view加入Navigation Controller,如下圖所示。

加入Navigation Controller
拖曳新的ViewController至Storyboard內,如下圖所示。

加入新的ViewController
建立Segue,並選擇push,如下圖所示。

建立Segue
選擇push
替Segue設定Identifier。

設定Segue的Identifier
用程式建立UIButton:

在ViewDidLoad內撰寫建立UIButton的程式碼,如下所示。

<Swift>
var theButton: UIButton = UIButton(frame: CGRectMake(50, 100, 150, 30))
theButton.setTitleColor(UIColor.redColor(), forState: .Normal)
theButton.setTitle("GoSecondView", forState: .Normal)
theButton.addTarget(self, action: "GoSecondView", forControlEvents: .TouchUpInside)
view.addSubview(theButton)

<Object-C>
UIButton *theButton = [[UIButton alloc] initWithFrame:CGRectMake(50, 100, 150, 30)];
[theButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[theButton setTitle:@"GoSecondView" forState:UIControlStateNormal];
[theButton addTarget:self action:@selector(goSecondView:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:theButton];

撰寫當按鈕按下後會觸發的func,如下所示。

<Swift>
func goSecondView(sender: UIButton) {
        
}

<Object-C>
-(void)goSecondView:(UIButton*)sender {

}

執行Segue的動畫:

當按鈕按下後要啟動Segue,所以可以透過performSegueWithIdentifier這個method來達成。

<Swift>
func goSecondView(sender: UIButton) {
    performSegueWithIdentifier("goSecondView", sender: nil)
}

<Object-C>
-(void)goSecondView:(UIButton*)sender {
    [self performSegueWithIdentifier:@"goSecondView" sender:nil];
}


【執行結果】

如此一來就可以用程式執行Segue的動畫,但是其餘的步驟還是由XML來完成。

UITapGestureRecognizer programmatically using Swift and Object-C language

【說明】

此份筆記是在紀錄當使用者點擊UITextField後想要點擊View就可以將Keyboard移除的方法。


【專案開發步驟】

建立專案:

使用Single View Application模板建立一個名為tapGestureRecognizer的專案,使用iPhone裝置。

設計使用者介面:

將UITextField加入到Storyboard內,並建立IBOutlet,如下圖所示。

設計使用者介面
將Tap Gesture Recognizer加入至View內:

點選Storyboard內的View,並將Tap Gesture Recognizer拖曳到View裡面,如下圖所示。

拖曳Tap Gesture Recognizer
替Tap Gesture Recognizer建立IBAction,並與Storyboard連結:

建立IBAction,如下所示,傳進的會是UITapGestureRecognizer。

<Swift>
@IBAction func userTap (sender: UITapGestureRecognizer) {
        
}

<Object-C>
-(IBAction)userTap:(UITapGestureRecognizer*)sender {

}

與Storyboard做連結,如下圖所示。

IBAction與Storyboard做連結
撰寫resign keyboard的程式:

於userTap內撰寫以下程式。

<Swift>
@IBAction func userTap (sender: UITapGestureRecognizer) {
    theTextField.resignFirstResponder()
}

<Object-C>
- (IBAction)userTap:(UITapGestureRecognizer *)sender {
    [self.theTextField resignFirstResponder];
}
讓TextField變成不是第一回應使用者的元件,使用resignFirstResponder這個method。


【執行結果】

如同文章說明所言,使用者按到UITextField時再按到View會消除keyboard。