SwiftUI - 文本(Text)

645 阅读3分钟

多语言

截屏2022-12-06 下午12.21.59.png

1.先创建多语言文件

Localizable.strings 默认寻找的 tableName, 否则, 使用的时候, 需要添加table name, 如果在 framework 等不同的bundle中, 还要添加 bundle

// en
"hungry_title" = "stay foolish, stay hungry";
// zh_sm
"hungry_title" = "求知若饥, 虚心若愚";

2.枚举类型, 添加Key

enum LocalizedKey {
    static let kHungryTitle: LocalizedStringKey = "hungry_title"
}

3.在 View 中实现

struct ContentView: View {
    var body: some View {
        VStack {
            Text(LocalizedKey.kHungryTitle)
        }
    }
}

富文本

Text 实现了操作符重载

截屏2022-12-06 下午12.20.55.png

1. + 号来拼接不同样式的文本

struct RichTextView: View {
    private let text: Text =
    Text("Stay ")
        .foregroundColor(.blue)
        .font(.title)
        .italic()
    + Text("Hungry, ")
        .font(.headline)
    + Text("Stay ")
        .foregroundColor(.red)
        .font(.title)
    + Text("Foolish!")
        .font(.headline)
        .underline()
        
    var body: some View {
        text
    }
}

2. 用字符串插值实现图文混排

截屏2022-12-06 下午12.19.41.png

struct RichTextView: View {
    private let text: Text =
    Text("Hello ")
        .foregroundColor(.blue)
    + Text("Swift \(Image(systemName: "swift")) ")
        .foregroundColor(.orange)

    var body: some View {
        text
    }
}

Markdown 支持

截屏2022-12-06 下午12.28.04.png

struct MarkDownView: View {
    var body: some View {
        Text("**This** ~~is~~ an *example* of **Markdown** `Text`, [More info](https://developer.apple.com/documentation/swiftui/text)")
    }
}

渐变文字

1. foregroundStyle

截屏2022-12-06 下午2.33.40.png

struct GradientTextView: View {
    var body: some View {
        Text("Stay Hungry, Stay Foolish!")
            .font(.title2)
            .bold()
            .textSelection(.enabled) //长按可复制
            .foregroundStyle(
                LinearGradient(
                    colors: [
                        .orange,
                        .yellow,
                        .blue,
                        .purple
                    ],
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing
                )
            )
    }
}

2.给定样式自动减弱

截屏2022-12-06 下午2.44.21.png

struct AutoHierarchicalStyleView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Label("Apple", systemImage: "applelogo")
                .foregroundStyle(.primary)

            Label("iPhone", systemImage: "iphone")
                .foregroundStyle(.secondary)

            Label("iMac", systemImage: "desktopcomputer")
                .foregroundStyle(.tertiary)

            Label("AirPods", systemImage: "airpods")
                .foregroundStyle(.quaternary)
        }
    }
}

AutoHierarchicalStyleView()
    .foregroundStyle(.blue)

iOS 15 新增协议 HierarchicalShapleStyle 有四个等级的属性可以选择 .primary .secondary .tertiary .quaternary

我们可以提供多个样式,查看不同效果

截屏2022-12-06 下午2.49.22.png

AutoHierarchicalStyleView()
                .foregroundStyle(.blue, .purple, .orange)

日期

Text 可以直接展示日期, 有如下初始化方法

public init(_ date: Date, style: Text.DateStyle)
public init(_ dates: ClosedRange<Date>)
public init(_ interval: DateInterval)

DateStyle 可以指定的样式

extension Text {
    public struct DateStyle {
        ///     11:23PM
        public static let time: Text.DateStyle
        ///     June 3, 2019
        public static let date: Text.DateStyle
        ///     2 hours, 23 minutes
        ///     1 year, 1 month
        public static let relative: Text.DateStyle
        ///     Text(event.startDate, style: .offset)
        ///     +2 hours
        ///     -3 months
        public static let offset: Text.DateStyle
        ///    2:32
        ///    36:59:01
        public static let timer: Text.DateStyle
    }
}

截屏2022-12-06 下午3.32.02.png

example 代码如下:

struct DateView: View {
    var now: Date {
        Date()
    }
    
