why do we need to foreUnwrap the dataType while creating an object? Swift 5 - class

whenever I go to internet or check projects on online while creating any object in ViewController class I see an exclamation mark(!) at end of data type why is that? for example:#IBOutlet weak var Label:UILabel!
so why is that '!' mark or why are we force unwrapping it?
And also when I remove it , it gives an error , we can also write like this #IBOutlet weak var Label=UILabel()
so why don't we use this?

You need to understand the things step by step to get an answer.
#IBOutlet: This is a property wrapper that tells Xcode to connect an element on the storyboard with the one in .swift file.
The ! mark: This force-unwrap thing is recommended because when the .swift file needs the instance, it's sure to find the connected element in storyboard. We can also write #IBOutlet weak var Label: UILabel? In this case the instance Label would be optional.
#IBOutlet weak var Label=UILabel(): In this case the instance Label's value is overridden with UILabel() immediately after initialization. The #IBOutlet weak var Label! and Label after Label = UILabel() are not the same instances.
#IBOutlet weak var label: UILabel is equivalent of writing var label: UILabel. Therefore the i-val does not have an initial value and Swift does not allow that. In Swift either you have to assign a value to a variable or make it optional/unwrapped to explicitly deal with nil values. And also when you unwrap the instance with #IBOutlet, you can use it with ? later on like label?.text = "Some text".
So, we are forced to use ! or ? for an outlet to explicitly make the variable optional as in for other variable declarations. And we don't use = UILabel() after declaration because it overrides the instance created from the Storyboard (initialized with an NSCoder/Coder from the nib).

Related

Swift: How to link Touch Bar controls to main window controls

I'm new to Swift/macOS dev, plenty of dev experience otherwise though. Just trying to make something rudimentary.
Here's my app storyboard:
I'm trying to get:
the Touch Bar slider to change when the slider on the main window changes
vice versa
update the Touch Bar Label button with the Int value of the slider.
Q) How do I achieve this?
Note: The main window slider control is wired up and working when I manipulate it e.g.
#IBOutlet weak var mySlider: NSSlider!
#IBAction func mySlider_Changed(_ sender: NSSlider) {
//... stuff happens here.
}
You'll want your view controller to have some explicit model/state of what the value of these sliders have. e.g.
class ViewController : NSViewController {
var value: Double
}
Then you can connect the sliders and textfield to update or display this value.
Approach 1: Target/Action/SetValue
This follows the use of explicit IBActions that you had started. In response to that action, we'll pull the doubleValue from the slider and update the ViewController's model from that:
#IBAction func sliderValueChanged(_ sender: NSSlider) {
value = sender.doubleValue
}
The second piece is updating everything to reflect that new value. With Swift, we can just use the didSet observer on the ViewController's value property to know when it changes and update all of the controls, e.g:
#IBOutlet weak var touchBarSlider: NSSlider!
#IBOutlet weak var windowSlider: NSSlider!
#IBOutlet weak var windowTextField: NSTextField!
var value: Double {
didSet {
touchBarSlider.doubleValue = value
windowSlider.doubleValue = value
windowTextField.doubleValue = value
}
}
And that's it. You can add a number formatter to the textfield so it nicely displays the value, which you can do in Interface Builder or programmatically. And any other time you change the value, all of the controls will still get updated since they are updated in the didSet observer instead of just the slider action methods.
Approach 2: Bindings
Bindings can eliminate a lot of this boiler plate code when it comes to connecting model data to your views.
With bindings you can get rid of the outlets and the action methods, and have the only thing left in the view controller be:
class ViewController: NSViewController {
#objc dynamic var value: Double
}
The #objc dynamic makes the property be KVO compliant, which is required when using bindings.
The other piece is establishing bindings from the controls to our ViewController's value property. For all of the controls this is done by through the bindings inspector pane, binding the 'Value' of the control to the View Controller's value key path:
And that's it. Again, you could add a number formatter to the textfield, and any other changes to the value property will still update your controls since it will trigger the bindings to it. (you can also still use the didSet observer for value to make other changes that you can't do with bindings)

Swift - Modifying a variable with the variable name stored in a variable

The title of this question is most probably confusing but I have no idea what to name this question. I am writing a program in swift and I want to modify a property of an object (in this case a UIImageView) but I'm not sure of which object I'm going to modify, I only know the type.
An example of what I'm talking about is shown below:
Inside the ViewController class
#IBOutlet weak var LeftImage: UIImageView!
#IBOutlet weak var RightImage: UIImageView!
// A function to change the image inside a UIImageView
func ChangeImage(imageViewLocation: UIImageView, newImage: String) {
// Change the image
self.imageViewLocation.image = UIImage(named: newImage))
}
// Call the function
ChangeImage(LeftImage, "smileyFace")
Can this be done?

How to add a custom uiview to a view controller programmatically SWIFT

