I have this view and would like to simplify it:
struct SUIBorderedIconTextButton: View {
struct IconInfo {
enum Position {
case left
case right
case top
case bottom
var isHorizontal: Bool {
return self == .left || self == .right
}
var isBeforeText: Bool {
return self == .left || self == .top
}
}
let icon: Image
let position: Position
}
let iconInfo: IconInfo?
let title: String
let fontSize: CGFloat
let backgroundColor: Color
let enabled: Bool
let action: () -> Void
@ViewBuilder
private var content: some View {
if let iconInfo = iconInfo {
switch iconInfo.position {
case .left:
HStack {
iconInfo.icon
Text(title).fontSize(fontSize)
}
case .right:
HStack {
Text(title).fontSize(fontSize)
iconInfo.icon
}
case .top:
VStack {
iconInfo.icon
Text(title).fontSize(fontSize)
}
case .bottom:
VStack {
Text(title).fontSize(fontSize)
iconInfo.icon
}
}
} else {
Text(title).fontSize(fontSize)
}
}
var body: some View {
Button(action: action) {
content
.padding()
.foregroundColor(color_button_text)
.background {
RoundedRectangle(cornerRadius: text_button_border_radius)
.foregroundColor(enabled ? backgroundColor : color_disabled_background)
}
}
.disabled(!enabled)
}
}
This view simply displays a text button, with optional icon (either on left/right/top/bottom of the text)
if position is left or right, we want to use HStack. Otherwise, use VStack.
if position is top or left, we want to put icon before text. Otherwise, put icon after text.
Note that there are quite a lot of boilerplate in the content
builder.
I created this helper function for the .top or .left cases:
@ViewBuilder
private func group(iconInfo: IconInfo) -> some View {
if iconInfo.position.isBeforeText {
iconInfo.icon
Text(title).fontSize(fontSize)
} else {
Text(title).fontSize(fontSize)
iconInfo.icon
}
}
But I am not sure how to handle choosing the constructor. I may need a pointer to the init function, but I have tried
let constructor = position.isHorizontal ? HStack.init : VStack.init
constructor {
...
}
This obviously does not compile. I am not sure how to proceed. If you have other ideas to simplify this logic, feel free to try it out too.
CodePudding user response:
Ternary operator actually is not applicable to SwiftUI types as they all value-types with generics so result in constructed different type entities and cannot be assigned to one variable.
Instead possible approach for your case is something like
@ViewBuilder var content: some View {
Text("Text")
Image(systemName: "checkmark")
}
var body: some View {
if isHorizontal {
HStack{ content }
} else {
VStack{ content }
}
}