functionswiftuiviewtextfieldcustom-ui

Trying to create a struct that returns a custom textfield according to the custom type selected


Trying to create a struct that returns a custom textfield according to the type selected... However, I get this error:

Function declares an opaque return type 'some View', but the return statements in its body do not have matching underlying types

I have already done this successfully with a Text() but apparently the TextField() works differently given that I will be accepting input by the user.

See code below:

import SwiftUI

/// The types of AtomsTextField's
enum AtomsTextFieldType {
    case roundedBorder
    case `default`
}

/// A struct that returns an AtomsTextField
struct AtomsTextField: View {
    
    /// The text entered into the AtomsTextField
    @Binding var text: String
    /// The AtomsTextField type
    let type: AtomsTextFieldType
    
    /// The body displaying the AtomsTextField
    var body: some View {
        generateAtomsTextField(text: text, type: type)
    }
    
    /// Generates an AtomsTextField according to the type that was passed in
    /// - parameter text: The text the AtomsTextField contains
    /// - parameter type: An AtomsTextFieldType used to determine what AtomsTextField will be returned
    private func generateAtomsTextField(text: String, type: AtomsTextFieldType) -> some View {
        // Returning an AtomsTextField according to the type given
        switch type {
        case .roundedBorder: return TextField("Type here", text: $text).textFieldStyle(.roundedBorder)
        case .default: return TextField("Type here", text: $text)
        }
    }
}

Any suggestions why and how to fix it?


Solution

  • You can simply use @ViewBuilder in front of your function to evaluate the results and return the view. You need to remove the "return"s from the function to use @ViewBuilder also. Effectively it causes the view to be "evaluated" each time the function is called much like the body.

    Often having to use @ViewBuilder is a bit of a "code smell" and ideally I would put that view in to its own view struct and pass the variable in to avoid this, but this is incredibly simple. Its always a bit of a trade off.

     @ViewBuilder private func generateAtomsTextField(text: String, type: AtomsTextFieldType) -> some View {
        // Returning an AtomsTextField according to the type given
        switch type {
        case .roundedBorder:
            TextField("Type here", text: $text).textFieldStyle(.roundedBorder)
        case .default:
            TextField("Type here", text: $text)
        }
    }