WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

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?

   

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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

Reply to this topic…

You need to create an account or log in to reply.

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.