skip to Main Content

I would like to inject my ViewModifier from a ViewModel which depends on user configuration.

Below code is a View which has a ViewModel. ViewModel can be created with ViewConfiguration and user should be able to either use a default style or custom style based on preferences.

When I try to do this it gives me error on View which says

Type ‘any ViewModifier’ cannot conform to ‘ViewModifier’


struct FeatureUnavailableView: View {
    
    var viewModel = ViewModel(config: ViewConfiguration(titleStyle: DefaultTitleModifier()))
    
    var body: some View {
        Text("Hello World")
            .modifier(viewModel.config.titleStyle)
    }
}

class ViewModel {
    
    let config: ViewConfiguration
    
    init(config: ViewConfiguration) {
        self.config = config
    }
}

struct ViewConfiguration {
    
    var titleStyle: any ViewModifier
}

public struct DefaultTitleModifier: ViewModifier {
    
    public func body(content: Content) -> some View {
        content
            .lineLimit(1)
            .foregroundColor(.red)
            .bold()
    }
}


2

Answers


  1. Rather than using any ViewModifier you need to use generics to set the type as a concrete ViewModifier type when the ViewConfiguration struct and ViewModel class are initialised.

    struct ViewConfiguration<T: ViewModifier> {
        
        var titleStyle: T
    }
    
    class ViewModel<T: ViewModifier> {
        
        let config: ViewConfiguration<T>
        
        init(config: ViewConfiguration<T>) {
            self.config = config
        }
    }
    
    Login or Signup to reply.
  2. Views don’t belong in Models, but you can use Models to make a View dynamic.

    Instead of expecting any ViewModifier you can expect ViewModiferModel.

    struct ViewConfiguration {
        
        var titleStyle: ViewModifierModel
        
    }
    

    Then store and transmit any values with that model.

    struct ViewModifierModel: Equatable {
        var color: UIColor
        var isBold: Bool
        var lineLimit: Int
        
        static let `default` = ViewModifierModel(color: .red, isBold: true, lineLimit: 1)
        static let two = ViewModifierModel(color: .orange, isBold: false, lineLimit: 2)
    }
    

    The modifier itself would switch from hardcoded values to the values in the model.

    public struct DefaultTitleModifier: ViewModifier {
        let model: ViewModifierModel
        public func body(content: Content) -> some View {
            content
                .lineLimit(model.lineLimit)
                .foregroundColor(Color(model.color))
                .bold(model.isBold)
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search