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()
}
}