I'm trying to create a custom dot indicator (like the one under TabView). This is the code:
import SwiftUI
public struct CustomDotIndicatorView: View {
private let indicatorCount: Int
private let height: CGFloat
private let spacing: CGFloat
@State private var _activeIndex: Int = 0
private let activeImage: some View = Image( "dotindicator-active")
.renderingMode(.original)
.colorMultiply(.green)
private let inactiveImage: some View = Image( "dotindicator-inactive")
public var activeIndex: Int {
get { return self._activeIndex }
set {
guard newValue >= 0 && newValue < indicatorCount else { return }
self._activeIndex = newValue
}
}
init(indicatorCount: Int,
default activeIndex: Int = 0,
height: CGFloat = 6,
spacing: CGFloat = 12) {
let actualCount = indicatorCount > 1 ? indicatorCount : 1
self.indicatorCount = actualCount
self.height = height
self.spacing = spacing
if activeIndex < 0 {
self.activeIndex = 0
} else if activeIndex >= actualCount {
self.activeIndex = actualCount - 1
} else {
self.activeIndex = activeIndex
}
}
public var body: some View {
HStack(alignment: .center, spacing: spacing) {
ForEach(0..<indicatorCount) { index in
index == self._activeIndex ? self.activeImage : self.inactiveImage
}
}
}
}
However it has this error:
'buildExpression' is unavailable: this expression does not conform to 'View'
'buildExpression' has been explicitly marked unavailable here (SwiftUICore.ViewBuilder.buildExpression)
Can anybody help me? Thank you
Use a simple if else
, such as:
if index == self._activeIndex {
self.activeImage
} else {
self.inactiveImage
}
instead of using a ternary expression
that returns two different View types,
which is not allowed in ForEach
. This is because ViewBuilder
does not handle the ternary case if the return types are not the same. For example:
you could also use
index == self._activeIndex ? AnyView(self.activeImage) : AnyView(self.inactiveImage)
But I would not recommend using that approach.
Note, you should not mutate @State
properties via computed properties like activeIndex
.