2

I'm building a CustomTextField View and wanting to add view modifiers.

Using the standard .modifier(CustomViewModifier) is working just fine. Now I want to build ViewModifier functions so that I can use my modifiers just like any other SwiftUI modifiers.

In my CustomTextField, I've got multiple methods like this:

func setCustomPlaceholder(placeholder: String = "Enter Text") -> some View {
        modifier(CustomTextFieldPlaceholderViewModifier(viewModel: viewModel, placeholder: placeholder))
    }

These methods work. However, I can only use one of these methods at a time.

I've read several articles and this thread. Because I don't want these modifiers available to any View, I'd like to avoid using an extension to View.

In this thread, one of the answers used an extension to Text. So I tried placing my modifier methods into an extension to my CustomTextField. However, I'm still facing the same issue. The View is only recognizing one modifier function at a time.

This is how I'm using it:

VStack {
            CustomTextField()
                .setCustomPlaceholder()
                .setBorder()
        }

If I use only one of the modifier methods at a time, it works.

How can I get the functionality I'm looking for? Do I need to build a Protocol and make my CustomTextField conform to it? Thanks for any help!

James Futures
  • 185
  • 2
  • 12
  • “I'd like to avoid using an extension to `View`“ why’s that? – Alexander Jun 03 '22 at 23:15
  • The whole sentence explains it: "Because I don't want these modifiers available to any View, I'd like to avoid using an extension to View." Edit for further explanation: These modifiers are specific to my custom text field. It would be bad practice to expose them to other views. – James Futures Jun 03 '22 at 23:19
  • “It would be bad practice to expose them to other views.” Only if they’re not applicable to other views (are they?), not simply if you don’t immediately need it. If you do need to narrow it down, you’ll have to use a protocol to describe the result of your modifiers. I think you can do `some View & YourProtocol` – Alexander Jun 03 '22 at 23:28
  • They are not applicable to other views. Well, ultimately, some of them are, but some aren't. As I refactor this I'll separate them out as needed. Right now, I'm just trying to figure out how to get these available to my specific view only. Because Protocols can inherit from each other, can I build a protocol that inherits from View? Then make my custom view conform to that protocol? – James Futures Jun 03 '22 at 23:33
  • 1
    “Because Protocols can inherit from each other” they actually can, but it’s not called inheritance. Though idr if making your protocol derive from `View` is practical (or a good idea). In my previous comment, I suggested you use protocol composition. The idea is that you’d define your operators on some protocol `YourProtocol`. Then you make your modifiers return `some View & YourProtocol`, communicating to your compiler that the result doesn’t just support the methods on `View`, but also the methods on your new protocol. – Alexander Jun 04 '22 at 04:13

2 Answers2

1

If I use only one of the modifier methods at a time, it works

That’s because the only thing known about the result of the first view modifier is that it returns a View (of an unspecified kind).

Since you haven’t defined your view modifier on View… you can’t call it on a View.

Alexander
  • 59,041
  • 12
  • 98
  • 151
  • Ah! Makes sense. Can I just return my specific view from those functions? How can I do this without using a View extension? – James Futures Jun 03 '22 at 23:29
1

We can just make needed modifiers as our view member functions, which return own type to be called sequentially.

Here is a main part. Tested with Xcode 13.4 / iOS 15.5

struct CustomTextField: View {

    // ... other code

    func setCustomPlaceholder(placeholder: String = "Enter Text") -> Self {  // << here Self !!
        var tmp = self
        tmp.placeholder = placeholder
        return tmp
    }

    // ... other code
}

Complete code and demo in project

Asperi
  • 228,894
  • 20
  • 464
  • 690