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})
}
}
Related
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")
}
}
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)
}
We like the auto equality behavior of struct, by just simply conform Equatable protocol.
Sometimes, we would like to exclude some properties out from struct equality comparison.
Currently, this is the method we are using.
struct House: Equatable {
var field_0: String
var field_1: String
...
var field_50: String
static func == (lhs: House, rhs: House) -> Bool {
if lhs.field_0 != rhs.field_0 { return false }
if lhs.field_1 != rhs.field_1 { return false }
...
if lhs.field_48 != rhs.field_48 { return false }
// Ommit field_49
if lhs.field_50 != rhs.field_50 { return false }
return true
}
}
But, such methodology is pretty error prone, especially when we add in new field in the struct.
Is there a way to exclude certain properties fields from equality comparison, without writing our own == function?
Is there a way to exclude certain properties fields from equality comparison, without writing our own == function?
No. At present, the only alternative to automatic synthesis of Equatable conformance is good old fashioned writing code.
There is a solution using Swift Property Wrapper feature actually. It's described here.
Basically what you need is to define next property wrapper
#propertyWrapper
struct EquatableNoop<Value>: Equatable {
var wrappedValue: Value
static func == (lhs: EquatableNoop<Value>, rhs: EquatableNoop<Value>) -> Bool {
true
}
}
And use it like next
struct Test: Equatable {
var a: Int
#EquatableNoop
var b: () -> Void
}
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 learning SwiftUI at the moment. I've been playing around with loading a list from CoreData and making changes on / filtering etc. I've run into the issues below. Essentially as soon as I try to apply any conditionals within the ForEach I I'm presented with that error.
This works if I run iterate through the organisations in List itself rather than a ForEach. This isn't the ideal solution as I loose the inbuilt deletion function.
Am I missing something stupid?
let defaults = UserDefaults.standard
#EnvironmentObject var userData: UserData
#Environment(\.managedObjectContext) var managedObjectContext
#FetchRequest(entity: Organisation.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Organisation.name, ascending: true)])
var orgs: FetchedResults<Organisation>
var body: some View
{
NavigationView {
List {
ForEach(orgs, id: \.self) {org in
if !self.userData.showFavsOnly || org.isFavorite {
NavigationLink(destination: OrganisationView(org: org, moc: self.managedObjectContext)) {
OrganisationRow(org: org)
}
}
}
}
}
}
There error code I get is I get is on the for each line and is
Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols
Thanks for your help
Type '()' cannot conform to 'View'; only struct/enum/class types can
conform to protocols
This error means that your ForEach loop expects some View. But you give it an if-statement instead. What if the condition returns false?
The solution may be to wrap the if-statement in some View - it could be a Group, VStack, ZStack...
ForEach(orgs, id: \.self) { org in
Group {
if !self.userData.showFavsOnly || org.isFavorite {
NavigationLink(destination: OrganisationView(org: org, moc: self.managedObjectContext)) {
OrganisationRow(org: org)
}
}
}
}