Subclassing UIView in Swift - error - ios

I have a file called MyView.swift that just looks like this:
class MyView : UIView {
}
I also have a MyView.xib file with a view with class set to MyView.
In a view controller I have this:
var myView: MyView?
override func viewDidLoad() {
super.viewDidLoad()
self.myView = NSBundle.mainBundle().loadNibNamed("MyView", owner: nil, options: nil)[0] as MyView
self.myView.frame = frame;
self.view.addSubview(self.myView);
}
I get this error:
MyView? does not have a member named 'frame'
Why? MyView is a subclass of UIView...

You will have to implicit unwrap that optional, or use optional chaining.
self.myView!.frame = frame
Or utilize optional chaining.
self.myView?.frame = frame
// can do this now in Swift.
The first option is more dangerous because when you forcefully unwrap an optional, and it is nil, a runtime error will occur.
Edit I apologize, optional chaining is not the right tool for the job here. It is the right tool if you wanted to query the value of the frame. Alternatively, you could use the if let syntax.
if let optionalView = self.myView {
var queriedFrame = optionalView.frame
}

MyView is a subclass of UIView, but MyView? is an Optional which has only two possible cases:
(1) it is nil, or
(2) it holds a wrapped value of type MyView
...and in either case, it is still an Optional, which means it doesn't have a member named 'frame' :) though if it's holding a wrapped value of MyView then that wrapped value does!
To get at that wrapped value (assuming it has one) it is necessary for you to either explicitly unwrap it with ! or for you to use Optional Binding (if let) in accessing its properties.

Related

Referencing UIView subclass .center with Swift 3 #keyPath

