Typecasting a superview in Swift - ios

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.

Related

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

Type cast an instance of a class

Let's say I have two classes: FirstViewController and SecondViewController.
self.tabBarController?.viewControllers![0] can be an instance from either of those two.
This one is no problem:
let firstVC = self.tabBarController?.viewControllers![0] as! FirstViewController
But this one gives error "classOfVC is not a type":
let aVC = self.tabBarController?.viewControllers![0]
let classOfVC = object_getClass(aVC)
let myVC = aVC as! classOfVC
Any suggestions?
Edit: I do not want to use isKindOfClass because I am trying to avoid using if ... else if... and since there are more than two classes envolved.
Type cast operator (as? or as!) is compile time operator. Type of any variable in Swift can't be dynamic as you intend to achieve with your cast.
You can't easily avoid if let, guard etc. In your your case you can do an explicit cast (as!), if you know that view controller at particular index never changes. But it is not recommended approach, usually you should tend to avoid using explicit casting.
You can use isKindOfClass to check the object of which class
let aVC = self.tabBarController?.viewControllers![0]
if avc.isKindOfClass(FirstViewController) {
//type of first ViewController
}
else if avc.isKindOfClass(SecondViewController) {
//type of second ViewController
}

Compilation error when using lazy variable

I am trying to understand how lazy works in swift. The following code compiles without any error.
import UIKit
class Test : UIViewController{
let i = 1
lazy var j:Int = self.i
}
Whereas if I remove the type of j and make it inferred like the code below,
import UIKit
class Test : UIViewController{
let i = 1
lazy var j = self.i
}
I get compilation error "Value of type 'NSObject -> () -> Test' has no member 'i'"
Can someone explain what is going on with the compiler. Thanks
You need to consider 3 aspects here.
A lazy stored property is a property whose initial value is not
calculated until the first time it is used. That is when you call
the property for the first time its value will be calculated.
As the actual value is created by evaluation, you need to declare
lazy variable's data type up front.
The properties you declare in your class are the instance variables,
if you need to access their values you need an instance(object) of
the class.
Let us consider following line from your non working code
lazy var j = self.i
Two problems are present in your non working code. First is you have not specified the data type of your lazy var. As the declaration is incomplete compiler won't treat 'j' as lazy var it will treat is as normal instance variable. You must be aware about swift's rule that whenever you declare any variable in the class you must assign some value to it or else you can make that variable optional. In above statement you are trying to assign i's value to j by using self within the class. When compiler is compiling that line no object of the class is created and the self you are using is nil that is why compiler is throwing the error(It will throw error even if you don't use self due to the same reason mentioned above).
Why it is not causing problems in your working code:
lazy var j:Int = self.i
As I have mentioned above the value to lazy var is assigned when you call that variable. How will you call that variable out side the class? Of course by creating object. And when you will call it with the help of object 'self' will point to the same object and self won't be nil it won't cause any problem.
In short two things you need to understand 3 things
While declaring lazy var you have to specify the data type
lazy var is not assigned when it is declared, it is assigned when it
is called
you can not call instance var without instance(object) of your class
Here is an example of lazy initialization with a view controller
Note the () at the end
lazy var myViewController : MyViewController? = {
let storyboard: UIStoryboard = UIStoryboard(name: "MyVC", bundle: nil)
return storyboard.instantiateViewController(withIdentifier: "MyAccount") as? MyViewController
}()

Determining from Objective-c if a Swift property is declared as dynamic

I have been trying for some time to inspect a Swift class, and determine if any of the properties are declared as dynamic. My example class is as below:
class SwiftTestClass : DBObject {
dynamic var SwiftTestString : String!
dynamic var SwiftTestNumber : NSNumber!
dynamic var lowercaseField : String!
var nonDynamicVariable : String!
func testThyself() {
SwiftTestClass.query().fetchLightweight().removeAll()
let newObject = SwiftTestClass();
newObject.SwiftTestString = "hello, world"
newObject.SwiftTestNumber = 123
newObject.lowercaseField = "lowercase"
newObject.nonDynamicVariable = "should not be persisted"
newObject.commit()
let result = SwiftTestClass.query().fetch().firstObject;
print(result)
}
}
I am basically trying to pick out the fact that the property nonDynamicVariable is not declared as dynamic as the rest of them are.
DBObject is a subclass of NSObject.
I have tried:
Looking at the type encoding of the property, they are identical (type for type)
Seeing if they have a difference in the method implementations, they do not. (e.g. class_getMethod), the dynamic properties still have getter/setter methods.
Grabbing the Ivars to see if there is any difference there
Looking at all of the property attributes, also identical.
What I do know:
If I try to class_replaceMethod for the <propertyName>/set<propertyName>, it works for a dynamic property (as you would expect, because it adds objc compatibility) but fails to work (but does replace?, well, the memory address of the method changes!) or be actioned on the non dynamic property.
Does anyone know how to differentiate the two property declarations in swift from objc?
Thanks

Subclassing UIView in Swift - error

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.

Resources