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"
}
}
Related
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()
}
I have a main view titled NoteTakerViewController. I have a weatherGetterController class with a protocol that returns 5 days of weather with a protocol function called getMyWeather. However the protocol function is not being called that returns the weather data to NoteTakerViewController. I am certain I have everything set up correctly with the delegates but perhaps I do not.
This is really not a duplicate of Swift Delegate Not Being Called as the solution on that page did not work.
Any help you could provide would be great.
Here's the relevant code snippets:
My weatherGetterController class:
protocol WeatherGetterControllerDelegate {
func getMyWeather(weather: [FiveDayForecast])
}
class WeatherGetterController: UIViewController, CLLocationManagerDelegate {
var weatherGetterDelegate: WeatherGetterControllerDelegate?
And in the WeatherGetterController "getWeather" function call. The line
self.weatherGetterDelegate?.getMyWeather(weather: myForecast)
is not being called.
func getWeather() {
...
getNetWeather { (fetchedInfo) in
if let fetchedInfo2 = fetchedInfo {
//self.updateUI(mainWeather: fetchedInfo2)
//need to call delegate here
let myForecast = self.figureFive(weather: fetchedInfo2)
//return the forecast
print(myForecast)
self.weatherGetterDelegate?.getMyWeather(weather: myForecast)
}
}
Finally the function implementation in NoteTakerViewController:
class NoteTakerViewController: UIViewController, ..., UITextFieldDelegate, WeatherGetterControllerDelegate
func getMyWeather(weather: [FiveDayForecast]) {
print("get my weather")
print(weather)
}
Through debugging I can see that "weatherGetterDelegate" is set to nil and I don't know why.
Thanks
First you need to make WeatherGetterController a property in NoteTakerViewController or a local variable where you call it but I will use the first option.
class NoteTakerViewController: UIViewController, ..., WeatherGetterControllerDelegate {
var weatherGetterController: WeatherGetterController?
//...
func viewDidLoad() {
//....
weatherGetterController = WeatherGetterController()
weatherGetterController?.delegate = self
}
//And then in some other part of your code you do
func someFunc() {
self.weatherGetterController?.getWeather()
I am curious why WeatherGetterController is define to be a viewcontroller, is that really correct?
class WeatherGetterController: UIViewController, CLLocationManagerDelegate {
Personally I would remove that part
class WeatherGetterController: CLLocationManagerDelegate {
Put this in init of WeatherGetterController
public weak var weatherGetterDelegate: WeatherGetterControllerDelegate?
/* if you are not initializing it anywhere else
and if you are initializing it anywhere else then make sure you are
initializing WeatherGetterController there too and then put delegate
of WeatherGetterController to NoteTakerViewController object
else write the below code */
var model = NoteTakerViewController()
public override init() {
self. weatherGetterDelegate = model
}
In the code below, which (I hope) includes all that's relevant to my question, a mouseEntered/-Exited event in ChangerView is supposed to change the display in ChangingView. (ChangerView and ChangingView are displayed side-by-side and share a view controller.) As an OOP newbie, though, I'm seriously missing something about how to set up delegation between these views. Here's ChangerView (in which DoThis?.show = nil, despite that I thought I was setting it to true or false):
import Cocoa
protocol DoThis { var show: Bool { get set } }
class ChangerView: NSView {
var changeDelegate: DoThis?
// Set up for mouseEntered/-Exited
override func mouseEntered(theEvent: NSEvent) { DoThis?.show = true }
override func mouseExited(theEvent: NSEvent) { DoThis?.show = false }
}
And here's changing view:
import Cocoa
class ChangingView: NSView, DoThis {
var show: Bool = false { didSet { needsDisplay = true } }
// Draw into the view
override func drawRect(dirtyRect: NSRect) {
switch show {
case true: // Display setup contingent on show = true
case false: // Display setup contingent on show = false
}
// Draw contingent display
}
}
As I understand things, views should do their own basic display work, and view controllers should handle model-related and higher-level display changes. For that reason, and to keep things simple, I want ChangerView and ChangingView to communicate directly. Unfortunately, I couldn't find any explanations about delegation close enough to this situation—at least not that I could understand.
What am I missing (besides a properly functioning brain)?
Thanks!
It looks like there are two issues.
In your ChangerView class, you should be using the delegate to set the show variable, like this:
import Cocoa
protocol DoThis { var show: Bool { get set } }
class ChangerView: NSView {
var changeDelegate: DoThis?
// Set up for mouseEntered/-Exited
override func mouseEntered(theEvent: NSEvent) { changeDelegate?.show = true }
override func mouseExited(theEvent: NSEvent) { changeDelegate?.show = false }
}
You may want to make the delegate variable weak to prevent reference cycles
The other issue is you've forgot the step where you assign the delegate. I (and I think everyone else) forget this often. Once you get used to setting up delegates you'll remember to check for it if things don't work at first.
So at some point you need to set the changeDelegate var to an instance of the ChangingView class (this is often done in the viewDidLoad() function.
It will look something like this:
class SomeViewController: UIViewController {
#IBOutlet weak var SomeChangerView: ChangerView!
#IBOutlet weak var SomeChangingView: ChangingView!
override func viewDidLoad() {
super.viewDidLoad()
SomeChangerView.changerDelegate = SomeChangingView
}
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.
I want to add an observer to check changes of a global variable, but it not seems possible.
var selectedItem: NSManagedObject?
class LCCC: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
// need to set up an "observer" that trigger selectedItemDidChange method, if selectedItem changed
}
func selectedItemDidChange {
}
}
I think the most idiomatic way to do this in swift would be using property observers:
var test:NSString = "hi" {
willSet {
}
didSet {
}
}
(Although you need to provide an initializer)
Have you tried something like:
NSNotificationCenter.defaultCenter().addObserver(selectedItem, selector: Selector("selectedItemDidChange"), name: "selectedItemObserver", object: self)