    var future: Date {
        now.addingTimeInterval(3600)
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            row(style: ".date") {
                Text(now, style: .date)
            }
            row(style: ".offset") {
                Text(future, style: .offset)
            }
            row(style: ".relative") {
                Text(future, style: .relative)
            }
            row(style: ".time") {
                Text(future, style: .time)
            }
            row(style: ".timer") {
                Text(future, style: .timer)
            }
            row(style: "Range") {
                Text(now ... future)
            }
            row(style: "Interval") {
                Text(DateInterval(start: now, end: future))
            }
        }
    }

    func row<Content: View>(style: String, @ViewBuilder content: () -> Content) -> some View {
        VStack {
            HStack {
                content()
                Spacer()
                Text(style)
                    .foregroundColor(.secondary)
            }
            Divider()
        }
    }
}

@ViewBuilder 可以修饰参数, 并从中构建视图

Label

可以快速生成图片和文字的组合, 默认左图右文

1. 改变位置 (左文右图)

Label {
    Image(systemName: "swift")
} icon: {
    Text("Swift")
}

2.LabelStyle

Label("Swift", systemImage: "swift")
//                .labelStyle(.titleOnly)
//                .labelStyle(.iconOnly)
                  .labelStyle(.titleAndIcon)

3.自定义样式

截屏2022-12-06 下午5.11.24.png

struct ShadowLabelStyle: LabelStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        Label(configuration)
            .shadow(color: .black.opacity(0.5), radius: 5, x: 0, y: 5)
    }
}

extension LabelStyle where Self == ShadowLabelStyle {
    static var shadowLabel: Self {
        Self()
    }
}

上面的样式有一定的局限性,如果我们需要一个垂直布局或是左右对齐的样式呢?]

截屏2022-12-06 下午5.44.30.png

struct VerticalLabel: LabelStyle {
    func makeBody(configuration: Configuration) -> some View {
        VStack(alignment: .center, spacing: 10) {
            configuration.icon
            configuration.title
        }
    }
}

extension LabelStyle where Self == VerticalLabel {
    static var verticalLabel: Self {
        Self()
    }
}

struct LeftTitleRightIconLabel: LabelStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack(alignment: .center, spacing: 10) {
            configuration.title
            Spacer()
            configuration.icon
        }
    }
}

extension LabelStyle where Self == LeftTitleRightIconLabel {
    static var leftTitleRightIconLabel: Self {
        Self()
    }
}

VStack {
    Label("Apple", systemImage: "applelogo")
        .labelStyle(.verticalLabel)

    Label("Apple", systemImage: "applelogo")
        .labelStyle(.leftTitleRightIconLabel)
}

TextField

TextField 有如下的三种构建方式:

截屏2022-12-06 下午6.18.18.png

1. Return 键的按下动作

@State var s1: String = ""
@State var s2: String = ""
@State var s3: String = ""
@State var pwd: String = ""

GroupBox(label: Text(s1)) {
    TextField("TextField", text: $s1)
        .onSubmit {
            print("click Return")
        }
}

2.监听编辑状态

GroupBox(label: Text(s2)) {
    TextField("Observe textField", text: $s2) { isEdting in
        print(isEdting)
    } onCommit: {
        print("Click return")
    }
}

3. Formatter

GroupBox(label: Text(s3)) {
    TextField("Formatter TextField", value: $s3, formatter: NumberFormatter()) { isEditing in
        print(isEditing)
    } onCommit: {
        print("click Return")
    }
    .textFieldStyle(.roundedBorder)
    .keyboardType(.numbersAndPunctuation)
}

4.密码输入框

GroupBox(label: Text("密码输入: \(pwd)")) {
    SecureField("Password", text: $pwd)
}

TextEditor

@State var text1: String = ""

TextEditor(text: $s1)
    .frame(height: 150)
    .lineSpacing(10)
    .multilineTextAlignment(.center)
    .overlay(RoundedRectangle(cornerRadius: 5).stroke(.blue, lineWidth: 1))
    .padding()

创建带有placeHolder 的editor

@State var text2: String = ""

ZStack(alignment: .topLeading) {
    TextEditor(text: $text2)
        .frame(height: 150)
        .border(.blue, width: 1)
        .padding()

    if text2.isEmpty {
        Text("Type someting")                          .foregroundColor(Color(UIColor.placeholderText))
            .padding(20)
    }
}