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 May 26, 2021

Basic TextField – SwiftUI Notes #27

SwiftUI

Contents

  • Chuẩn bị
  • TextField
  • 1. Display TextField
  • 2. Read TextField
  • 3. Write TextField
  • 4. State & Action
  • 5. Formatted text
    • 5.1. Int
    • 5.2. Date
    • 5.3 Demo
    • 5.4 Lưu ý
  • 6. Styling
  • Tạm kết

Chào bạn đến với Fx Studio. Chúng ta sẽ tiếp tục series SwiftUI Notes với bài viết về đối tượng TextField. Đây là một SwiftUI View và có rất nhiều điểm thú vị từ nó mà bạn cần khám phá.

Nếu mọi việc ổn rồi, thì …

Bắt đầu thôi!

Chuẩn bị

Về mặt tool và version, các bạn tham khảo như sau:

    • SwiftUI 2.0
    • Xcode 12

Về mặt kiến thức, bạn cần biết trước các kiến thức cơ bản với SwiftUI & SwiftUI App. Tham khảo các bài viết sau, nếu bạn chưa đọc qua SwiftUI:

    • Làm quen với SwiftUI
    • Cơ bản về ứng dụng SwiftUI App

Về mặt demo, hầu như là demo đơn giản, vì tập trung vào từng View riêng lẻ. Do đó, bạn cũng không cần phải quá lo lắng và việc tạo mới project cũng không ảnh hưởng gì nhiều. Ngoài ra, bạn có thể checkout mã nguồn của các bài viết tại đây.

(Mặc định, mình xem như bạn đã biết về cách tạo project với SwiftUI & SwiftUI App rồi.)

TextField

Một đối tượng SwiftUI View khá là bá đạo nhất. Nó là trong những Control Input mà được sử dụng nhiều nhất. Phiên bản tiền nhiệm của nó trong iOS & MacOS là UITextField & NSTextField. Ngoài ra, bạn có thể thấy được mức độ phổ biến của nó thông qua hằng hà sa số các thư viện custom lại.

Đặc trưng của TextField là:

  • Là giao diện cho việc chỉnh sửa văn bản
  • Đi kèm với nó là một bản phím ảo
  • Sẽ có nhiều biến thể để phù hợp với nhiều định dạng của văn bản (số, email, ngày tháng ….)

Chúng ta tìm hiểu sơ như vậy thôi, hầu như ai cũng biết nó rồi. Và bây giờ, ta sẽ tập trung phần chính nào.

1. Display TextField

Nói là đơn giản nhưng cũng không phải là đơn giản quá mức cho việc hiển thị một TextField à. Vì SwiftUI, chúng ta đang tương tác với kiểu lập trình Declarative.

Bạn cần phải khai báo đồng thời phần data & ui . Và khi người dùng tương tác với View (TextField) thì dữ liệu sẽ tự động cập nhật. Hoặc khi bạn thay đổi dữ liệu thì View cũng sẽ thay đổi thay.

Bắt đầu, bạn hay tạo một file SwiftUI View để dùng làm demo cho bài viết này. Và thêm một thuộc tính đơn giản đầu tiên cho View đó. Ví dụ như sau:

struct TextFieldDemoView: View {
    
    @State var name: String = ""
    
    var body: some View {
        Text("Hello, World!")
    }
}

Thuộc tính ta thêm vào có tên là name và là một Wrapper Property @State. Nó cho phép bạn có thể thay đổi giá trị từ các View ràng buộc với nó. Tiếp tục, ta thêm đối tượng TextField vào body , sẽ thay thế cho Text kia. Code ví dụ như sau:

var body: some View {
    TextField("Enter username...", text: $name)
}

Trong đó:

  • tham số title với giá trị được truyền vào là "Enter username..." . Nó như là một placeholder của TextField
  • text cần đối tượng để binding dữ liệu. Ta sử dụng thuộc tính name vừa tạo ở trên.
  • Keyword $ trước name để giúp cho View có thể thay đổi giá trị của thuộc tính/biến truyền vào

