I am trying to render a simple Text()
and apply an arbitrary number of different styles .
This is my code:
struct Cell {
var content: String
var style: TextStyle
}
@ViewBuilder
func styledText(cell: Cell) -> some View {
if (cell.style.contains(TextStyle.rtl)) {
Text(cell.content)
.frame(maxWidth: .infinity, alignment: .leading)
.environment(\.layoutDirection, .rightToLeft)
.lineSpacing(10)
} else if (cell.style.contains(TextStyle.customFont)) {
Text(cell.content).font(.custom("MyFont", size: 19))
} else if (cell.style.contains(TextStyle.pinkColour)) {
Text(cell.content).foregroundStyle(Color(UIColor.systemPink))
} else {
Text(cell.content)
}
}
You can see from the above that only one branch can execute in the above function. But in my app, several styles could be applied (e.g. BOTH customFont
and pinkColour
). How do I achieve this?
@ViewBuilder
func styledText(cell: Cell) -> some View {
var text = Text(cell.content)
if (cell.style.contains(TextStyle.rtl)) {
text = text
.frame(maxWidth: .infinity, alignment: .leading)
.environment(\.layoutDirection, .rightToLeft)
.lineSpacing(10)
} else if (cell.style.contains(TextStyle.customFont)) {
text = text.font(.custom("MyFont", size: 19))
} else if (cell.style.contains(TextStyle.pinkColour)) {
text = text.foregroundStyle(Color(UIColor.systemPink))
}
text
}
As far as I understand (I'm new to Swift), every branch must result in a return value so the above does not work.
I also tried not using @ViewBuilder
at all, but I cannot get my code to type check. I get errors such as Cannot assign value of type 'some View' (result of 'Self.font') to type 'some View' (type of 'text')
What am I missing?
You can extract a view modifier for each style, which will take a Bool
and decide whether to return a modified view, or self
.
@ViewBuilder
func styledText(cell: Cell) -> some View {
Text(cell.content)
.isRtl(cell.style.contains(.rtl))
.isCustomFont(cell.style.contains(.customFont))
.isPinkColor(cell.style.contains(.pinkColor))
}
extension View {
@ViewBuilder
func isRtl(_ enabled: Bool) -> some View {
if enabled {
self.frame(maxWidth: .infinity, alignment: .leading)
.environment(\.layoutDirection, .rightToLeft)
.lineSpacing(10)
} else {
self
}
}
@ViewBuilder
func isCustomFont(_ enabled: Bool) -> some View {
if enabled {
self.font(.custom("MyFont", size: 19))
} else {
self
}
}
@ViewBuilder
func isPinkColor(_ enabled: Bool) -> some View {
if enabled {
self.font(.custom("MyFont", size: 19))
} else {
self.foregroundStyle(.pink)
}
}
}
Alternatively, always apply all the modifiers, and use some default values when particular TextStyle
s are not in style
.
func styledText(cell: Cell) -> some View {
let isRtl = cell.style.contains(.rtl)
let isCustomFont = cell.style.contains(.customFont)
let isPink = cell.style.contains(.pinkColor)
Text(cell.content)
.frame(maxWidth: isRtl ? .infinity : nil, alignment: isRtl ? .leading : .center)
.environment(\.layoutDirection, isRtl ? .rightToLeft : .leftToRight)
.lineSpacing(isRtl ? 10 : 3)
.font(isCustomFont ? .custom("MyFont", size: 19) : .body)
.foregroundStyle(isPink ? AnyShapeStyle(.pink) : AnyShapeStyle(.opacity(1.0)))
}