I am trying to add a custom view programmatically to my view controller. I used this snippet of code with no success of it appearing in front of my main view controller.
var DynamicView = CustomFilter()
DynamicView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(DynamicView)
CustomFilter Class:
import UIKit
class CustomFilter:
UIView {
#IBOutlet weak var party: UIButton!
#IBOutlet weak var outdoors: UIButton!
#IBOutlet weak var sports: UIButton!
#IBOutlet weak var diner: UIButton!
#IBOutlet weak var music: UIButton!
#IBOutlet weak var gaming: UIButton!
}
The custom filter is connected to a xib file.
Xib File:
Is there a possibility that the custom view maybe out of placed? I'm not sure what I'm doing wrong.
In order to use a view we designed in a xib, we most load from the xib.
if
let bundle = NSBundle(forClass: CustomFilter.self),
let nib = bundle.loadNibNamed("<#Xib File Name#>", owner: self, options: nil),
let dynamicView = nib.first as? CustomFilter {
self.view.addSubview(dynamicView)
}
An alternative approach would be to write your CustomFilter's init to load the view from the xib itself.
More clearly, the problem you're having is that none of your CustomFilter's initializers are going to care about the xib file you made unless you write them and tell them to care about it. Your current code is returning a 0x0 view with probably a white or clear background. If you modified your current code to set the CustomFilter's frame to something other than 0x0 size and set the background color to something like UIColor.greenColor(), you'd see it clear as day.
Also, you could use Xcode's visual debugger to find it.
It's probably zero height and zero width, and may be off-screen also.
You need to give it height and width constraints and x and y position constraints.
You should probably also use CustomFilter(frame: someFrameRect), since as I recall initWithFrame is the designated initializer for UIView.
As an aside, variable names should start with a lower-case letter, so DynamicView should be dynamicView

Swift: Generate Identifier for Variable

I have a large list of NSButtons (80+) that I assign to an array. I've used a numbering scheme to name them and I'd like to generate the identifiers for them instead of calling them directly by name one at a time.
#IBOutlet weak var button120: NSButton!
#IBOutlet weak var button121: NSButton!
var buttons [NSButton]()
Currently (in ViewDidLoad()) I'm doing the equivalent of:
buttons = [ button120, button121 ]
I'd prefer to do something like:
for index in 120...121 {
buttons.append("button\(index)".toIdentifier)
}
Can this be done in Swift?
This might be an easier solution, view is the parent view of the buttons
let buttons = view.subviews.filter { $0 is NSButton}

unwrapping optionals in swift

I am still trying to wrap my head around optionals in swift. Can someone explain why when we created the model object we put an '!' after and also when creating the quizCountries array of strings we also put the '!' and set it equal to nil. I don't really understand why we would want something to ever be nil in the program.
// QuizViewController.swift
import UIKit
class QuizViewController: UIViewController, ModelDelegate {
#IBOutlet weak var flagImageView: UIImageView!
#IBOutlet weak var questionNumberLabel: UILabel!
#IBOutlet weak var answerLabel: UILabel!
#IBOutlet var segmentedControls: [UISegmentedControl]!
private var model: Model! // reference to the model object
private let correctColor = UIColor(red: 0.0, green: 0.75, blue: 0.0, alpha: 1.0)
private let incorrectColor = UIColor.redColor()
private var quizCountries: [String]! = nil // countries in quiz
private var enabledCountries: [String]! = nil // countries for guesses
private var correctAnswer: String! = nil
private var correctGuesses = 0
private var totalGuesses = 0
Exclamation mark after the property name indicates that this is an implicitly unwrapped optional.
Implicitly unwrapped optionals are useful when an optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter. The primary use of implicitly unwrapped optionals in Swift is during class initialization, as described in Unowned References and Implicitly Unwrapped Optional Properties.
These are useful when referenced object, e.g. model in your case, is not yet known at the moment of initialisation of QuizViewController but still is guaranteed to be available and assigned to the model property before its first use. Same goes for quizCountries, we do not know these countries yet when the controller is created but will certainly know them before they are going to be used.
In case if implicitly unwrapped optional, contrary to expectation, is (still) nil at the moment of its use the application will crash.
If you create IBOutlets by dragging from elements in interface builder to the source file they default to being Implicitly Unwrapped Optionals (IUOs) with !'s. You can manually change the them to be proper optionals with ? so that you can handle them carefully and let the compiler check all the cases.
You might be interested in the cases when they could be nil. These are a few that I have thought of although I may have missed some.
Before the view loads (before viewDidLoad has been called) they will be nil.
If you ever explicitly set them to nil.
If the item has been removed from Interface Builder.
If the outlet has been removed from Interface Builder.
If you have multiple views/view controllers in Interface Builder which use the same class and one has the outlets connected and another doesn't.
If you have two Storyboards (e.g. one for iPhone and one for iPad) that both reference the same class but only one has the IBOutlet connected.
If you ever unload the view the outlets are likely to become nil again.
Given all these possibilities I prefer to use proper optionals. I blogged about how I deal with optionals and also gave a talk on the topic.