Rất là nhanh, bạn bấm Resume và xem kết quả nha.

TextField

2. Read TextField

Xong bước setup và sang bước sử dụng nó thôi. Làm thế nào để đọc được dữ liệu từ TextField. Câu trả lời cực kì ngắn gọn là …

Không cần làm gì cả.

Vì theo nguyên tắt lập trình Declarative, mọi thứ sau khi đã khai báo với nhau. Mọi dữ liệu & giao diện sẽ được đồng bộ với nhau một cách tự động.

Tuy nhiên, để cho bạn an tâm thì bạn hãy xem tiếp đoạn code ví dụ sau:

var body: some View {
    VStack {
        TextField("Username", text: $name)
        Spacer()
        Text(name == "" ? "Please, input your name" : "Hello, \(name)!")
        Spacer()
    }
    .frame(height: 100.0)
    .padding()
}

Trong đó:

  • Tuỳ chỉnh giao diện với VStack và thêm một Text nữa vào
  • Nhiệm vụ của Text là đọc name và hiển thị sao cho phù hợp với giá trị của name

Okay, bạn bấm Live Preview để kiểm tra xem người dùng nhập giá trị vào TextField, thì có kéo theo giá trị ở Text thay đổi hay không.

3. Write TextField

Để thay đổi hiển thị của TextField thì cũng khá đơn giản và nó vẫn chịu tác động của Declarative. Bạn chỉ cần thay đổi giá trị của thuộc tính được binding với TextField là oki.

Chúng ta xem code ví dụ nữa nào:

var body: some View {
    VStack {
        TextField("Username", text: $name)
        Spacer()
        Text(name == "" ? "Please, input your name" : "Hello, \(name)!")
        Spacer()
        Button(action: {
            name = ""
        }, label: {
            Text("Clear")
        })
    }
    .frame(height: 100.0)
    .padding()
}

Trong đó:

  • Thêm một Button để lấy sự kiện action của nó, khi người dùng nhấn vào Button
  • Tại đó, chúng ta sẽ thay đổi giá trị của thuộc tính name. Ví dụ là gán lại với ""

Bạn tiếp tục Live Preview và kiểm tra việc nhấn vào Button sau khi đã nhập vào TextField. Thì kết quả ở TextField có thay đổi text hay không.

4. State & Action

Nếu bạn là một iOS Dev, khi dùng UITextField, thì kèm theo đó là việc sử dụng tới các Delegate Protocol của nó. Chúng ta sẽ cần bắt được các sự kiện hay các trạng thái mà TextField phát ra. Để xử lý trong nhiều trường hợp. Ví dụ như, validate field ngay khi kết thúc nhập …

Nhưng, TextField trong SwiftUI sẽ không có Delegate Protocol.

Nếu bạn muốn lấy các sự kiện này. Bạn vẫn có thể bắt được chúng & SwiftUI cung cấp cho bạn các hàm khởi tạo tương ứng. Vẫn là code ví dụ trên. Bạn thay đổi lại TextField như sau.

TextField("Username", text: $name) { isBegin in
    if isBegin {
        print("Begins editing")
    } else {
        print("Finishes editing")
    }
} onCommit: {
    print("commit")
}

Trong đó:

  • tham số đầu là onEditingChanged . Nó tương tự như textFieldDidBeginEditing trong Delegate của UITextField
    • Với giá trị trả về trong closure cho tham số isBegin . Nếu
    • true là bắt đầu thay đổi
    • flase là kết thúc việc thay đổi
  • Tham số tiếp theo được cung cấp là onCommit. Nó tương tự như textFieldShouldReturn
    • Khi người dùng bấm Return Key của bàn phím ảo, thì nó sẽ được gọi.

Bạn thử lại với build project lên Simulator và xem kết quả nha ở Console nha.

Begins editing
commit
Finishes editing

