Friday, October 16, 2015

Lập trình iOS: Bài 4 - UINavigationViewController và ứng dụng Multiple ViewController

Ở bài viết này chúng ta sẽ tìm hiểu về UINavigationController và ứng dụng Multiple ViewController tức là có hơn 1 ViewController mà ở bài 3 chúng ta đã tìm hiểu.
Yêu cầu ở bài 4 này, chúng ta đã làm quen và có thể thao tác được với việc tạo các đối tượng UIView khác như UILabel, UIButton,... và add vào View của một ViewController, biết các liên kết Outlet, Action, những việc mà chúng ta đã tìm hiểu qua ở bài 3.

1. UINavigationController

 Trước hết, UINavigationController là một ViewController, nó cũng có nhiệm vụ điều khiển view để hiển thị các đối tượng view trực quan. Tuy nhiên, UINavigationController, nó quản lý nhiều hơn một ViewController.

Chúng ta hãy nhớ tới ứng dụng Setting quen thuộc của iPhone


Đây chính là kiến trúc Navigation, Setting ViewController -> General ViewController -> Auto-lock ViewController, tất cả các ViewController kể ra đó, đều được quản lý bởi một UINavigationController thông qua một Navigation Stack mà thực chất là 1 mảng ViewController.


Một UINavigationController sẽ có ít nhất là 1 ViewController trong mảng NSArray viewControllers, phần tử đầu tiên của mảng này chính là root viewcontroller của NavigationController.
Theo nguyên lý hoạt động của stack, UINavigationController sẽ hiển thị các đối tượng view "visual" của viewcontroller nào ở trên top, mà ta sẽ tạm gọi là topmost ViewController.

Giả sử,  UINavigationController đang có một root ViewController là Setting, nội dung trên view của Setting sẽ được hiển thị trên màn hình, chúng ta thực hiện push vào navigation stack một ViewController mới General, thì khi đó màn hình sẽ chỉ hiển thị nội dung của topmost ViewController General lên màn hình. Và cứ như thế đối với Auto-lock,...
Và khi đang hiển thị Auto-lock, chúng ta pop topmost ViewController này ra khỏi navigation stack thì General lúc này sẽ trở thành topmost, và hiển nhiên là view của nó sẽ được hiển thị.

2. Khái niệm về Push ViewController và Pop ViewController

Tiếp tục ứng dụng ở Bài 3, ta thêm 1 button Push VC và liên kết nó với thuộc tính _btnPush của ViewController

Với button Push VC, khi user touch vào nó ứng dụng sẽ hiển thị thêm một ViewController nữa thông qua Navigation stack.
Như vậy, trước tiên chúng ta cần 1 NavigationController, trong khi hiện tại ta chỉ mới có 1 ViewController. Chọn vào vùng view của ViewController, sau đó click Menu Editor của Xcode, Embed in \ Navigation Controller. Khi đó ViewController của chúng ta sẽ được add vào Navigation stack của một UINavigationController mới được tạo ra


Như vậy ViewController của chúng ta tạo trước đây đã là topmost và cũng là root ViewController của NavigationController này, giờ chúng ta tiếp tục tại mới một UIViewController nữa để thực hiện push vào navigation stack.
Để tạo mới 1 UIViewController, chúng ta hoàn toàn có thể kéo thả vào Main.storyboard, nhưng theo mình chúng ta nên tự tạo bằng tay thủ công và khoan hãy sử dụng Main.storyboard, vì như vậy chúng ta sẽ hiểu thêm được nhiều thứ hơn.

Chọn New File


Chúng ta lưu ý là phải tick chọn "Also create XIB file" để Xcode tạo cho chúng ta file giao diện NIB của ViewController sắp tạo

Cuối cùng,  file mới của lớp SecondViewController được tạo ra, click chọn SecondViewController.xib để chuẩn bị một ít về mặt giao diện cho ViewController mới này.


Ở đây ta có thể thấy ViewController này có Class là SecondViewController, lớp mà ta vừa mới tạo.

Quay trởi lại lớp ViewController ban đầu của chúng ta, sau khi liên kết các button với Outlets và liên kết Touch Up Inside Action với hàm buttonClick, chúng ta sẽ viết lại code để thực hiện show SecondViewController khi user touch vào button Push VC.

Đầu tiên, cần #import khai báo của lớp SecondViewController vào ViewController trong file .m

#import "ViewController.h"
#import "SecondViewController.h"
Ta viết lại code của hàm buttonClick, nơi mà ta xử lý sự kiện touch của các button

