Modifiers

Platform libraries may provide any number of modifiers for customizing the look and feel of native elements. These modifiers are automatically available as functions when rendering platform-specific templates in a module that uses one of the following macros:

Modifiers are typically used to adjust styling (colors, typography, etc.), presentation, interactivity and other properties of the native UI you're targeting.

Modifier functions

Here's a simple example of calling modifier functions inline for elements with the modifiers attribute in SwiftUI:

Source

defmodule MyAppWeb.ModifiersExampleLive do
  use Phoenix.LiveView
  use LiveViewNative.LiveView

  @impl true
  def render(%{platform_id: :swiftui} = assigns) do
    # This UI renders on the iPhone / iPad app
    ~SWIFTUI"""
    <VStack>
      <Text>This text is normal</Text>
      <Text modifiers={font_weight(:bold)}>This text is bold</Text>
      <Spacer modifiers={frame(height: 16)} />
      <HStack>
        <Image system-name="heart.fill" modifiers={
          background(alignment: :center, content: :heart_bg)
          |> foreground_style({:color, :white})
        }>
          <Circle template={:heart_bg} modifiers={
            frame(width: 32, height: 32)
            |> foreground_style({:color, :red})
          } />
        </Image>
      </HStack>
    </VStack>
    """
  end
end

Result

Modifiers example

Modifier functions may have different arities and take different types of arguments, which are generally based on the original APIs they're based on. All modifier functions return a %LiveViewNativePlatform.Env{} struct (same as the @native assign) which can be passed to other modifier functions, effectively allowing them to be chained together.

For more information of which modifiers a platform supports and how to use them, check the documentation for that platform as well as the relevant source material for that platform.

Modifier Classes

Using a lot of modifiers in your templates can cause them to become overly verbose and difficult to maintain over time. You also might want to share modifiers between many different views and elements instead of copying them across templates. Modifier classes solve both of these problems by letting you decouple your modifiers from your templates.

Here's the previous example, adjusted to use modifier classes defined in a separate module:

modifiers_example_live.ex

defmodule MyAppWeb.ModifiersExampleLive do
  use Phoenix.LiveView
  use LiveViewNative.LiveView

  import MyAppWeb.Modclasses, only: [modclass: 3]

  @impl true
  def render(%{platform_id: :swiftui} = assigns) do
    ~SWIFTUI"""
    <VStack>
      <Text>This text is normal</Text>
      <Text modclass="bold">This text is bold</Text>
      <Spacer modclass="spacer" />
      <HStack>
        <Image modclass="heart" system-name="heart.fill">
          <Circle modclass="heart_bg" template={:heart_bg} />
        </Image>
      </HStack>
    </VStack>
    """
  end
end

modclasses.ex

defmodule MyAppWeb.Modclasses do
  use LiveViewNative.Modclasses, platform: :swiftui

  def modclass(native, "bold", _assigns) do
    font_weight(native, :bold)
  end

  def modclass(native, "spacer", _assigns) do
    frame(native, height: 16)
  end

  def modclass(native, "heart", _assigns) do
    native
    |> background(alignment: :center, content: :heart_bg)
    |> foreground_style({:color, :white})
  end

  def modclass(native, "heart_bg", _assigns) do
    native
    |> frame(width: 32, height: 32)
    |> foreground_style({:color, :red})
  end
end

Result

Modifiers example

An element can have any number of modifier classes, providing some composability for modifier functions:

modifiers_example_live.ex

defmodule MyAppWeb.ModifiersExampleLive do
  use Phoenix.LiveView
  use LiveViewNative.LiveView

  import MyAppWeb.Modclasses, only: [modclass: 3]

  @impl true
  def render(%{platform_id: :swiftui} = assigns) do
    ~SWIFTUI"""
    <VStack>
      <Text>This text is normal</Text>
      <Text modclass="bold">This text is bold</Text>
      <Text modclass="italic">This text is bold</Text>
      <Text modclass="bold italic">This text is bold and italic</Text>
    </VStack>
    """
  end
end

modclasses.ex

defmodule MyAppWeb.Modclasses do
  use LiveViewNative.Modclasses, platform: :swiftui

  def modclass(native, "bold", _assigns) do
    font_weight(native, :bold)
  end

  def modclass(native, "italic", _assigns) do
    italic(native, %{})
  end
end

Result

Modclasses example

Modclasses within templates are translated at compile-time to their inline counterparts, so passing an assign or other dynamic value to the modclass attribute won't work. To support dynamic modifier classes that reference assigns or the modifier name itself, define any conditional logic within modclass/3.