Contents
Chào bạn đến với Fx Studio,
Chúng ta lại tiếp tục với series dài của Combine và phần Combine vs. UIKit. Phần này sẽ là việc xử lý logic của các thành phần trong ViewController. Áp dụng các Operators vào cho việc xử lý.
Bắt đầu thôi!
Chuẩn bị
- Xcode 11.0
- Swift 5.1
- iOS 13.0
Project code demo thì sử dụng lại project trong bài Combine vs. UIKit – Navigation. Chúng ta sẽ cài đặt thêm các function để xử lý các tính tăng mới.
1. Cập nhật dữ liệu
Chúng ta reset lại function gotoSettingsVC
từ HomeViewController nha.
@objc func gotoSettingsVC() { // vc let settingsVC = SettingsViewController() // push self.navigationController?.pushViewController(settingsVC, animated: true) }
Chúng ta sẽ cài đặt việc update title
của màn hình Settings, mỗi lần nhập dữ liệu. Trước tiên cần phải tạo nơi lưu trữ các subscriptions trong Settings trước.
- Thêm property này vào trong class
SettingsViewController
var subscriptions = Set<AnyCancellable>()
- Update lại function
viewDidLoad
- Tiến hành
subscribe
biếncount
- Sử dụng
map
để biến đổi dữ liệu nhận được từ Int thành String assig
dữ liệu nhận được trực tiếp lên thuộc tínhtitle
của ViewController
- Tiến hành
override func viewDidLoad() { super.viewDidLoad() // update UI // title $count .map { "\($0)" } .assign(to: \.title, on: self) .store(in: &subscriptions) // textfield countTexyField.text = "\(count)" }
Bạn build project và test lại. Title sẽ được liên tục update dựa theo giá trị của count
.
2. Xử lý dữ liệu nhận được
Tuy nhiên, chúng ta mới hoàn thành một nữa công việc. Còn việc lấy sự kiện từ UITextField để cật nhật lại dữ liệu cho biến count
Giải quyết bài toán này chúng ta sẽ tạo ra 1 Publisher cho UITextField để nhận các sự kiện phát ra từ nó. Tạo một file mới có tên là UITextField.Publisher.swift
import Foundation import UIKit import Combine extension UITextField { var publisher: AnyPublisher<String?, Never> { NotificationCenter.default .publisher(for: UITextField.textDidChangeNotification, object: self) .compactMap { $0.object as? UITextField? } .map { $0?.text } .eraseToAnyPublisher() } }
- Viết
extension
cho UITextField. - Khai báo thêm 1 compute property là
publisher
với Output là String và Failure là Never - Sử dụng
publisher
củaNotificationCenter.default
nhằm bắt sự kiện notificationtextDidChangeNotification
compactMap
để xác định đúng TextField sau khi ép kiểu, tránh các trường hợpnil
map
để biến dữ liệu nhận được thành StringeraseToAnyPublisher
để xoá dấu vết để lại
Bây giờ chúng ta sang phần chính. Thêm đoạn code sau vào function viewDidLoad
của SettingsViewController.
countTexyField.publisher .sink { value in guard let value = value, let temp = Int(value) else { return } self.count = temp }.store(in: &subscriptions)
Giải thích:
- Subscribe thuộc tính
publisher
của UITextField chúng ta vừa mới tạo ra. sink
để subscription nó- Thực hiện việc biến đổi String? thành Int và gán dữ liệu cho
count
- Lưu trữ lại subscription
Tiến hành build ứng dụng và test hoạt động đã đúng yêu cầu hay chưa.
Tiện tay update lại function gotoSettingsVC
ở HomeViewController. Khi các dữ liệu đã ổn định rồi và bạn không cần lo lắng gì tới biến count
.
@objc func gotoSettingsVC() { // vc let settingsVC = SettingsViewController() settingsVC.$count .sink { value in self.countPublisher.value = value }.store(in: &subscriptions) // push self.navigationController?.pushViewController(settingsVC, animated: true) }
3. Kiểm tra điều kiện
Chuyển sang phần mà chắc nhiều bạn sẽ quan tâm. Nếu trường hợp TextField của chúng ta với dữ liệu nhập chưa đúng yêu cầu. Thì đi kèm với đó là các UI Control liên quan sẽ không hoạt động. Nhằm loại bỏ đi 1 cơ số bugs có thể phát sinh do việc handle điều kiện trong ứng dụng.
Quay về ví dụ của chúng ta, nếu như dữ liệu của TextField chúng ta à số và không âm. Thì Button DONE
mới hoạt động được. Ta tiến hành cài đặt như sau:
Mở file SettingsViewController
và thêm thuộc tính sau:
var validated : AnyPublisher<Bool, Never> { return Publishers.Map(upstream: $count) { $0 >= 0 }.eraseToAnyPublisher() }
Nó giúp việc luôn kiểm tra trên stream count
. Và só sánh điều kiện >= 0
để quyết định trả về trạng thái kích hoạt cho Button DONE. Tiếp tục, thêm đoạn code sau vào function viewDidLoad
:
validated .assign(to: \.isEnabled, on: doneButton) .store(in: &subscriptions)
Vì vadidated
có Output là Bool
. Và nó luôn cập nhật theo giá trị của luồng count
. Vì vậy, chúng ta có thể assign
trực tiếp tới thuộc tính isEnabled
của doneButton
.
Build và test kiểm tra lại sự hoạt động của ViewController. Ngoài ra, để thêm hiệu ứng đẹp cho ứng dụng, thì bạn có thể tuỳ chỉnh giao diện của nút DONE theo isEnabled
. Và bạn có thể áp dụng thêm một số toán tử như combineLatest
để kiểm tra một lúc nhiều TextField.
OKAY. Tới đây, mình xin kết thúc bài viết này và bạn có thể download code demo tại đây.
Tạm kết
- Sử dụng các Operator để biến đổi dữ liệu phù hợp với các UI Control
- Extension để thêm các Publisher cho UI Control
- Kiểm tra kết hợp nhiều điều kiện theo Combine code
- Điều kiển các UI Control dựa theo các điều kiện
Cảm ơn bạn đã đọc bài viết này!
Written by chuotfx
Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!
Leave a Reply Cancel reply
Fan page
Tags
Recent Posts
- Charles Proxy – Phần 1 : Giới thiệu, cài đặt và cấu hình
- Complete Concurrency với Swift 6
- 300 Bài code thiếu nhi bằng Python – Ebook
- Builder Pattern trong 10 phút
- Observer Pattern trong 10 phút
- Memento Pattern trong 10 phút
- Strategy Pattern trong 10 phút
- Automatic Reference Counting (ARC) trong 10 phút
- Autoresizing Masks trong 10 phút
- Regular Expression (Regex) trong Swift
Archives
- September 2024 (1)
- July 2024 (1)
- June 2024 (1)
- May 2024 (4)
- April 2024 (2)
- March 2024 (5)
- January 2024 (4)
- February 2023 (1)
- January 2023 (2)
- November 2022 (2)
- October 2022 (1)
- September 2022 (5)
- August 2022 (6)
- July 2022 (7)
- June 2022 (8)
- May 2022 (5)
- April 2022 (1)
- March 2022 (3)
- February 2022 (5)
- January 2022 (4)
- December 2021 (6)
- November 2021 (8)
- October 2021 (8)
- September 2021 (8)
- August 2021 (8)
- July 2021 (9)
- June 2021 (8)
- May 2021 (7)
- April 2021 (11)
- March 2021 (12)
- February 2021 (3)
- January 2021 (3)
- December 2020 (3)
- November 2020 (9)
- October 2020 (7)
- September 2020 (17)
- August 2020 (1)
- July 2020 (3)
- June 2020 (1)
- May 2020 (2)
- April 2020 (3)
- March 2020 (20)
- February 2020 (5)
- January 2020 (2)
- December 2019 (12)
- November 2019 (12)
- October 2019 (19)
- September 2019 (17)
- August 2019 (10)