- (IBAction)buttonClick:(id)sender {
    if (sender == _btnTouchMe) {
        _myLabel.text = @"You have just touched on button!";
        _myLabel.textColor = [UIColor redColor];
    } else if (sender == _btnPush) {
        SecondViewController *secondVC = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
        [self.navigationController pushViewController:secondVC animated:YES];
    }
}
Vì có nhiều button cùng gọi đến hàm này khi có sự kiện touch up inside, do vậy chúng ta phân biệt chúng bằng tham số sender.
Ở đoạn else, chúng ta thấy có 2 dòng lệnh
SecondViewController *secondVC = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
Dòng lệnh này khởi tạo một SecondViewController từ file NIB có tên là "SecondViewController.xib"
Dòng lệnh thứ 2
[self.navigationController pushViewController:secondVC animated:YES];
Đây chính là dòng lệnh thực hiện push sendVC vào navigation stack của chúng ta, sau khi dòng lệnh này được gọi, sendVC sẽ trở thành topmost ViewController và hiển nhiên là nó sẽ được hiển thị, che đi ViewController cũ của chúng ta.

Run ứng dụng

Chúng ta sẽ thấy một điểm khác so với ứng dụng khi chạy lên ở Bài 3, một vùng mới ở bên trên xuất hiện
Đây được gọi là Navigation Bar, nó là một phần mới mà ta đã tìm hiểu ở cấu trúc bên trên của UINavigationController.
Tiếp tục touch vào Push VC button, thì khi đó SecondViewController sẽ được hiển thị lên (chạy từ phải sang trái)


Và ta để ý ở Navigation Bar, nó sẽ tự động xuất hiện 1 button Back, và khi touch vào button này, ứng dụng sẽ thực hiện pop secondVC ra khỏi navigation stack và hiển thị lại topmost lúc này là ViewController ban đầu.






Hoặc chúng ta cũng có thể tự viết đoạn pop này với một button khác của mình như sau


Button Pop Me! sẽ liên kết Touch Up Inside action với hàm popButtonClick, và chúng ta sẽ viết code cho hàm đó như sau trong lớp SecondViewController
- (IBAction)popButtonClick:(id)sender {
    [self.navigationController popViewControllerAnimated:YES];
}
Đây chính là dòng lệnh pop ViewController ra khỏi navigation stack, khi dòng lệnh này được gọi, SecondViewController cũng sẽ được pop ra như chúng ta Touch vào button Back ở trên.

 3. Present Modal ViewController

Tiếp theo khái niệm Push ở trên, đến đây chúng ta sẽ tìm hiểu thêm 1 khái niệm hiển thị ViewController khác, đó là Present Modal. Cách hiển thị này không như navigation stack ở trên, mà present modal, nó sẽ hiển thị phủ lên một ViewController mới từ một ViewController trước đó, và đặc biệt là tai mỗi thời điểm, chỉ có duy nhất một ViewController được hiển thị theo cách này. Điều này có nghĩa là nếu đã có 1 ViewController đã được show modal, bạn phải "tắt" nó để mới có thể show modal một ViewController khác.

Ta bổ sung thêm một button mới ở ViewController, Present VC

 Liên kết button này với Outlet _btnPresent, và action của nó với hàm buttonClick, ta viết code lại như sau
- (IBAction)buttonClick:(id)sender {
    if (sender == _btnTouchMe) {
        _myLabel.text = @"You have just touched on button!";
        _myLabel.textColor = [UIColor redColor];
    } else if (sender == _btnPush) {
        SecondViewController *secondVC = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
        [self.navigationController pushViewController:secondVC animated:YES];
    } else if (sender == _btnPresent) {
        SecondViewController *secondVC = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
        [self presentViewController:secondVC animated:YES completion:nil];
    }
}
Với dòng lệnh [self presentViewController:secondVC animated:YES completion:nil]; chúng ta thực hiển show Modal secondVC, ở đây ta chỉ cần gọi self tức là ViewController để show modal, chỉ cần ViewController tại thời điểm hiện tại, trạng thái của nó không phải là đang được show modal là ta có thể sử dụng hàm presentViewController:
Run ứng dụng và touch vào Present VC button

Chúng ta thấy là thanh Navigation Bar không còn nữa, vì đây không được NavigationController quản lý, và như vậy button Back cũng không có, và nếu Touch vào Pop Me! thì cũng không thể quay trở lại ViewController được.
Do đó, chúng ta sẽ bổ sung thêm một button mới Dismiss Me! để đóng SecondViewController trong trường hợp này


Button này liên kết action với hàm dismissButtonClick:
- (IBAction)dismissButtonClick:(id)sender {
    [self dismissViewControllerAnimated:YES completion:nil];
}
Run ứng dụng và ta sẽ có được kết quả.

Như vậy, qua bài 4 này chúng ta đã phần nào hiểu được các vấn đề

- NavigationController
- Push & Pop ViewController trong ứng dụng Multiple ViewController
- Present Modal & Dismiss ViewController

 Đến đây, chúng ta đã có thể tự mình xây dựng vài ứng dụng đơn giản khác rồi :)

No comments:

Post a Comment