Skip to content
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Fx Studio
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Written by chuotfx on March 3, 2021

Extracting subview – SwiftUI Notes #5

SwiftUI

Contents

  • Chuẩn bị
  • 1. Tái sử dụng
  • 2. Extract Subview
  • 3. Hoàn thiện
  • Tạm kết

Chào bạn đến với Fx Studio. Bài viết này sẽ giúp bạn tạo một Subview với SwiftUI. Và cũng là bài viết tiếp nối  của series SwiftUI Notes.

Bạn có thể đọc lại bài viết trước tại đây, nhằm để bổ sung thêm kiến thức trước khi vào bài viết này.

    • Presenting an Alert

Và nếu như mọi việc đã ổn rồi, thì  …

Bắt đầu thôi!

Chuẩn bị

Về mặt cấu hình và phiên bản sử dụng thì như sau:

    • Xcode 12
    • Swift 5.3
    • SwiftUI 2.0
    • MacOS 10.15.x

Về demo bài viết, chúng ta sử dụng tiếp project ở bài trước. Bạn có thể checkout code tại đây:

    • Toàn bộ repo: https://github.com/fx-studio/swiftui-notes
    • Project bài trước: https://github.com/fx-studio/swiftui-notes/tree/main/004-PresentingAlert

Với bài viết này, bạn có thể sử dụng code của ContentView từ bài viết trước. Tạm thời, mình sẽ dụng đoạn code tạo giao diện từ bài viết trước như sau:

VStack(alignment: .center) {
                Button(action: {
                    if number < 20 {
                        number += 1
                    } else {
                        isShowAlert = true
                        message = "Maximum"
                    }
                    
                }) {
                    Image(systemName: "arrowtriangle.up.fill")
                        .resizable()
                        .frame(width: 50.0, height: 50.0)
                        .foregroundColor(.red)
                }
                .frame(width: 50.0, height: 50.0)
                Text("\(number)")
                    .font(.title)
                    .fontWeight(.bold)
                    .foregroundColor(Color.blue)
                    .multilineTextAlignment(.center)
                Button(action: {
                    if number > 0 {
                        number -= 1
                    } else {
                        isShowAlert = true
                        message = "Minimum"
                    }
                }) {
                    Image(systemName: "arrowtriangle.down.fill")
                        .resizable()
                        .frame(width: 50.0, height: 50.0)
                }
                .frame(width: 50.0, height: 50.0)
            }

Giao diện hiển thị sẽ trông như thế này:

Updating UI - SwiftUI

Tại bài viết trước, chúng ta có một vấn đề cần phải được giải quyết ngày. Đó là:

Số dòng code phát sinh trong SwiftUI quá dài.

1. Tái sử dụng

Mọi việc đơn giản khi mỗi View được tạo ra là duy nhất. Tuy nhiên với các ứng dụng, nhiều Control lặp lại trong chính một màn hình. Ta có bài toán mới với giao diện như sau:

Ta sẽ có tới 3 block giao diện nhỏ trong một màn hình. Và chúng giống nhau. Nếu như bạn suy nghĩ code của nó như thế nào. Thì bạn hãy nhìn đoạn code ở phần Chuẩn bị. Các bước như sau:

  1. Đưa block view cơ bản đầu tiền vào một HStack
  2. Sao chép lại block code view cơ bản đó
  3. Dán lại chúng nó thêm 2 lần nữa vào trong HStack

Cũng khá là EZ nhỉ!

Chúng ta sẽ nhân 3 lần số dòng code đó lên. Tuy nhiên, ở đời không ai làm như vậy cả. Vậy giải pháp kinh điểm muôn đời đó là:

Tạo một Subview, để tái sử dụng nhiều lần.

(Xin lỗi vì file code lúc này quá dài nên mình xin phép không bỏ vào đây!)

2. Extract Subview

Bạn sẽ bắt đầu lại bằng file code ở phần Chuẩn bị. Và ta có code của ContentView như sau:

struct ContentView: View {
    @State var number: Int = 10
    @State var isShowAlert: Bool = false
    @State var message: String = ""
    
    var body: some View {
        HStack {
            VStack(alignment: .center) {
                Button(action: {
                    if number < 20 {
                        number += 1
                    } else {
                        isShowAlert = true
                        message = "Maximum"
                    }
                    
                }) {
                    Image(systemName: "arrowtriangle.up.fill")
                        .resizable()
                        .frame(width: 50.0, height: 50.0)
                        .foregroundColor(.red)
                }
                .frame(width: 50.0, height: 50.0)
                Text("\(number)")
                    .font(.title)
                    .fontWeight(.bold)
                    .foregroundColor(Color.blue)
                    .multilineTextAlignment(.center)
                Button(action: {
                    if number > 0 {
                        number -= 1
                    } else {
                        isShowAlert = true
                        message = "Minimum"
                    }
                }) {
                    Image(systemName: "arrowtriangle.down.fill")
                        .resizable()
                        .frame(width: 50.0, height: 50.0)
                }
                .frame(width: 50.0, height: 50.0)
            }
        }
        .alert(isPresented: $isShowAlert, content: {
            Alert(title: Text("Error"),
                  message: Text(message))
        })
    }
}

Bạn hãy đưa con trỏ về VStack và nhấn tổ hợp phím Command + click. Sau đó chọn như hình dưới đây.

Extract Subview

Sau đó, bạn bấm Enter và đổi tên đoạn code của mình vừa mới tách ra.

Rất nhanh chóng, với chức này hữu ích này của Xcode thì bạn đã nhanh chóng có một Custom View theo đúng cú pháp của SwiftUI rồi.

3. Hoàn thiện

Tuy nhiên, vì View và các Subview sẽ bị ràng buộc với nhau bởi các thuộc tính. Nên MyView mới sẽ bị lỗi. Công việc của bạn là fix chúng nó.

Đầu tiên, chuyển các thuộc tính ở ContentView về MyView.

@State var number: Int
@Binding var isShowAlert: Bool
@Binding var message: String

Có một chút khác biệt ở đây. Đó là @Binding. Vì chúng ta thay đổi giá trị tại Subview và giá trị này sẽ ảnh hưởng tới thuộc tính ở ContentView.

@Binding là một Property Wrappers trong SwiftUI, nó cho phép chúng ta khai báo một giá trị thực chất được khai báo ở một nơi khác, nhưng được chia sẻ lẫn nhau.

Và ta sẽ khởi tạo MyView trong ContentView như sau:

HStack {
            MyView(number: 10, isShowAlert: $isShowAlert, message: $message)
        }

Chú ý, bạn sẽ thấy các thuộc tính truyền vào hàm khởi tạo của MyView và bạn phải thêm từ khoá $ vào. Nôm na là báo cho View con kia biết địa chỉ gốc của thuộc tính mà dữ liệu mà nó sẽ gởi về mà thôi.

Okay! Ta sẽ có code hoàn thiện của MyView như sau:

struct MyView: View {
    
    @State var number: Int
    @Binding var isShowAlert: Bool
    @Binding var message: String
    
    var body: some View {
        VStack(alignment: .center) {
            Button(action: {
                if number < 20 {
                    number += 1
                } else {
                    isShowAlert = true
                    message = "Maximum"
                }
                
            }) {
                Image(systemName: "arrowtriangle.up.fill")
                    .resizable()
                    .frame(width: 50.0, height: 50.0)
                    .foregroundColor(.red)
            }
            .frame(width: 50.0, height: 50.0)
            Text("\(number)")
                .font(.title)
                .fontWeight(.bold)
                .foregroundColor(Color.blue)
                .multilineTextAlignment(.center)
            Button(action: {
                if number > 0 {
                    number -= 1
                } else {
                    isShowAlert = true
                    message = "Minimum"
                }
            }) {
                Image(systemName: "arrowtriangle.down.fill")
                    .resizable()
                    .frame(width: 50.0, height: 50.0)
            }
            .frame(width: 50.0, height: 50.0)
        }
    }
}

