Cách tạo style cho các thành phần trong SwiftUI ( custom modifier) & cách tạo thêm Grid Stack cho SwiftUI

Tui muốn tạo style riêng cho Buttom hoặc Text hoặc tạo 1 BG có gắn Watermark của riêng tui thì làm ntn?

Tạo Style đơn giản cho Text để sử dụng lại cho tiện ?

Step 1: Tạo Style Title sử dụng ViewModifier như sau:

struct Title: ViewModifier {
    func body(content: Content) -> some View {
        content
            .font(.largeTitle)
            .foregroundColor(.white)
            .padding()
            .background(Color.blue)
            .clipShape(RoundedRectangle(cornerRadius: 10))
    }
}

Step2: Trong body ở View cần hiển thị, add thêm modifier vào phần Text ("Hello world") như sau:

Text("Hello World")
    .modifier(Title())

Kết quả:

Tạo Style Pro giống như kiểu của SwiftUI tạo ra : .padding() hoặc .background(Color.blue) ntn?

Wrap .modifier(Title()) phía trên thành style giống như của SwiftUI : .titleStyle() như đoạn code dưới:

extension View {
    func titleStyle() -> some View {
        self.modifier(Title())
    }
}

Và sử dụng :

Text("Hello World")
    .titleStyle()

Tạo Watermark Style như nào ?

Tương tự như cách tạo style cho Text phía trên, các bước làm như sau:

Step 1: Tạo style ViewModifier & Wrap nó với extension mới của View:

struct Watermark: ViewModifier {
    var text: String

    func body(content: Content) -> some View {
        ZStack(alignment: .bottomTrailing) {
            content
            Text(text)
                .font(.caption)
                .foregroundColor(.white)
                .padding(5)
                .background(Color.black)
        }
    }
}

extension View {
    func watermarked(with text: String) -> some View {
        self.modifier(Watermark(text: text))
    }
}

Step 2: Sử dụng trong View:

Color.blue
    .frame(width: 300, height: 200)
    .watermarked(with: "quilv.com")

Kết quả:

Source code demo: https://github.com/QuiLeVan/SwiftUICutomModifierAndStack.git

Cách để thêm Grid Stack cho SwiftUI, mặc định chỉ có HStack & VStack thì làm ntn?

Step 1

Tạo ra 1 type mới GridStack sẽ chứa rất nhiều view (View protocol) dưới dạng lưới như sau:

struct GridStack<Content: View>: View {
    let rows: Int
    let columns: Int
    let content: (Int, Int) -> Content

    var body: some View {
        // more to come
    }
}

Dòng đầu tiên: struct GridStack<Content: View>: View sử dụng tính năng nâng cao hơn của Swift gọi là generics Type, truyền vào bất kỳ loại nào nhưng loại đó phải theo View protocol.

Hoàn thiện code trong body:

var body: some View {
    VStack {
        ForEach(0..<rows, id: \.self) { row in
            HStack {
                ForEach(0..<self.columns, id: \.self) { column in
                    self.content(row, column)
                }
            }
        }
    }
}

Chú ý ở đây: Khi duyệt range thì Swift chỉ có thể truy cập trực tiếp đến phạm vi của range nếu như biết chắc là range đó ko thay đổi. Vd ở đây: sử dụng : ForEach voiws 0..rows và 0..columns, những giá trị này có thể thay đổi, vd ta muốn thêm dòng / cột cho nó. Trong trường hợp này, để có thể sử dụng được thì phải add thêm param thứ 2 : id: \.self để báo cho SwiftUI biết làm thế nào để xác định từng view trong vòng lặp.

Step2:

Sử dụng trong View:

struct ContentView: View {
    var body: some View {
        GridStack(rows: 4, columns: 4) { row, col in
            Text("R\(row) C\(col)")
        }
    }
}

Trong GridStack có thể chấp nhận bất kỳ loại content nào miễn sao phù hợp với View Protocol nên có thể kết hợp nhiều thành phần trong view như sau:

GridStack(rows: 4, columns: 4) { row, col in
    HStack {
        Image(systemName: "\(row * 4 + col).circle")
        Text("R\(row) C\(col)")
    }
}

Tham khảo từ: https://www.hackingwithswift.com/books/ios-swiftui/custom-containers


All Rights Reserved