In a view controller in my app, I'm reacting to changes to a view's positioning by key-value observing its center property, like this:
class CoordinatingViewController: UIViewController {
#IBOutlet weak var cardContainerView: CardView!
override func viewDidLoad() {
super.viewDidLoad()
addObserver(self, forKeyPath: "cardContainerView.center", options: [.new], context: nil)
}
}
This works just fine for now, but since that key path is a string, it can't be checked by the compiler. To mitigate this, I'd like to use Swift 3's #keyPath() syntax, which takes a compiler-checked key path and returns the proper corresponding string. However, when I change that line to use it like this:
addObserver(self, forKeyPath: #keyPath(cardContainerView.center), options: [.new], context: nil)
The compiler gives me 2 errors:
Type 'CardView!' has no member 'center'
Cannot refer to type member 'center' within instance of type 'CardView!'
I don't understand why I'm getting these errors, as center is a documented property of UIView, which CardView inherits from directly, which I can both read and write to outside the #keyPath() statement there. Furthermore, everything appears to work ok when I pass the key path directly as a string, as in my first example, which makes this even more confusing. How can I have the compiler check that key path for me?
It seems like Swift is not happy about the weak modifier. Remove it and the code will compile. From this, we can see that outlets are recommended to be strong, unless you actually have a retain cycle. See my answer here for how to find retain cycles.
In Swift 4, you will be able to do this, using the new \ key path syntax:
// no problems with "weak"!
let observation = observe(\.cardContainerView.center) { (object, change) in
...
}

Typecasting a superview in Swift

I have subclassed a UIView, and I am trying to access it's superview, but it is returned as a UIView instead of the type of my subclass.
Here is a Swift 4 Playground showing an oversimplification of the problem.
import UIKit
class SubUIView: UIView {}
var parent:SubUIView = SubUIView()
var child:SubUIView = SubUIView()
parent.addSubview(child)
var other:SubUIView = child.superview
How can I typecast child.superview into a SubUIView?
Currently, it throws this error:
Cannot convert value of type 'UIView?' to specified type 'SubUIView'
Previous Stakeoverflow answers on this topic were not clear to me, but the answer provided here was.
superview is a property on UIView that your view subclass inherits. If you want to cast the view returned from the call to superview you have mainly two options:
Force casting, where other's type will be SubUIView. If this operation fails – i.e. the super view isn't actually of type SubUIView, the application will crash. This is done with:
var other = child.superview as! SubUIView
You can also use as? instead of as!. This way the type for other will be SubUIView?, and if the casting fails, the value will be nil.
If you add type annotation to other, it'll have to match the result of the casting operation. As in:
var other: SubUIView = child.superview as! SubUIView
var other: SubUIView? = child.superview as? SubUIView
The type annotations are not needed though.

How to create an object of another class to access the class's variables in Swift?

Like in objective C we create an object of a class to access that class's variable -
TableViewCell *obj = [[TableViewCell alloc] init];
then using "obj" we can access the variables of the class.
Similarly, I have a property of an UIView in a TableViewCell's class and I want to access that in the main class of the ViewController in which the table view is present.
I have tried doing this -
let viewObj = ViewAllCell()
Inside viewDidLoad
viewObj.menuView.isHidden = true;
But just as this code executes I get the following error
"fatal error: unexpectedly found nil while unwrapping an Optional
value"
I know this question must have been asked a million times before but I didn't understand what to search for in Swift as I'm quite new to this language, hence I am asking again.
Your problem is not with respect to accessing the variable.
The menuView variable is an optional variable and it's set to nil.
Case 1:
If you're sure that every ViewAllCell should have a menuView, don't make it an optional. Your current code will look something like:
ViewAllCell.swift
internal var menuView : MenuView? = nil
Change that to:
internal var menuView : MenuView = MenuView(frame:<yourRequiredframe>)
Either set the frame here, or just instatiate with menuView = MenuView(), and set it later.
Case 2:
If you want the menuView to be optional, instantiate a menuView in your viewDidLoad()
viewDidLoad()
let viewObj = ViewAllCell()
viewObj.menuView = MenuView(frame:<yourRequiredframe>)
viewObj.menuView.isHidden = true;
If you want to know a lot, lot more about optionals, wrapping, unwrapping, and guard statements, have a look at this:
http://www.informit.com/articles/article.aspx?p=2469047&seqNum=2
no need to create object to Access class variable.
According to obj C => clasname.staticVariable
/**********************/
UITableViewCell *fontCell = [_tableView cellForRowAtIndexPath:[_tableView indexPathForSelectedRow]]
UIView *view = Font.view;
But view Cannot be static

Using the word self in a class or its methods instead of just using the property name [duplicate]

In a simple example like this, I can omit self for referencing backgroundLayer because it's unambiguous which backgroundLayer the backgroundColor is set on.
class SpecialView: UIView {
let backgroundLayer = CAShapeLayer()
init() {
backgroundLayer.backgroundColor = UIColor.greenColor().CGColor
}
}
But, just like in Objective-C, we can confuse things by adding local variables (or constants) named similarly. Now the backgroundColor is being set on the non-shape layer:
class SpecialView: UIView {
let backgroundLayer = CAShapeLayer()
init() {
var backgroundLayer = CALayer()
backgroundLayer.backgroundColor = UIColor.greenColor().CGColor
}
}
(this is resolved by using self.backgroundLayer.backgroundColor)
In Objective-C I always eschewed ivars for properties and properties were always prefixed with self for clarity. I don't have to worry about ivars in swift but are there other considerations for when I should use self in swift?
The only times self is required are when referencing a property inside a closure and, as you pointed out, to differentiate it from a local variable with the same name.
However, personally, I prefer to always write "self" because:
That is an instant and obvious sign that the variable is a property. This is important because it being a property means that its state can vary more widely and in different ways than a local variable. Also, changing a property has larger implications than changing a local variable.
The code does not need to be updated if you decide to introduce a parameter or variable with the same name as the property
Code can be easily copied in and out of closures that do require self
Most of the time we can skip self. when we access class properties.
However there is one time when we MUST use it: when we try to set self.property in a closure:
dispatch_async(dispatch_get_main_queue(), {
// we cannot assign to properties of self
self.view = nil
// but can access properties
someFunc(view)
})
one time when we SHOULD use it: so you don't mess a local variable with class property:
class MyClass {
var someVar: String = "class prop"
func setProperty(someVar:String = "method attribute") -> () {
print(self.someVar) // Output: class property
print(someVar) // Output: method attribute
}
}
other places where we CAN use self.
before property just to be expressive about were variable/constant comes from.
Looking at Ray Wenderlich's style guide
Use of Self
For conciseness, avoid using self since Swift does not require it to access an object's properties or invoke its methods.
Use self only when required by the compiler (in #escaping closures, or in initializers to disambiguate properties from arguments). In other words, if it compiles without self then omit it.
Swift documentation makes the same recommendation.
The self Property
Every instance of a type has an implicit property called self, which is exactly equivalent to the instance itself. You use the self property to refer to the current instance within its own instance methods.
The increment() method in the example above could have been written like this:
func increment() {
self.count += 1
}
In practice, you don’t need to write self in your code very often. If you don’t explicitly write self, Swift assumes that you are referring to a property or method of the current instance whenever you use a known property or method name within a method. This assumption is demonstrated by the use of count (rather than self.count) inside the three instance methods for Counter.
The main exception to this rule occurs when a parameter name for an instance method has the same name as a property of that instance. In this situation, the parameter name takes precedence, and it becomes necessary to refer to the property in a more qualified way. You use the self property to distinguish between the parameter name and the property name.
Here, self disambiguates between a method parameter called x and an instance property that is also called x:
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
print("This point is to the right of the line where x == 1.0")
}
// Prints "This point is to the right of the line where x == 1.0"
I'm going to go against the flow and not use self unless absolutely required.
The reason why is that two of the main reasons to use self is
When capturing self in a block
When setting self as a delegate
In both cases, self will be captured as a strong reference. This might be what you want, but in many cases, you actually want to use a weak one.
Therefor, forcing the developer to use self as an exception and not a rule will make this strong capture more conscious, and let him reflect on this decision.
As Apple documentation says in https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Methods.html
The self Property
Every instance of a type has an implicit property called self, which
is exactly equivalent to the instance itself. You use the self
property to refer to the current instance within its own instance
methods.
The increment() method in the example above could have been written
like this:
func increment() {
self.count += 1
}
In practice, you don’t need to write self in your code very often. If
you don’t explicitly write self, Swift assumes that you are referring
to a property or method of the current instance whenever you use a
known property or method name within a method. This assumption is
demonstrated by the use of count (rather than self.count) inside the
three instance methods for Counter.
The main exception to this rule occurs when a parameter name for an
instance method has the same name as a property of that instance. In
this situation, the parameter name takes precedence, and it becomes
necessary to refer to the property in a more qualified way. You use
the self property to distinguish between the parameter name and the
property name.
Here, self disambiguates between a method parameter called x and an
instance property that is also called x:
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
print("This point is to the right of the line where x == 1.0")
}
// Prints "This point is to the right of the line where x == 1.0"
Without the self prefix, Swift would assume that both uses of x
referred to the method parameter called x.
I would prefer to keep using self whenever I'm using a property to omit these misunderstandings.
As Nick said, in objective-c we had ivars + synthesized properties which gave the _internal variable names to delineate things. Eg.
#IBOutlet (nonatomic,strong) UITableView *myTableView;
resulting in _myTableView to be (preferably) referenced internally - and self.myTableView to be reference beyond the class. While this is pretty black and white, consider the exception when programmatically instantiating views, you can gain clarity/ simplicity / reduce boilerplate by removing self.
#interface CustomVC:UIViewController
{
UITableView *myTableView;
}
In swift, the public / internal properties clarify this scope.
If it's a public property that other classes will interact with err on self.
Otherwise if it's internal skip self and avoid the automatic repetition.
The compiler will catch you when it's needed.
// UIViewcontroller swift header
public var title: String? // Localized title for use by a parent controller.
public var navigationItem: UINavigationItem { get }
/// In your class
self.title = "Clarity"
self.navigationItem.leftBarButtonItem = UIBarButtonItem()
// In superclass
#property(nonatomic, copy) NSString *screenName // use self.screenName in swift subclass
#IBOutlet myTableView:UITableView // use self
public var myTableView:UITableView // use self
internal var myTableView:UITableView // skip self
var myTableView:UITableView // skip self

