skip to Main Content

I am trying to assign a custom label style based on my icon alignment value, that is left or right. if it is left, the icon comes before text, if it is right the icon comes after it. These are my custom label styles:

var iconAlignment: IconAlignment
enum IconAlignment {
    case left
    case right
}
struct IconFirstLabelStyle: LabelStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.icon
            configuration.title
        }
    }
}
struct IconFirstLabelStyle: LabelStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.title
            configuration.icon
        }
    }
}

I want to do this now:

Label { Text("hi") } icon : {Image(systemName: "plus")}
    .labelStyle(iconAlignment == .left ? IconFirstLabelStyle() : TextFirstLabelStyle())

but this causes an issue and Xcode tells me these are two different structs, even though they are inheriting the same protocol. Is there any way to use ternary here, or should I go a long approach with if else?

2

Answers


  1. You say "even though they are inheriting the same protocol"—that does not display the understanding that some, and the ternary operator, each require one type. Such as this:

    labelStyle(
      iconAlignment == .left
        ? AnyStyle(IconFirstLabelStyle())
        : AnyStyle(TextFirstLabelStyle())
    )
    
    import SwiftUI
    
    /// A type-erased "`Style`".
    public struct AnyStyle<Configuration> {
      private let makeBody: (Configuration) -> AnyView
    }
    
    // MARK: - public
    public extension AnyStyle {
      init(makeBody: @escaping (Configuration) -> some View) {
        self.makeBody = { .init(makeBody($0)) }
      }
      
      func makeBody(configuration: Configuration) -> some View {
        makeBody(configuration)
      }
    }
    
    // MARK: - LabelStyle
    extension AnyStyle: LabelStyle where Configuration == LabelStyleConfiguration {
      public init(_ style: some LabelStyle) {
        self.init(makeBody: style.makeBody)
      }
    }
    
    // MARK: - ButtonStyle
    extension AnyStyle: ButtonStyle where Configuration == ButtonStyleConfiguration {
      public init(_ style: some ButtonStyle) {
        self.init(makeBody: style.makeBody)
      }
    }
    
    // etc.
    
    Login or Signup to reply.
  2. Ternary operator requires both parts to be of the same type.

    A simple solution for this is just to use one style type and inject condition via argument, like

    Label { Text("hi") } icon : {Image(systemName: "plus")}
        .labelStyle(IconAlignedLabelStyle(alignIcon: iconAlignment))
    
    struct IconAlignedLabelStyle: LabelStyle {
        var alignIcon = IconAlignment.left      // << default one !!
        func makeBody(configuration: Configuration) -> some View {
            HStack {
                if let alignIcon == .left {
                  configuration.icon
                  configuration.title
                } else {
                  configuration.title
                  configuration.icon
                }
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search