Basic Usage

How to set up appearance and manage behavior of the components.

Appearance

All components in the library have view models that should be used to configure their appearance. These models are shared between SwiftUI and UIKit components. For example, an input field can be configured as follows:

let inputFieldVM = InputFieldVM {
  $0.title = "Email"
  $0.placeholder = "Enter your email"
  $0.isRequired = true
}

All view models in ComponentsKit do not have memberwise initializers. Instead, they conform to the ComponentVM protocol, which defines an initializer that modifies default values:

/// Initializes a new instance by applying a transformation closure to the default values.
///
/// - Parameter transform: A closure that defines the transformation.
init(_ transform: (_ value: inout Self) -> Void)

This approach has two main benefits:

  1. It allows you to set parameters in any order, making the initializers easier to use.
  2. Future changes can be made without breaking your code, as it simplifies deprecation.

Behavior

To control the behavior in SwiftUI, you should use bindings:

struct App: View {
  @State var email = ""
  @FocusState var isEmailFieldFocused: Bool
  
  var body: some View {
    VStack {
      SUInputField(
        text: $email,
        isFocused: $isEmailFieldFocused,
        model: inputFieldVM
      )
      .onChange(of: self.email) { newValue in
        // Track changes in the inputted text
        print("Email: \(newValue)")
      }
      
      SUButton(
        model: .init {
          $0.title = isEmailFieldFocused
          ? "Hide Keyboard"
          : "Show Keyboard"
          $0.isFullWidth = true
        },
        action: {
          // Control the focus (hide / show the keyboard)
          self.isEmailFieldFocused.toggle()
        }
      )
    }
    .padding(.horizontal)
  }
}

To control the behavior in UIKit, you should use the components' public variables:

class ViewController: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    
    let button = UKButton(model: .init {
      $0.title = "Show Keyboard"
    })
    
    let inputField = UKInputField(
      model: inputFieldVM,
      onValueChange: { text in
        // Track changes in the inputted text
        print("Email: \(text)")
      }
    )
    
    button.action = { [weak inputField, weak button] in
      // Control the focus (hide / show the keyboard)
      guard let inputField, let button else { return }
      if inputField.isFirstResponder {
        inputField.resignFirstResponder()
        button.model.title = "Show Keyboard"
      } else {
        inputField.becomeFirstResponder()
        button.model.title = "Hide Keyboard"
      }
    }

    view.addSubview(inputField)
    view.addSubview(button)
    
    inputField.horizontally(16)
    inputField.centerVertically(offset: -50)
    
    button.below(inputField, padding: 10)
    button.horizontally(16)
  }
}