I know it's a weird title but there are lots of posts with similar titles and completely different problems. Mostly people writing other stuff than View code inside their view, which I am not doing (as far as I can tell).
I'm trying to make Picker compatible with other BinaryInteger types since it doesn't work with anything but Int, and I'm having some trouble getting the Previews to work. Here's the code :
import SwiftUI
struct CompatibilityPicker<Label, SelectionValue, Content> : View where Label : StringProtocol, SelectionValue : BinaryInteger, Content : View {
var content : () -> Content
var label : Label
@Binding private var _selection : SelectionValue
private var selection: Binding<Int> { Binding<Int>(
get: {
Int(_selection)
},
set: {
self._selection = SelectionValue($0)
})
}
init(_ label : Label, selection : SelectionValue, content : @escaping () -> Content) {
self.label = label
self._selection = selection
self.content = content
}
var body: some View {
Picker(label, selection: selection, content: content)
}
}
struct CompatibilityPicker_Previews: PreviewProvider {
@State static var selection : UInt8 = 4
static var previews: some View {
CompatibilityPicker("Difficulty", selection: selection) { //error : Type'()' cannot conform to 'View'
Text("Easy").tag(0)
Text("Normal").tag(1)
Text("Hard").tag(2)
}
}
}
What gives ? I have a normal Picker that uses the exact same syntax and that works, I don't know what I'm doing wrong.
Thanks to @RobMayoff's solution, I am one step further ahead, however seemingly nonsensical errors have shown up that don't clear with cmd shift k :
init(_ label : Label, selection : SelectionValue, @ViewBuilder content : @escaping () -> Content) {
self.content = content
self.label = label
self._selection = selection //variable self._selection used before initialised
// This stays on this line if I change the order,
} // Return from initializer without initialising all stored properties
// That is not true, as far as I can tell
CodePudding user response:
Shenaniganically, you are trying to use ViewBuilder syntax in the trailing closure, but you didn't adorn content with the @ViewBuilder annotation. So Swift infers that the trailing closure returns () (also called Void).
Change the init declaration to mention @ViewBuilder:
struct CompatibilityPicker<blah blah blah>: View where blah blah blah {
init(
_ label : Label,
selection : SelectionValue,
@ViewBuilder content : @escaping () -> Content
// ^^^^^^^^^^^^
) {
blah blah blah
UPDATE
@Binding private var _selection : SelectionValue
blah blah blah
init(_ label : Label, selection : SelectionValue, content : @escaping () -> Content) {
self.label = label
self._selection = selection
self.content = content
}
The _selection variable is wrapped by the Binding wrapper, which means that it is really a computed property. The stored property is named __selection (note the two underscores) and has type Binding<SelectionValue>. Because _selection is a computed property, init cannot mention it until all stored properties are initialized. Probably you should change init to take a Binding<SelectionValue> argument instead of a SelectionValue argument:
init(
_ label : Label,
selection : Binding<SelectionValue>,
@ViewBuilder content : @escaping () -> Content
// ^^^^^^^^^^^^
) {
self.label = label
self.content = content
__selection = selection
}
UPDATE 2
struct CompatibilityPicker<Label, SelectionValue, Content>: View where Label : StringProtocol, SelectionValue : BinaryInteger, Content : View {
var label : Label
@Binding var selection : SelectionValue
var content : () -> Content
init(_ label : Label, selection : Binding<SelectionValue>, @ViewBuilder content : @escaping () -> Content) {
self.label = label
self._selection = selection
self.content = content
}
private var value: Binding<Int> { Binding<Int>(
get: {
Int(selection)
},
set: {
self.selection = SelectionValue($0)
})
}
var body: some View {
Picker(label, selection: value, content: content)
}
}
struct CompatibilityPicker_Previews: PreviewProvider {
struct TestView: View {
@State var selection : UInt8 = 1
var body: some View {
CompatibilityPicker("Difficulty", selection: $selection) {
Text("Easy").tag(0)
Text("Normal").tag(1)
Text("Hard").tag(2)
}
}
}
static var previews: some View {
TestView()
}
}