Và code của ContentView như sau:

struct ContentView: View {
    
    @State var isShowAlert: Bool = false
    @State var message: String = ""
    
    var body: some View {
        HStack {
            MyView(number: 10, isShowAlert: $isShowAlert, message: $message)
            MyView(number: 10, isShowAlert: $isShowAlert, message: $message)
            MyView(number: 10, isShowAlert: $isShowAlert, message: $message)
        }
        .alert(isPresented: $isShowAlert, content: {
            Alert(title: Text("Error"),
                  message: Text(message))
        })
    }
}

Bạn hãy bấm Live Preview và tự cảm nhận kết quả nha! Giờ thì code của ContentView đã thu gọn đi nhiều lần rồi.

 

Tạm kết

  • Cách tách một đoạn code thành một Subview
  • Binding dữ liệu từ View con về View cha
  • @Binding vs. @State

 

Okay! Tới đây, mình xin kết thúc bài viết này. Và nếu có gì thắc mắc hay góp ý cho mình thì bạn có thể để lại bình luận hoặc gởi email theo trang Contact.

  • Bạn có thể checkout code tại đây.
  • Bài viết tiếp theo tại đây.

Cảm ơn bạn đã đọc bài viết này!

FacebookTweetPinYummlyLinkedInPrintEmailShares9

Related Posts:

  • feature_bg_swiftui_7
    Giới thiệu MapKit trên SwiftUI
Tags: SwiftUI, SwiftUI Notes
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

Your email address will not be published. Required fields are marked *

Donate – Buy me a coffee!

Fan page

Fx Studio

Tags

Actor Advanced Swift AI api AppDistribution autolayout basic ios tutorial blog ci/cd closure collectionview combine concurrency crashlytics dart dart basic dart tour Declarative delegate deploy design pattern fabric fastlane firebase flavor flutter GCD gradients iOS MVVM optional Prompt engineering protocol Python rxswift safearea Swift Swift 5.5 SwiftData SwiftUI SwiftUI Notes tableview testing TravisCI unittest

Recent Posts

  • Vibe Coding là gì?
  • Cách Đọc Sách Lập Trình Nhanh và Hiệu Quả Bằng GEN AI
  • Nỗ Lực – Hành Trình Kiến Tạo Ý Nghĩa Cuộc Sống
  • Ai Sẽ Là Người Fix Bug Khi AI Thống Trị Lập Trình?
  • Thời Đại Của “Dev Tay To” Đã Qua Chưa?
  • Prompt Engineering – Con Đường Để Trở Thành Một Nghề Nghiệp
  • Vấn đề Ảo Giác (hallucination) khi tương tác với Gen AI và cách khắc phục nó qua Prompt
  • Điều Gì Xảy Ra Nếu… Những Người Dệt Mã Trở Thành Những Người Bảo Vệ Cuối Cùng Của Sự Sáng Tạo?
  • Khi Cô Đơn Gặp Python
  • Học vì tồn tại

You may also like:

  • Giới thiệu MapKit trên SwiftUI
    feature_bg_swiftui_7

Archives

  • April 2025 (1)
  • March 2025 (8)
  • January 2025 (7)
  • December 2024 (4)
  • 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)

About me

Education, Mini Game, Digital Art & Life of coders
Contacts:
contacts@fxstudio.dev

Fx Studio

  • Home
  • About me
  • Contact us
  • Mail
  • Privacy Policy
  • Donate
  • Sitemap

Categories

  • Art (1)
  • Blog (43)
  • Code (10)
  • Combine (22)
  • Flutter & Dart (24)
  • iOS & Swift (101)
  • No Category (1)
  • RxSwift (37)
  • SwiftUI (80)
  • Tutorials (86)

Newsletter

Stay up to date with our latest news and posts.
Loading

    Copyright © 2025 Fx Studio - All rights reserved.