Nếu bạn tìm kiếm TextField với responder, cho việc focus hay dismiss một TextField tương tự như UITextField. Mình có tìm kiếm với SwiftUI thì chưa có hoặc chưa hỗ trợ. Bạn muốn sử dụng thì custom lại hoặc tải thêm Package trên mạng đã làm sẵn rồi.

5. Formatted text

SwiftUI cũng hỗ trợ TextField thêm nhiều sức mạnh, đó là việc chuyển đổi qua lại giữa vài kiểu dữ liệu cơ bản. Ví dụ như sau:

  • Bạn có TextField để nhập số tuổi. Kiểu dữ liệu mong muốn của bạn sẽ là Int
  • Bạn có TextField để nhập ngày tháng. Kiểu dữ liệu mong muốn của bạn sẽ là Date

Với cách truyền thống, chúng ta sẽ có một hàm nào đó. Và ngồi convert từ String của tất cả các TextField sang từng kiểu dữ liệu ứng với từng biến muốn sử dụng.

Chúng ta sẽ làm cách nhanh hơn, đó là truyền thẳng Formatter cho TextField.

Trước tiên, mình sẽ trình bày từng bước ví dụ để bạn thấy nó hoạt động như thế nào. Ta bắt đầu với kiểu Int

5.1. Int

  • Ví dụ với kiểu Int, chúng ta cần một đối tượng formatter của nó trước.
static var numberFormater: NumberFormatter {
    let nf = NumberFormatter()
    nf.numberStyle = .decimal
    return nf
}
  • Tạo một thuộc tính @State tương ứng với kiểu Int (ý nghĩa thì cũng giống như biến name ở trên)
@State var age: Int = 0
  • Cấu hình vào TextField với tham số formater cho hàm khởi tạo của nó.
TextField("Age",
          value: $age,
          formatter: TextFieldDemoView.numberFormater)

Như vậy, bạn đã hoàn thành việc tạo một TextField với formatter để liên kết với một thuộc tính khác String

5.2. Date

Một kiểu dữ liệu mà chúng ta dùng thường xuyên nữa đó là Date. Hầu như xuất hiện khá nhiều trong các form đăng ký hay tạo mới một cái gì đó.

Giải quyết việc này thì cũng tương tự Int ở trên. Đầu tiên, bạn cần phải có 1 đối tượng Formatter riêng cho nó.

static var dateformater: DateFormatter {
    let df = DateFormatter()
    df.dateStyle = .short
    return df
}

Tạo thêm một thuộc tính mà bạn sẽ dùng làm binding với đối tượng TextField. À, nó kiểu Date & @State nha.

@State var birthday: Date = Date()

Cuối cùng, bạn hoàn thành TextField với tham số formatter mới nào.

TextField(
    "Birthday",
    value: $birthday,
    formatter: TextFieldDemoView.dateformater)

Với 2 cách đơn giản này, bạn giảm tải đi rất nhiều việc convert kiểu dữ liệu String sang các kiểu khác cho TextField.

5.3 Demo

Đã nắm sơ qua việc sử dụng Formatter cho TextField rồi. Bây giời, chúng ta áp dụng cả 2 vào demo. Mình sẽ đưa ví dụ hoàn thành với 2 formatter ở trên nha.

struct TextFieldDemoView: View {
    
    @State var name: String = ""
    @State var birthday: Date = Date()
    @State var age: Int = 0
    
    static var dateformater: DateFormatter {
        let df = DateFormatter()
        df.dateStyle = .short
        return df
    }
    
