Any idea how I can return a UIView from within didSet ?
I have a method that returns a UIView.
I need to observe an Int and return a UIView as the Int changes. I have a didSet observer set, however, I get an error when trying to return the UIView.
Appreciate any help! Thanks.
func newUIView() -> UIView {
var newUIView = UIView()
return newUIView
}
var observingValue: Int = someOtherValue {
didSet {
//Xcode complains whether I use return or not
return func newUIView()
}
}
You say in a comment:
I guess my struggle is how to observe that value and react (update UI) to it accordingly
An observer is a perfectly good way to do that. But you don't return something; you call something. This is a very, very common pattern in Swift iOS Cocoa programming:
var myProperty : MyType {
didSet {
self.updateUI()
}
}
func updateUI() {
// update the UI based on the properties
}
At the time that the didSet code runs, myProperty has already been changed, so the method updateUI can fetch it and use it to update the interface.
What you are asking doesn't make any sense.
A didSet is a block of code you add to an instance variable that gets invoked when anybody changes the value of that variable. There is no place to return anything.
If you need code that changes an instance variable and returns a view, you need to write a function:
func updateObservingValue(newValue: Int) -> UIView {
observingValue = newValue
return newUIView()
}
Related
I know the title may be confusing, but this should clear it up.
Say I define the following extension on UIView...
extension UIView {
var isVisible:Bool {
get { return !isHidden }
set { isHidden = !newValue }
}
}
In code, I can do this without issue...
let myView = UIView()
myView.isVisible = true
But if I try pulling out the extension into a reusable protocol (so I can apply it to both UIView and NSView without having to duplicate the code) like so...
public protocol ExtendedView {
var isHidden: Bool { get set }
}
public extension ExtendedView {
var isVisible: Bool {
get { return !isHidden }
set { isHidden = !newValue }
}
}
extension UIView: ExtendedView {}
extension NSView: ExtendedView {}
...then while I can read it like so...
let myView = UIView()
if myView.isVisible {
....
}
...This line will not compile!
myView.isVisible = true
It gives the following compile-time error...
cannot assign to property: 'myView' is a 'let' constant
To fix it, I have to either change the variable to a var (not what I want to do), or conform the protocol to AnyObject, like so...
public protocol ExtendedView : AnyObject {
var isHidden: Bool { get set }
}
My question is why? I mean the compiler knows at compile time the type of item the extension is being applied to so why does the protocol have to conform to AnyObject? (Yes, I do acknowledge that extending UIView (or NSView) implies an object, but still... doesn't the call site know it's not a value type?)
doesn't the call site know it's not a value type?
That doesn't matter. Protocol members allows for mutation of self. For example, if you don't constrain the protocol to AnyObject, this will always compile:
set { self = newValue as? Self ?? self }
I.e. protocols provide the only way to be able to change a reference internally. Even though you're not actually doing that in your code, the possibility of the reference mutation is there.
And even if you don't actually cause any mutation, property observers are still going to be triggered by mutating protocol members.
var myView = UIView() {
didSet {
print("Still the same \(myView) after `isVisible` changes, but that's not provable at compile-time.")
}
}
Your particular issue is due to the default of set accessors.
{ get set }
is shorthand for
{ nonmutating get mutating set }
If you change the get to be mutating as well, you'll run into the same issue.
public protocol ExtendedView {
var isHidden: Bool { get }
}
public extension ExtendedView {
var isVisible: Bool {
mutating get { !isHidden }
}
}
// Cannot use mutating getter on immutable value: 'myView' is a 'let' constant
let myView = UIView()
myView.isVisible
I have to either change the variable to a var (not what I want to do), or conform the protocol to AnyObject
Although it's not apparent why you shouldn't be constraining to AnyObject or something more restrictive, you can just use
var isHidden: Bool { get nonmutating set }
That's enough to be able to make myView a constant. However, it's more accurate to mark isVisible completely nonmutating as well, which will stop property observers triggering.
nonmutating set { isHidden = !newValue }
Ultimately, constraining as much as possible is going to make working with any protocol easier. Especially when it allows you to enforce reference semantics.
public enum OldUIFramework { }
#if os(macOS)
import AppKit
public extension OldUIFramework {
typealias View = NSView
}
#else
import UIKit
public extension OldUIFramework {
typealias View = UIView
}
#endif
extension OldUIFramework.View: ExtendedView { }
public protocol ExtendedView: OldUIFramework.View {
var isHidden: Bool { get set }
}
If you really need ExtendedView to apply to value types sometimes, then make a constrained extension for the other cases, calling the value type code.
any should be some, here, but the compiler has bugs that make it not work right now.
public extension ExtendedView where Self: OldUIFramework.View {
var isVisible: Bool {
get {
let `self`: any ExtendedView = self
return `self`.isVisible
}
nonmutating set {
var `self`: any ExtendedView = self
`self`.isVisible = newValue
}
}
}
I mean the compiler knows at compile time the type of item the extension is being applied to
I know, it looks like the compiler knows that, especially when you write the lines next to each other like that:
let myView = UIView()
myView.isVisible = true
But command-click on isVisible in that code, and where do you end up? In the protocol ExtendedView. In other words, isVisible is not ultimately a property declared by UIView; it's a property declared by ExtendedView.
And nothing about the protocol itself guarantees that the adopting object will be a reference type — unless you guarantee it by qualifying the protocol, either directly or in an extension of the protocol, by saying what kind of object can adopt it.
I would just like to add that the situation you've posited is extremely specialized: the issue only arises in exactly the situation you've created, where a protocol extension injects a computed property implementation into its adopters. That's not a common thing to do.
I have a protocol
protocol Example: class {
var value: Bool { get set }
func foo()
func bar()
}
And extension:
extension Example {
// var value: Bool { // Error: Extensions must not contain stored properties
// didSet {
// switch value {
// case true:
// foo()
// case false:
// bar()
// }
// }
// }
func foo() {
// logic...
}
func bar() {
// logic...
}
}
When value is set to true, I want foo() to be called
When value is set to false, I want bar() to be called
However, I do not want to redundantly implement didSet{ } logic into every class that conforms to Example
But, if I try to add didSet{ } logic into the extension, Xcode says "Extensions must not contain stored properties".
What is the best practice for adding default property-observing logic without having to copy/paste into every conforming class?
The Goal:
I want any subclass of UIView to conform to my protocol Expandable. The requirements of my protocol are isExpanded: Bool, expand(), and collapse. I want isExpanded = true to call expand(), and isExpanded = false to call collapse() (much like the behavior of setting isHidden). But for every subclass of UIView, I don't want to have rewrite any logic. I'd like to just make the class conform to Expandable, and jump right in to setting isExpanded.
You don't need observers for what you're describing. You just need some storage for your state. Since you know this is an NSObject, you can do that with the ObjC runtime.
// Since we know it's a UIView, we can use the ObjC runtime to store stuff on it
private var expandedProperty = 0
// In Xcode 10b1, you can make Expandable require this, but it's probably
// nicer to still allow non-UIViews to conform.
extension Expandable where Self: UIView {
// We'll need a primitive form to prevent infinite loops. It'd be nice to make this private, but
// then it's not possible to access it in custom versions of expand()
var _isExpanded: Bool {
get {
// If it's not know, default to expanded
return objc_getAssociatedObject(self, &expandedProperty) as? Bool ?? true
}
set {
objc_setAssociatedObject(self, &expandedProperty, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
}
var isExpanded: Bool {
get {
return _isExpanded
}
set {
_isExpanded = newValue
if newValue { expand() } else { collapse() }
}
}
func expand() {
_isExpanded = true // Bypassing the infinite loop
print("expand")
}
func collapse() {
_isExpanded = false
print("collapse")
}
}
If you didn't know that this were an NSObject, you can get the same thing with a global (private) dictionary that maps ObjectIdentifier -> Bool. It just leaks a tiny amount of memory (~16 bytes per view that you collapse).
That said, I wouldn't do it this way. Having two ways to do the same thing makes everything much more complicated. I would either have just isExpanded as settable, or have isExpanded as read-only and a expand and collapse. Then you don't need the extra _isExpanded.
You have to implement getter, setter explicitly:
protocol Example {
var value: Bool { get set }
func foo()
func bar()
}
extension Example {
var value: Bool {
get { return value }
set(newValue) {
value = newValue
value ? foo() : bar()
}
}
func foo() {
print("foo")
}
func bar() {
print("bar")
}
}
Hello I been researching the proper way to implement this and I found multiple articles but not the best correct way to implement this. Lets say I have a variable test, which is an optional and I would like to add some getters and setters. The problem that I have is the variable will be nil when the view loads, but I keep getting an error right on the get function that says: EXEC_BAD_ACCESS. I know the reason why I am getting this but also the system will not allow me to just add a setter by itself, any ideas to work with this? Thank you for your help
code:
var test: String? {
get {
return self.test
}
set {
//some logic
test = logic response
}
}
override func viewdidload(){
super.viewdidload()
//some logic
test = newValue
}
Your code has many syntax errors :)
However you are not looking for a getter & setter, what you want is the willSet observer.
class Controller: UIViewController {
var test: String? {
willSet {
// custom logic
self.test = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
test = "my value"
}
}
When creating a stored property with Observing Accessors, I can specify a default value. However, when overriding a stored property and its Accessors I cannot specify a default value.
Variable with getter/setter cannot have initial value.
Which seems very strange, as this is NOT a computed property with a getter/setter, but a set of Observing Accessors on a stored property!
class FirstViewController: UIViewController {
internal var test: Float = 32.0 {
willSet {
}
didSet {
}
}
The first view controller compiles fine, with a stored property initialized to 32.0
class SecondViewController: FirstViewController {
override var test: Float = 64.0 {
willSet {
}
didSet {
}
}
The second view controller does not compile, as the 'computed property' is being given an initial value
In swift you are able to override properties only with computed properties (which are not able to have default values) with same type. In your case, if you wish override test property in SecondViewController you need write something like this:
override var test: Float {
get {
return super.test
}
set {
super.test = newValue
}
}
And there is no way to override didSet/willSet observers directly; you may do this by write other methods invoked in observers and just override them:
FirstViewController:
internal var test: Float = 32.0 {
willSet {
test_WillSet(newValue)
}
didSet {
test_DidSet(oldValue)
}
}
func test_WillSet(newValue: Float) {}
func test_DidSet(oldValue: Float) {}
SecondViewController:
override func test_WillSet(newValue: Float) {
super.test_WillSet(newValue)
}
override func test_DidSet(oldValue: Float) {
super.test_DidSet(oldValue)
}
I know that this has been asked a long time ago but I came up with a slightly different solution and it works exactly as you wanted. You have a property in the first ViewController then in the inherited one you override it and have observers set on it in the form of didSet.
So in the FirstViewController you have a property like in the example below:
var myNumber: Double = 20.00
Then in the SecondViewController which inherits from FirstViewController you override it as follows:
override var myNumber: Double {
didSet {
//Here you can update UI or whatever you want to do once the property changes
//Print its value
print("Value of myNumber is : \(myNumber)")
}
I hope this will help someone with the above issue as this is a nice and easy way to solve the problem mentioned above.
I have a simple class below
import Foundation
public class UsefulClass: NSObject{
var test:NSNumber{
get{return self.test}
set{
println(newValue)
self.test = newValue
}
}
override init() {
super.init()
self.test = 5;
}
}
and I'm initializing it here
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var testClass = UsefulClass()
}
}
But it results in xcode printing out 200 5s and then crashing due to EXC_BAD_ACCESS code = 2. Why does this happen?
#vadian has provided a solution in his answer, which should fix your problem. Let me just explain what's happening.
You have created a computed property, i.e. a property which is not backed by a variable, instead both the getter and the setter do some processing, usually on another stored property, in order to respectively return a value and set a new value.
This is your computed property:
var test: NSNumber {
get { return self.test }
set {
println(newValue)
self.test = newValue
}
}
Look at the getter implementation:
return self.test
What does it do? It reads the test property of the current instance, and returns it. Which is the test property? It's this one:
var test: NSNumber {
get { return self.test }
set {
println(newValue)
self.test = newValue
}
}
Yes, it's the same property. What your getter does is to recursively and indefinitely calling itself, until a crash happen at runtime.
The same rule applies to the setter:
self.test = newValue
it keeps invoking itself, until the app crashes.
Swift variables are synthesized properties by default.
In the most cases this is sufficient (it's recommended to prefer Swift types)
var test: Int
override init() {
super.init()
test = 5
}
If you need to do something after a variable is set, use
var test: Int {
didSet{
println("\(oldValue) - \(newValue)")
}
}
your code sets the variable permanently by calling the setter which calls the setter which …
It's an infinite loop; your setter is recursively calling itself.
var test: NSNumber {
set {
test = newValue
}
}
This compiles fine, and an Objective-C programmer might expect no loop due to instead setting a "backing ivar" such as _test rather than re-calling the setter method.
But property-backing instance variable _ivars do not exist in Swift for computed properties unless you create them yourself.