UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Is it possible to create a ViewModifier where output is conditional on the concrete type of Content?

Forums > SwiftUI

I would like to create a ViewModifier where the output is conditional on the type of content it is modifying.

The best test of the concept I've managed (using Text and TextField as example View types) is as follows:

struct CustomModifier<T: View>: ViewModifier {
    @ViewBuilder func body(content: Content) -> some View {
        if content is Text.Type {
            content.background(Color.red)
        } else {
            if content is TextField<T>.Type {
                content.background(Color.blue)
            }
        } 
            content
    }
}

The problem with the above modifier is that you need to explicitly provide the generic term when you use the modifier so seems incorrect (and, to my nose, a code smell) as you then need to define a generic on the parent View and then a generic on its parent, etc, etc..

e.g.

struct ContentView<T: View>: View {
    var body: some View {
        VStack {
            Text("Hello world")
                .modifier(CustomModifier<Text>())

            TextField("Textfield", text: .constant(""))
                .modifier(CustomModifier<TextField<T>>())
        }
    }
}

I managed to get around this problem (with some guidance from Cristik) using this extension on View:

extension View {
    func customModifier() -> some View where Self:View {
        modifier(CustomModifier<Self>())
    }
}

The modifier was tested using the following using iPadOS Playgrounds:

struct ContentView: View {
    var body: some View {
        Form {
            Text("Hello")
                .customModifier()

            TextField("Text", text: .constant(""))
                .customModifier()
        }
    }
}

This compiles and runs but the output is not what I expected. The Text and TextField views should have different backgrounds (red and blue respectively) but they are displayed unchanged. Overriding the View type checking in the modifier (hard coding the type check to 'true') results in a background colour change so the modifier is being applied; it's the type check that's failing.

I dragged the code into Xcode to try and get a better idea of why this was happening and got an immediate compiler warning advising that the type check would always fail (the modifier name in the screenshot is different - please disregard):

Xcode compiler errors: Errors

This explains why the code does not perform as intended but I'm unable to determine whether I have made a mistake or if there is (in real terms) no way of checking the concrete type of a View sent to a ViewModifier. As best I can tell, the content parameter sent to a ViewModifier does seem to be type erased (based on the methods accessible in Xcode) but there does seem a way of obtaining type information in this context because certain modifiers (e.g. .focused()) only operate on certain types of View (specifically, interactive text controls) and ignore others. This could of course be a private API that we can't access (yet...?)

Any guidance / explanation?

2      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.