uiview as optional parameters in swift - swift

I have a function where I am passing the UIStackview as a parameter. I want that parameter to be optional.
func render(parentview: UIStackView = nil) {
//
}
how can I make this function optional and how to give a default value to it?

You can declare method like below:
func test(_ myNumber: Int? = 2) {
}
In above function, I have provided a default value as 2.
I can call the function as
test()
or
test(5)

Related

How do I safely unwrap an instance variable that is used in a function? Swift

I want to safely unwrap the instance variable imageHeight. Inside the func Apply() the variable has a value. But outside the function, the variable has a value of nil. How can pass the value to the instance variable of the class PinterestCell?
Here is the code:
class PinterestCell: UICollectionViewCell {
//I want to store the value here ... but i can't, it is always nil.
var imageViewHeight: NSLayoutConstraint?
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
if let attributes = layoutAttributes as? PinterestLayoutAttributes {
// But it seems like the variable is just stored inside the function ...
imageViewHeight.constant = attributes.imageHeight
}
}
}
where you setup your constraint?
imageViewHeight will always nil if you not doing something like this
imageViewHeight = imageView.topAnchor.constraint(equalTo: titleControl.bottomAnchor)

Initialize instance variables inside instance function

final class TestVC: UIViewController {
var usersFooter: Footer!
var groupsFooter: Footer!
override func viewDidLoad() {
super.viewDidLoad()
bind(footer: &usersFooter)
}
func bind(footer: inout Footer) {
footer = Footer(style: .autoFooter, height: 57) {
// doing something
}
}
}
Thats what Footer is:
final class Footer: RefreshView {
private var loader: MDCActivityIndicator!
override public init(style: Style, height: CGFloat, action: #escaping () -> Void) {
// initializing and doing something with loader
super.init(style: style, height: height, action: action)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
I get this:
Cannot pass immutable value of type 'Footer' as inout argument
How to pass TestVC instance's footers in it's function and be able to initialize them?
Why is footer that immutable (Declared as var)?
This occurs because
var someVar: Footer!
does not define a variable of type Footer but a variable of type Optional<Footer> that is implicitly unwrapped. The code:
var someFooter: Footer!
bind(footer: &someFooter)
is logically equivalent to
var someFooter: Footer?
guard let tempFooter = someFooter? else { fatalError() }
bind(footer: &tempFooter)
As you can see, tempFooter is a let, so it can't be passed as an inout variable and even if it could, the result would be thrown away.
You can fix this in one of three ways:
make the parameter to bind an optional, e.g. func bind(footer: inout Footer?) or use Martin's syntax to make it implicitly optional.
Force the unwrap yourself:
var unwrapped: Footer = someFooter
bind(footer: unwrapped)
someFooter = unwrapped
redesign the API. It seems the first thing you do in the bind function is overwrite the old footer with a newly initialised footer. So don't use an inout parameter, return the value you want i.e.
func bind() -> Footer
{
var theFooter = Footer(...) { ... }
// do stuff
return theFooter
}
someFooter = bind()
I think the last option is the best in this case.
Write bind method like this. It will resolve your error.
func bind(footer: inout Footer!) {
footer = Footer(style: .autoFooter, height: 57) {
// doing something
}
}
It seems like inout thinks Footer & Footer! are different. Either update method as above or change declaration as given below.
var userFooter: Footer
I don't know the exact reason but the error which we are getting is confusing.

What does this mean: "You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement"?

I've found this note in the Swift documentation about initializers:
You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement with an inherited initializer.
What is an "explicit" implementation? What is an "implicit" one then?
What does "satisfy the requirement with an inherited initializer" mean precisely?
Could you give me a code example, in which I don't have to provide an explicit implementation of a required initializer?
Here's an example with an inline explanation:
protocol JSONInitializable { // Use Encoders, but just for example
init(fromJSON: String)
}
class Foo: JSONInitializable {
let x: Int
// "required" is necessary because this init is required for the
// conformance to JSONInitializable
required init(fromJSON json: String) {
//...
x = 123 //some value from the JSON
}
}
class Baz: Foo {
// `init(fromJSON json: String)` can be inherited,
// so it's implicitly defined for Baz, as well as Foo.
}
class Bar: Foo {
// The presence of this uninitialized constant `y` requires an
// a value in the declaration, or an initializer that sets it
let y: Int
// Since we didn't specify a value for `y` in its declaration,
// this initializer must be explicitly specified so as to initialize `y`.
// Doing so blocks the inheritance of `init(fromJSON json: String)` from
// the super class, and requires us to define it ourselves,
// in order to preserve conformance to `JSONInitializable`
required init(fromJSON json: String) {
//...
y = 0
super.init(fromJSON: json)
}
}
It is saying this: If you have initialized all your properties as you declare them, there's no need to write an initializer.
Normally, when you have properties declared and not set, you write an init() method and set them there, and if there's a required initializer in the parent class, you call
super.init(possible, arg: anotherArg)
Since you don't need to set anything, you don't need to write anything, and since there's no init in your class, the super call will happen automatically. (Of course, if the required init needs values passed in, you still need to supply them.) So how is that accomplished? (see below.) The bottom line is that most of the work is done for you.
However, once you write an init(), then you're taking away this automatic behavior, and you have to make sure that those "automatic" calls are explicitly made.
Finally, before I provide an example, I should address this statement:
"if you can satisfy the requirement with an inherited initializer"
If the parent doesn't have an initializer which takes no arguments, how will it get those arguments? In that case you need initialize your class using that required super's init with the proper arguments.
Here's an example of a view controller which is not set up in IB, I would normally create it using the super's required initializer, since I didn't write an initializer for my derived class:
let vc = MagicController(nibName: nil, bundle: nil)
import UIKit
import Dotzu
class MagicController: UIViewController, UIGestureRecognizerDelegate {
let action = #selector(MagicController.buttonTapped(_:))
let action2 = #selector(MagicController.secondButtonTapped(_:))
let mainAction = #selector(MagicController.mainButtonTapped(_:))
let defaultPalette = Palette.randomPalette()
var questions = [Question]()
var startTime = TimeInterval()
var endTime = TimeInterval()
var selectedTimeIndex = 0
var selectedTagIndex = 0
var selectedRatingIndex = 0
var selectedTag = "swift"
let timeSpanDropDown = DropDown()
let ratingDropDown = DropDown()
let tagDropDown = DropDown()
var pageNumber = 1
var savedIndex = 0
var savedPostId = -1
var quotaCount = -1
var isFirstTime = true
let queryFactory = Queries(client: APIClient())
var timeSpanButton = UIBarButtonItem()
var ratingButton = UIBarButtonItem()
var tagButton = UIBarButtonItem()
var dbButton = UIBarButtonItem() // 🗄
/// start the console log, configure the main view's buttons/actions and present the UI
override func viewDidLoad() {
super.viewDidLoad()
Dotzu.sharedManager.enable()
self.edgesForExtendedLayout = []
configureButtons(container: view)
view.setNeedsLayout()
}
///configure the buttons/actions, prepare and present the UI
/// The first time this is called it sets up the buttons and drop downs
/// cleanupDataSource() has no effect the first time it is called
/// - Parameter animated: passed to super
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if isFirstTime {
isFirstTime = false
initializeButtons()
setupDropDowns()
}
cleanupDataSource()
}
/// etc.
}
As you can see, I did not write an init for this view controller, even though a view controller has a required init. I initialized my view controller using an init() that isn't even in my code.

Accessing a computed property outside of a method

I have a generic function inside a class in which a computed property is declared:
class CalculatorBrain {
var internalProgram = [AnyObject]()
var accumulator: Double = 0.0
var variableValues: Dictionary<String,Double> = [:]
func setOperand<T> (operand: T) {
if operand is Double {
accumulator = operand as! Double
internalProgram.append(operand as AnyObject)
}
else if variableName == operand as? String {
var dictionaryValue: Double? {
get {
return variableValues[variableName!]
}
set {
accumulator = newValue!
internalProgram.append(newValue! as AnyObject)
}
}
}
}
I want to set dictionaryValue to the value shown in the display from the view controller:
private var brain = CalculatorBrain()
#IBAction func setVariableValue(_ sender: UIButton) {
brain.dictionaryValue = displayValue
}
Obviously I can't, because dictionaryValue is locally defined and "Value of type CalculatorBrain has no memeber dictionaryValue" Now the question is, how can I make a computed property global, and make changes to it from inside a class method? Or, how can I access a computed property defined inside a class method from outside the function?
The problem is dictionaryValue is not a computed property of your class, it is just a variable declared in the setOperand function, so it is not accessible from outside the function.
You should declare it as a stored property of your class and change it when setOperand is called.

How do I pass a class as a parameter in Swift?

I want to write a function with the below signature:
func viewHasSuperviewOfClass(view: UIView, superclass: AnyObject.Type) -> Bool {
return view.superview is superclass
}
But it won't compile. What am I doing wrong? How do I pass the superclass and treat it as a parameter?
Pass the superclass as AnyClass and use isKind(of:) to test it:
func viewHasSuperviewOfClass(view: UIView, superclass: AnyClass) -> Bool {
return view.superview?.isKind(of: superclass) ?? false
}
Since view.superview is an optional you need to unwrap it. Using optional chaining will return nil if there is no superview, so use the nil coalescing operator ?? to return false if there is no superview.
Example:
let button = UIButton()
let label = UILabel()
label.addSubview(button)
viewHasSuperviewOfClass(view: button, superclass: UILabel.self) // true
viewHasSuperviewOfClass(view: label, superclass: UIButton.self) // false
It will read a little better if you make the function signature this:
func view(_ view: UIView, hasSuperviewOfClass superclass: AnyClass) -> Bool {
return view.superview?.isKind(of: superclass) ?? false
}
// example call
view(button, hasSuperviewOfClass: UILabel.self)