    static var numberFormater: NumberFormatter {
        let nf = NumberFormatter()
        nf.numberStyle = .decimal
        return nf
    }
    
    
    var body: some View {
        VStack {
            // Username
            VStack {
                TextField("Username", text: $name) { isBegin in
                    if isBegin {
                        print("Begins editing")
                    } else {
                        print("Finishes editing")
                    }
                } onCommit: {
                    print("commit")
                }
                
                Spacer()
                Text(name == "" ? "Please, input your name" : "Hello, \(name)!")
                Spacer()
                Divider()
            }
            
            VStack {
                TextField("Age",
                          value: $age,
                          formatter: TextFieldDemoView.numberFormater)
                
                Spacer()
                Text(age == 0 ? "Please, input your age" : "\(age)")
                Spacer()
                Divider()
            }
            
            // Birthday
            VStack {
                TextField(
                    "Birthday",
                    value: $birthday,
                    formatter: TextFieldDemoView.dateformater)
                Spacer()
                Text(TextFieldDemoView.dateformater.string(from: birthday))
                Spacer()
                Divider()
            }
            
            // Button
            VStack {
                Button(action: {
                    name = ""
                    age = 0
                    birthday = Date()
                }, label: {
                    Text("Clear")
                        .foregroundColor(Color.white)
                })
                .padding()
                .background(Color.blue)
                
            }
        }
        .frame(height: 300.0)
        .padding()
    }
}

Bạn có thể tham khảo để có demo riêng của bạn. Còn bây giờ thì bấm Live Preview và test thử.

TextField

5.4 Lưu ý

  • Bạn bấm Return Key thì mới áp dụng formartter
  • Nếu như dữ liệu nhập vào sai (sai ngày tháng) thì giá trị của thuộc tính/biến & TextField sẽ không thay đổi. Và giữ nguyên giá trị trước lúc nhập.
  • Nếu như bạn nhập sai format thì cũng như trên. TextField sẽ lấy giá trị trước đó từ thuộc tính/biến để hiển thị.

6. Styling

Nếu để ý bạn thấy TextField chúng ta qua xấu. Tuy nhiên, là do nó chưa được thêm style. SwiftUI cũng hỗ trợ bạn một modifier để thêm style cho TextField, đó là .textFieldStyle

Bạn thử một số loại style cơ bản như sau nha.

  • DefaultTextFieldStyle
    • Cơ bản đầu tiên, nó sẽ dựa vào mỗi nền tảng và version OS mà hiển thị
  • PlainTextFieldStyle
    • Chỉ có text mà thôi
  • RoundedBorderTextFieldStyle
    • Hay sử dụng nhất và được bo tròn 4 góc
  • SquareBorderTextFieldStyle
    • Cho MacOS và mình cũng không rành lắm

Code ví dụ như sau:

TextField("Username", text: $name) { isBegin in
                    if isBegin {
                        print("Begins editing")
                    } else {
                        print("Finishes editing")
                    }
                } onCommit: {
                    print("commit")
                }
                .textFieldStyle(RoundedBorderTextFieldStyle())

Xem kết quả nhoé!

TextField

Với dân chơi hệ iOS thì mình khuyên bạn chỉ cần sử dụng mỗi RoundedBorderTextFieldStyle là đẹp ngất ngây con gà tây rồi.

 

Tạm kết

  • Sử dụng được TextField ở mức giao diện cơ bản
  • Các trạng thái và sự kiện
  • Binding dữ liệu với các thuộc tính và giao diện
  • Chuyển đổi qua lại giữa các kiểu dữ liệu

 

Okay! Tới đây, mình xin kết thúc bài viết về đối tượng TextField trong SwiftUI. 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!

FacebookTweetPinYummlyLinkedInPrintEmailShares2

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

  • Role-playing vs. Persona-based Prompting
  • [Swift 6.2] Raw Identifiers – Đặt tên hàm có dấu cách, tại sao không?
  • 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?

You may also like:

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

Archives

  • May 2025 (2)
  • 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 (44)
  • Code (11)
  • Combine (22)
  • Flutter & Dart (24)
  • iOS & Swift (102)
  • No Category (1)
  • RxSwift (37)
  • SwiftUI (80)
  • Tutorials (87)

Newsletter

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

    Copyright © 2025 Fx Studio - All rights reserved.