Creating custom modifiers for Swift UI views

2020-07-17 02:36发布

问题:

Was wondering how it's possible to create modifiers for Swift UI views?

For example, let's say I have some view defined as so:

struct LabelView: View {
   let font1: Font = .header
   let font2: Font = .body

   var body: Some View {
     // two views, where one uses font1 and other uses font2
   }
}

How would it be possible to create a modifier that allows something like:

LabelView()
  .font1(.callout)
  .font2(.body)

I'm trying to learn how to write API's in the declarative nature that Apple is pushing with Swift UI but it seems like documentation isn't complete on this. I've tried creating some sort of ViewModifier type but I'm not really sure what I need to do with this, since it required I return _ModifiedContent<_, _> and not exactly sure how to do this. Basically, is it possible to modify the properties of a view using a declarative syntax like the ones in the built in SwiftUI views.

回答1:

As dfd linked to in the comments, you can create custom modifiers that use apple's provided modifiers. You can also create your own methods that modify vars though.

Note: You can't use mutating methods here because function builders return immutable values. You'll get a compiler error. You need to make a copy of self and return that.

extension LabelView {
    func font1(_ font1: Font) -> Self {
        var copy = self
        copy.font1 = font1
        return copy
    }
}

You can also create a generic version that updates variables using a keypath:

extension View {
    func modifying<T>(_ keyPath: WritableKeyPath<Self, T>, value: T) -> Self {
        var copy = self
        copy[keyPath: keyPath] = value
        return copy
    }
}

Usage of both versions:

struct LabelView: View {
    var font1: Font = .headline
    var font2: Font = .body
    var body: some View { ... }
}

LabelView()
    .modifying(\.font2, value: .callout)
    .font1(.largeTitle)

And here is the result:



回答2:

See if this is something you want. You can simply modify the properties according to your need. If you want a fixed textView, then keep it static in the custom labelView.

import SwiftUI

struct LabelView: View {
    var font1: Font = .headline
    var font2: Font = .subheadline
    var text1: String = ""
    var text2: String = ""
    var body: some View {
        VStack {
            Text(text1).lineLimit(nil).font(font1)
            Text(text2).lineLimit(nil).font(font2)
        }
    }
}

struct StackOverFlow : View {
    var body: some View {
        //LabelView(font1: .title, font2: .subheadline )
        LabelView(font1: .title, font2: .subheadline, text1: "What is Lorem Ipsum?", text2: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.")
    }
}