I am working on an iOS project.
I have a UISlider on my ViewController.
I want to bind a Float property (var) to the current slider value.
If I change this property value, I want to automatically update my slider position.
If I move my slider, I want to automatically update property value.
I have made it work manually with delegates et stuff, but I want to know if there is a simplest way.
In fact, I have many (very huge) UISliders, and the values can change from other part of program.
I know in C# for example it is possible to observe a value and bind it with UI components. I want to do the same thing in swift
There are many ways to do this and many libraries that help you with this task.
I like Bond.
It lets you observe property values without the didSet boilerplate and helps you keep your code clean.
For example, this does exactly what you are looking for:
import Bond
let ageSlider = UISlider()
let age = Observable<Float>(0)
ageSlider.bnd_value.bidirectionalBindTo(age)
This helps you keeping the MVC or MVVM pattern, because changing the model property will also update the UI right away (and viceversa!):
age.Value = 18
print(ageSlider.value) // Prints 18
You can also combine values into a single one:
let slider1 = UISlider()
let slider2 = UISlider()
let averageValue = combineLatest(slider1.bnd_value, slider2.bnd_value).map{ (v1, v2) in (v1 + v2) / 2 }
averageValue.observe { newValue in
print("New average value is \(newValue)")
}
More info in the project page.
Related
What's the main difference between property observers and property wrappers? They seem to be very similar in that they manage how the properties are stored. The only thing I can think of is that you can reuse property wrappers since there is a layer of separation between code that manages how a property is stored and the code that defines a property.
Property Wrapper
#propertyWrapper
struct TwelveOrLess {
private var number: Int
init() { self.number = 0 }
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
struct Rectangle {
#TwelveOrLess var height: Int
#TwelveOrLess var width: Int
}
Property Observer
struct Rectangle {
var height: Int {
didSet {
if oldValue > 12 {
height = 12
} else {
height = oldValue
}
}
}
var width: Int {
didSet {
if oldValue > 12 {
width = 12
} else {
width = oldValue
}
}
}
}
The two cases above accomplish pretty much the same thing, which is to set the properties to be equal to or less than 12.
You say:
The only thing I can think of is that you can reuse property wrappers since there is a layer of separation between code that manages how a property is stored and the code that defines a property.
Your example (and some of your text) appears to be lifted from the Swift Programming Language: Property Wrapper manual:
A property wrapper adds a layer of separation between code that manages how a property is stored and the code that defines a property. For example, if you have properties that provide thread-safety checks or store their underlying data in a database, you have to write that code on every property. When you use a property wrapper, you write the management code once when you define the wrapper, and then reuse that management code by applying it to multiple properties.
So, yes, the virtue of the property wrapper is the reuse achieved by separating the “code that manages how a property is stored and the code that defines a property.” This resulting reuse is the whole m.o. of property wrappers.
You clearly, you can write your own setters and getters (which is better, IMHO, than a pattern of writing an observer that mutates itself), too, but you lose the reuse and abstraction that the property wrappers offer.
You go on to say:
The two cases above accomplish pretty much the same thing, which is to set the properties to be equal to or less than 12.
Sure, but if you want to do this for ten different properties, the wrapper avoids you from needing to repeat this code ten times. It also abstracts the details of this “equal to or less than 12” logic away from where you declare the property.
Another big difference betwixt property observers and property wrappers is that property observers can access self, whilst property wrappers cannot yet (as of this writing) access self using a stable, documented interface.
You can work around this limitation by manually passing self to the property wrapper in init. This workaround is described in the property wrapper proposal.
You can access self in a property wrapper using an undocumented, unstable interface which you can learn about by typing “property wrapper _enclosingInstance” into your favorite search engine.
I currently have a function that, when a button is pressed, takes a value that is determined from a UIStepper and adds it to a list of numbers. When I press the Add Tip button, it correctly displays the tip amount in the text view, but when I add a new value it replaces it rather than adding it underneath.
Here is the function:
#IBAction func addTipButton(_ sender: UIButton) {
let tipDollarCent = dollar + cent
sampleLog.text = "\(tipDollarCent)\n"
totalLabel.text = tipDollarCent
}
sampleLog is the Text View that needs to take a variable amount of lines of data, depending on how many time the user presses addTipButton
I am aware that my best course of action is probably to do an incremental loop, and I have tried implementing a separate addNewLine function, but tipDollarCent was out of scope and gave me an error.
I also initially tried adding sampleLog.text = "\(tipDollarCent)\n" += "\(tipDollarCent)\n" directly to the function.
I am hoping someone would be able to patiently and kindly explain to me what the best loop to use in this scenario would be, and how to properly implement it.
Here is a screenshot of my app so it is easier to see what I am trying to accomplish
If you want the textView text to append the newly created string, you can use the compound-assign operator for addition += to concatenate the what you previous had and grow it with a new string value.
var foo:String = "Foo"
let bar:String = "Bar"
foo += bar /* FooBar */ /* foo = foo + bar */
And for your comment on additions with doubles, the compound operator will also work with same-typed operands.
var pi:Double = 3.0
let fourteen:Double = 0.14
pi += fourteen /* 3.14 */ /* pi = pi + fourteen */
I would like to monitor the number of tableview cell and once it becomes zero (delete all the rows) my button would be disabled immediately otherwise (insert a new row) it will be enabled.
And I would like to do this with ReactiveCocoa.
I'm a newbee with RAC and what I tried is like this:
let count = NSNumber(integer: self.records!.count)
let countSignal: RACSignal = count.rac_willDeallocSignal();
countSignal.subscribeNext { (AnyObject) in
NSLog("here i am")
self.navigationItem.rightBarButtonItem?.enabled = AnyObject.integerValue > 0 ? true : false;
}
But it didn't work.
So far I know how to generate signals and monitor the change with some text fields cause it just comes like this:
self.myTextField.rac_textSignal
But what if I want to product signals or monitor the change of properties or variables so I could subscribe and pass the signals on and do some callback based on their changes?
You will get a value changed signal by using rac_valuesForKeyPath method to observer the property value,here is an tutorials:SWIFT AND REACTIVECOCOA,in Objective-C there is macro RACObserver(target,key path)
As a preface, this might be an incredibly simple and/or ignorant question.
In ReactiveCocoa 2.x, we were able to use RACObserve and RAC to observe properties of an object. From the documentation I can find in Reactive 3 and 4, we now use PropertyType to observe changes to an object property. I have so far been unsuccessful in being able to observe any property change when using MutableProperty or DynamicProperty.
class TempObject {
var property: String
}
let tempObject = TempObject()
let propertyObserver: MutableProperty<String> = MutableProperty(tempObject.property)
From what I understand, I should be able to use propertyObserver to view changes to tempObject.property. I tried adding a map function to the signal producer from propertyObserver to see if it was firing, but don't see anything when updating tempObject.property. Again, could be a trivial thing that I am missing, thanks so much.
Edit
NachoSoto nailed it - I needed to make my property KVO compliant. I also ended doing this:
let tempObjectSignal: MutableProperty<TempObject> = MutableProperty(tempObject)
let propertyObserver: MutableProperty<String> <~ tempObjectSignal.producer.map({ $0.property })
And whenever tempObject.property is updated I make sure to call
tempObjectSignal.value = tempObject
This fires off all the necessary signals. I don't know if this breaks any best practices, though. Let me know what you think!
MutableProperty(value) creates a mutable property but only with value that as the initial value.
What you want to use is DynamicProperty, which will use the Objective-C runtime and KVO to detect changes to an object's property:
let property = DynamicProperty(tempObject, "property")
For that reason, however, you need to make sure that the property you want to observe is part of the Objective-C runtime, by making the class a subclass of NSObject, and by either using the dynamic keyword:
class TempObject: NSObject {
dynamic var property: String
}
Or using #objc to ensure that it gets exported to the runtime:
class TempObject: NSObject {
#objc var property: String
}
I am creating a game in which, depending on the number of 'swipes' chosen to do, (let's say 3), 3 different patterns show on the screen, one by one. I am working on developing the first pattern.
So I have this:
if (swipes.no_of_swipes) == 3 {
swipeArray = Array<UInt32>(count: 3, repeatedValue: 0)
for i in 0 ..< 3 {
swipeArray[i] = arc4random_uniform(84)}
}
As far as I am aware, this code creates an array with three UInts which can be accessed by doing swipeArray[0], swipeArray[1], and swipeArray[2]. My first question is how long will this swipeArray stay the same? Until the close the view? Should I have a 'refresh button' when the user loses - and if so, how would I make one?
Then I have a property observer. You will notice the for loop, which I am using to keep code concise. I understand that I could do something like x++ somewhere in here so that it will go through each one.
var playBegin: Bool = false{
didSet {
if playBegin == true {
println("\(playBegin)")
var swipes = Menu()
if (swipes.no_of_swipes) == 3 {
for i in 0 ..< 3 {
patternRoom.image = UIImage(named: "pattern\(swipeArray[x])")
//rest of code
}
}
}
The pattern image comes from a set of 84 images named like pattern7 and pattern56. My second question is, how could I code the for loop to go through each swipeArray[x].
Thank you in advance,
Will
how long will this swipeArray stay the same?
This is a bit too open ended. It’ll stay the same until you assign a new value to it, either from this same bit of code or a different part. Only you can know when that will be, by looking at your code.
Since you express an interest in keeping the code concise, here’s a couple of code tips.
You might think about writing your first snippet’s loop like this:
swipeArray = (0..<swipes.no_of_swipes).map { _ in
arc4random_uniform(84)
}
This combines creating a new array and populating the values. By the way, just in case you don’t realize, there’s no guarantee this array won’t contain the same value twice.
It’s also probably better to make swipeArray of type [Int] rather than [UInt32], and to convert the result of arc4random to an Int straight away:
Int(arc4random_uniform(84))
Otherwise the UInt32s will probably be a pain to work with.
For your second for loop, you can do this:
for i in swipeArray {
patternRoom.image = UIImage(named: "pattern\(i)")
// rest of code
}
When writing Swift, usually (but not always), when you find yourself using array[x] there’s a better more expressive way of doing it.