I'm struggling to find how to bind an array value to a Toggle view in SwiftUI.
Lets says I have an observabled class with a Boolean array:
class TestClass: ObservabledObject {
#Published var onStates: [Bool] = [true, false, true]
static let shared = TestClass()
}
and in a View I have
...
Toggle(isOn: TestClass.shared.$onStates[0]) { // Throws error 'Referenceing subscript 'subscript(_:)' requires wrapped value of type '[Bool]'
Text("Example Toggle")
}
Why is it seemingly impossible to bind a particular array value to the toggle button?
Thanks.
We need observer in view for observable object, so fixed variant is
#StateObject var vm = TestClass.shared // << observer
var body: some View {
Toggle(isOn: $vm.onStates[0]) { // << binding via observer
Text("Example Toggle")
}
}
Related
I've got the following object that contain list of strings:
class handler: ObservableObject {
#Published var items = [String]()
}
In some closure, I set this list with valid values :
for item in item_list! {
self.handler.items.append(item.stringData)
}
and in the ContentView part I've got Picker that support to present that list of strings in realtime:
VStack {
Picker("items", selection: $arg1) {
ForEach($handler.items, id: \.self) {
Text($0)
}
}
}
However, it fails to compile due to the following reason :
Initializer 'init(_:)' requires that 'Binding<String>' conform to 'StringProtocol'
Any Idea how to resolve this ?
You don't need binding here to loop items, instead use handler directly (ie. without $), like
ForEach(handler.items, id: \.self) { // << here !!
Text($0)
}
Im making a Multiplatform KMM project, and im having trouble assigning dynamic value to the button property.
Basically I have this like button which changes its states depending on selected property:
struct SelectFavouriteButton: View {
#State var selected = false
let action: (Bool) -> Void
var body: some View {
Button(action: {
selected.toggle()
self.action(selected)
}) {
Image(systemName: selected ? "heart.fill" : "heart" )
.foregroundColor(.accentRed)
}
}
}
The view model from Kotlin part:
#Serializable
data class Data(
...
var isLiked: Boolean? = null,
var isSaved: Boolean? = null,
...
}
And the view where this button is called:
struct CellView: View {
#State var advert: Data?
var body: some View {
// Cannot convert value of type 'KotlinBoolean' to expected argument type 'Bool'
// Insert ' as! Bool'
SelectFavouriteButton(selected: advert?.isLiked ?? false){ isPressed in // Error is here
self.action(isPressed, advert?.id ?? "" , false)
}
}
}
Should I use mapping? If yes how do I do it in the current context?
Pls help
Kotlin primitives transferred to NSNumber in swift, so try the following (not tested - idea only)
let linked = advert.isLiked as? NSNumber // or NSNumber?
SelectFavouriteButton(selected: linked?.boolValue ?? false){ isPressed in
self.action(isPressed, advert?.id ?? "" , false)
}
#Asperi's suggestion should work, but the reasoning is not quite right.
Kotlin primitives are perfectly transferable to ObjC primitives, and those are easily accessible from Swift.
But ObjC primitives such as NSInteger or BOOL do not support optionals. So when you declare an optional primitive in Kotlin, it is converted to `KotlinNumber', as an object is needed to store an optional value in ObjC.
KotlinNumber is a subclass of NSNumber, so you can use it that way:
advert?.isLiked?.boolValue
Since this is the top search result for "convert bool to KotlinBoolean", I'll put this here for devs like me going the other way:
let selected = KotlinBoolean(bool: swiftObject.selected)
I'm trying to pass a variable object from a SwiftUI View to an observable Object but I'm running into the error: "Cannot use instance member 'loadedGroup' within property initializer; property initializers run before 'self' is available".
Here is how my SwiftUI View class is currently structured
struct LoadedGroupView: View {
#Binding var loadedGroup: group
#StateObject var userData = UserViewModel()
#StateObject var postData = PostViewModel(passedLoadedGroup: loadedGroup) //error here
var body: some View {
...
}
}
Here is my Observable Object class for PostViewModel()
class PostViewModel: ObservableObject {
var loadedGroup: group
let ref = Firestore.firestore()
init(passedLoadedGroup: group) {
group = passedLoadedGroup
}
}
How would I go about fixing this error because I really need to get that value passed into this observable object class from the View somehow. Thanks for the help!
I have two classes, one is the ContentView who is displaying the information from my data source. The data source is my CharacterRepository.
What I'm struggling with right now is making sure I always have a sorted list inside of my CharacterRepository.
Here's the code I have so far:
class CharacterRepository: ObservableObject {
#Published public var characters = [Character(name: "Nott the Brave",
initiative: 23,
isActive: false),
Character(name: "Caduceus Clay",
initiative: 2,
isActive: false),
...]
...
}
and
struct InitiativeTrackerScreen: View {
#EnvironmentObject var characterRepository: CharacterRepository
var body: some View {
NavigationView {
VStack {
List {
ForEach(characterRepository.characters) { entry in
CharacterListElement(character: entry)
}
...
Now my intended approach would be something like turning the characters variable into a computed property that runs the "sorted by" function every time the get gets executed. Unfortunately, Binding to a computed property is not yet possible in SwiftUI (not sure, will it ever be?).
Can someone please help me with this? I don't want to go back to the old approach of sorting and redrawing every time something changes. That's not why I am using SwiftUI with ist sweet bindings.
I think it may be unnecessary overhead to be sorting the data in the property getter.
The simple (and performance-conscious) solution is to sort the data when it changes, and then to update a #Published non-computed property and bind to that:
class CharacterRepository: ObservableObject {
func updateCharacters(_ characters: [Character]) {
self.characters = characters.sorted() // or whatever sorting strategy you need...
}
#Published public var characters = [Character(name: "Nott the Brave",
initiative: 23,
isActive: false),
Character(name: "Caduceus Clay",
initiative: 2,
isActive: false),
...]
...
}
If you prefer to be über-purist about this at the cost of performance, then you can manually create a Binding - something like this:
class CharacterRepository: ObservableObject {
let sortedCharacters: Binding<[Character]>
...
init() {
self.sortedCharacters = .init(get: { return characters.sorted() }, set: { self.characters = $0 })
}
...
}
I like to answer by related question. For what do you ever need some binding to computed property?
class CharacterRepository: ObservableObject {
#Published public var characters = [Character(name: "Nott the Brave",
initiative: 23,
isActive: false),
Character(name: "Caduceus Clay",
initiative: 2,
isActive: false),
...]
...
var sorted: [Character] {
characters.sorted()
}
}
is all you need.
I'm unable to use logical not ! operator with Bindable $ object.
Here is the scenario I want-
struct ContentView: View {
#State private var isLoggedIn:Bool = true
var body: some View {
Text("Root View")
.sheet(isPresented: !self.$isLoggedIn) {
SignInView()
}
.onAppear { self.performAuthentication() }
}
}
Sign In View should present as soon as I set isLoggedIn = false by some button action. For which I have to use logical not operator before $.
Compiler error: Cannot convert value of type 'Binding' to
expected argument type 'Bool'
How can I achieve this?
As I said in comment to question there were approach posted for SwiftUI: transform Binding into another Binding. If you, however, want to have it explicitly as operator, you can use the following (tested & works with Xcode 11.2)
extension Binding where Value == Bool {
static prefix func !(_ lhs: Binding<Bool>) -> Binding<Bool> {
return Binding<Bool>(get:{ !lhs.wrappedValue },
set: { lhs.wrappedValue = !$0})
}
}