Where Is Uiview.Init() Documented

Where does UIView.init() come from?

Keep in mind that UIView is an Objective-C class, and Objective-C is not Swift. None of the rules about designated and convenience initializers and the rules of their inheritance exist in Objective-C — a fact that can sometimes cause significant trouble when using those classes in Swift. That said, in effect, UIView's init() is a convenience initializer that calls init(frame: .zero).

iOS: UIView subclass init or initWithFrame:?

The designated initializer is the one that all the other initializers must call. UIView and subclasses are a little unusual in that they've actually got two such initializers: -initWithFrame: and -initWithCoder:, depending on how the view is created. You should override -initWithFrame: if you're instantiating the view in code, and -initWithCoder: if you're loading it from a nib. Or, you could put your code in third method and override both those initializers such that they call your third method. In fact, that's often the recommended strategy.

So, for example, you might create a UIView subclass, ClueCharacter, that has its own initialization method: -initWithPerson:place:thing:. You then create your view like this:

Obj-C:

ClueCharacter *mustard = [[ClueCharacter alloc] initWithPerson:@"Col. Mustard"
place:kInTheStudy
thing:kTheRope];

Swift:

var mustard = ClueCharacter("Col. Mustard", place: kInTheStudy, thing: kTheRope)

That's fine, but in order to initialize the UIView part of the object, your method must call the designated initializer:

Obj-C:

-(id)initWithPerson:(NSString*)name place:(CluePlace)place thing:(ClueWeapon)thing
{
if ((self = [super initWithFrame:CGRectMake(0, 0, 150, 200)])) {
// your init stuff here
}
}

Swift:

func init(name: String, place : CluePlace, thing : ClueWeapon)
{
if (self = super.init(CGRectMake(0, 0, 150, 200))) {
// your init stuff here
}
}

If you want to call your subclass's initializer -init, that's okay as long as you call -initWithFrame: in the implementation.

How to override UIView initializer in Swift 1.0

A class must have at least one designated initializer. It may have more than one.

All you have to do to make your code work is remove the keyword convenience from init(frame: CGRect).

Why does calling init() calls init(frame: CGRect)?

Calling CustomView() is using an inherited initializer for NSObject. This implicitly calls UIView's designated initializer, init(frame:) with .zero. Since you've overriden that initializer, your override is called and thus the setup() method is called too.

You can see this by printing out the frame parameter in your initializer; you will get a .zero rect.

How do I check if a generic view class actually implements init(frame:)?

Since Swift doesn't automatically inherit from UIView it will not be possible to check if your your class implement init(frame:) constructor.

I suppose instancesRespond(to:) return true, because it's checking if your class conform to this message with Message dispatching. However Swift uses Table dispatching for method declared in classes. So this checking will work with Objective-C but not with Swift

So, to achieve what you want, you can use protocol

create protocol Buildable which will have init(frame: CGRect) method

protocol Buildable {
init(frame: CGRect)
}

conform your class to this protocol

class MyView: UIView, Buildable {...}

now init(frame:) method will be required for your class. Change generic type of you Builder class to Buildable

Now your class could be like this

class Builder<V: Buildable>  {
func build() -> V? {
return V(frame: .zero)
}
}

and now when you will be able to build your view Builder<MyView>().build(), and if you class will be not confirm to Buildable protocol you will get compile-time error:

class SecondView: UIView {
init(custom: String) {
super.init(frame: .zero)
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

Builder<SecondView>().build()

will throw compile-time error:

error: type 'SecondView' does not conform to protocol 'Buildable'
Builder<SecondView>().build()

Getting size of UIView in init

The initializer is called too early in the lifecycle of the view to accurately do layout unless you know the exact dimensions in advance. Even so, it is idiomatically the wrong place to do it.

Try using the layoutSubviews method as such:

class SubView: UIImageView {

var mySubView: UIImageView

required init?(coder aDecoder: NSCoder) {

mySubView = UIImageView()
mySubView.backgroundColor = UIColor.cyan

super.init(coder: aDecoder)
self.addSubview(mySubView)
}

override func layoutSubviews() {
mySubView.frame = self.bounds
super.layoutSubviews()
}
}

Now the subview bounds will be set properly at the start of each layout pass. It’s a cheap operation.

Also, the bounds property of a UIView is the frame translated to the view’s internal coordinate space. This means that normally this is true: bounds = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height). I suggest reading the documentation on view layout.

Alternatively, you can ditch manual layout entirely and use AutoLayout to do this for you.

class SubView: UIImageView {

var mySubView: UIImageView

required init?(coder aDecoder: NSCoder) {

mySubView = UIImageView()
mySubView.backgroundColor = UIColor.cyan

super.init(coder: aDecoder)
self.addSubview(mySubView)

mySubView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
mySubView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
mySubView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
mySubView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
}
}

Xcode UIView.init(frame:) must be used from main thread only

Xcode 9 has a new runtime Main Thread Checker that detects call to UIKit from a background thread and generate warnings.

I know its meant to generate warnings and not crash the app, but you can try disabling Main Thread Checker for your test target.

Sample Image

I tried this code in a sample project, the debugger paused at the issue (as it is supposed to), but the app didn't crash.

override func viewDidLoad() {
super.viewDidLoad()

DispatchQueue.global().async {
let v = UIView(frame: .zero)
}
}


Related Topics



Leave a reply



Submit