Delegate Error in Swift

I am trying to send a double value from a UIView (which is loaded from a XIB) to a ViewController using a delegate
Here is my protocol, it is just sending a double to the main ViewController on a button click.
protocol HoursWorkedDelegate{
func sendHoursWorked(hoursWorked: Double);
}
class HoursWorkedView: UIView{
var delegate: HoursWorkedDelegate?;
#IBAction func calculateHoursWorked(sender: AnyObject){
// Do some calculations for the hoursWorked value
// Give value
delegate!.sendHoursWorked(hoursWorked);
}
}
// This class now wants that Double value
class ViewController: UIViewController, HoursWorkedDelegate{
// Conform to protocol
func sendHoursWorked(hoursWorked: Double){
// Lets say we just want to assign this value to a textField
hoursWorkedTextField.text = NSString(format: "%.4f", hoursWorked);
}
}
The error message I get is Thread 1: EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP, subcode=0x0)
Any help would be much appreciated, Thank You!
As a start, change the exclamation point in this snippet to a question mark:
delegate!.sendHoursWorked(hoursWorked);
This is what's likely causing the crash, as you are force-unwrapping the optional delegate property. A question mark means we'll only call sendHoursWorked() on the delegate if the delegate exists.
That fix will now probably mean that your program is no longer crashing, but you still don't get the desired results, because sendHoursWorked() is never called. We have to tell our HoursWorkedView object who is delegating it.
Somewhere in your code, you might have something like this:
let hoursWorkedView = HoursWorkedView()
self.view.addSubview(hoursWorkedView)
It's right here where we should be setting the delegate:
let hoursWorkedView = HoursWorkedView()
hoursWorkedView.delegate = self
self.view.addSubview(hoursWorkedView)
Though if it's me, I probably add a constructor to HoursWorkedView that accepts the delegate property:
init(delegate: HoursWorkedDelegate) {
super.init()
self.delegate = delegate
}
And now we can just do this:
let hoursWorkedView = HoursWorkedView(delegate: self)
self.view.addSubview(hoursWorkedView)
I think you're getting your view and your viewcontroller mixed up: a ViewController controls things; a view just displays them. The viewController tells the view what to display.
So, you want to connect your button to the viewController -- not the view. And you don't need a custom view class or a delegate.
Set it up like this:
create a textField and a button
create an outlet for the textField
put calculateHoursWorked directly in your viewController
create an action to connect the button to calculateHoursWorked
in calculateHoursWorked, set self.textField.text to the result of the calculation (where "textField" is whatever you named your outlet)
You wouldn't use a delegate in this context because the viewController knows everything the view does. The delegate pattern is for cases where one object has no visibility into another.
EDIT:
That being said, the bug here is that the delegate isn't actually being set anywhere.
Swift Optionals (the ! and ?) help prevent cases like this. If you explicitly unwrap an optional using !, you have to make sure it's always defined. In this case, since delegate is defined as optional (?) you have to check it:
#IBAction func calculateHoursWorked(sender: AnyObject){
// Do some calculations for the hoursWorked value
// Give value
if let currentDelegate = self.delegate {
currentDelegate.sendHoursWorked(hoursWorked)
}
}

Resources