Instantiating a class and using properties makes app crash - ios

I'm building a keyboard extension.
In my program I have a view controller(1) and a view(2) class which I use for a xib file .
1)
class KeyboardViewController: UIInputViewController {
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "view1", bundle: nil)
let objects = nib.instantiate(withOwner: self, options: nil)
view = objects[0] as? UIView
}
class View1: UIView {
#IBOutlet weak var someLabel: UILabel!
#IBOutlet weak var someButton: UIButton!
}
I wanted to instantiate the view class inside of my view controller so, following apple's documentation I did this:
class KeyboardViewController: UIInputViewController {
let v1 = View1()
let v2 = View2()
}
The problem is that whenever I try to call inside of my view did load something like:
v1.someLabel.text = "something"
and I run my app, for some reasons it doesn't work and eventually crashes.
Important things: the views are connected to two different .xib files and I'm working on a custom keyboard extension.
I'm sure I'm missing something in the instantiation but I can't find out what it is, I see other developers on git hub doing exactly the same as I do but running their apps gives no problem... So what am I missing out? If you can please send me more documentation about it as well...
Edit:
In both my view classes I'm doing the following to initialize them:
class View1: UIView {
#IBOutlet weak var someLabel: UILabel!
#IBOutlet weak var someButton: UIButton!
init(label: UILabel, button: UIButton) {
self.someLabel = label
self.someButton = button
super.init(frame: CGRect(x: 0, y: 0, width: 330, height: 200))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
But then in my main view controller, when i call let v1 = View1(), it becomes:
let v1 = View1(coder: NSCoder)
And I'm having a hard time figuring out what to put in the parameter field

If it's crash, I think that your UITextField in your View1 is null. This field is init in your class or it refers to a UINib or UIStoryboard ?
To fix this, try init child elements in your custom UIView as well.

I found a solution by myself:
Go to your xib file, in the file's owner section set the custom class on View1; As per the view section, set the custom class to UIView;
In your class View1: UIView add a view outlet from your xib file ie:
#IBOutlet var view: UIView!
Paste this in your class View1: UIView
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit(){
Bundle.main.loadNibNamed("view1", owner: self, options: nil)
addSubview(view)
view.frame = self.bounds
view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
//Add any other setup here
}
Now create another xib file, make sure it's file's Owner is KeyboardViewController. Add an UIView to it and make sure its class is of type View1.
Now go to class KeyboardViewController: UIInputViewController and link the view of type View1 in your code. Right under it write weak var v : View1!
In viewDidLoad()you can eventually write view = View1()
You're done!

Related

Xcode - How IBOutlets and Nib files work?

I'm for the first time using nib files. I mean xib and the corresponding swift class.
Here is my swift class:
import UIKit
#IBDesignable class LittleVideoView: UIView {
var view: UIView!
var nibName: String = "LittleVideoView"
// MARK: Views
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var clicksLabel: UILabel!
#IBOutlet weak var channelNameLabel: UILabel!
#IBOutlet weak var thumbnailImageView: UIImageView!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: nibName, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
}
From the editor view I created some IBOutlets as you can see. Everything works properly.
There is just something I don't understand. I am programmatically loading the nib in this class, so why can I create IBOutlets while Xcode doesn't really knows that I will really load the correct nib file? Shortly, I don't understand how IBOutlets can work in this case. So, how will Xcode correclty link the loaded UIView in the setup() method with the IBOutlets ?
Nib files have a "File's Owner" type which the editor uses to list available outlets and actions. However when the nib is loaded at runtime that type is not enforced. Outlet's are connected using -setValue:forKey: under the assumption that the "Owner" passed to instantiateWithOwner is compatible with any outlet bindings defined in the nib.
One xib File with Multiple "File's Owner"s
If you have an IBOutlet, but not a property, is it retained or not?

Cannot instantiate UIView from nib. "warning: could not load any Objective-C class information"

I get "could not load any Objective-C class information. This will significantly reduce the quality of type information available." warning in the console while initializing an instance of this class:
#IBDesignable
class SystemMessage: UIView{
#IBOutlet weak var lbl_message: UILabel!
var view: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup(){
view = loadViewFromNib()
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
addSubview(view)
}
func loadViewFromNib() -> UIView{
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "SystemMessage", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
}
Execution stops on line let view = nib.instantiateWithOwner... with "Thread 1: EXC_BAD_ACCESS(code=2...)"
What could be the possible reason behind this?
Found the solution. It all comes to understanding of how xibs work.
What I did was that I set class for both view and File's Owner and connected all the outlets from the View rather than from the File's owner.
This seems like you are going the long way round instantiating a view. You have a view of class SystemMessage which instantiates a nib of name SystemMessage and then inserts that as a view :/
The simplest way to do this is to set the root view in your Xib to be of type SystemMessage
Then you connect your outlets to the view that you just gave the right type
This means that you can lose have your code and end up with
import UIKit
#IBDesignable
class SystemMessage: UIView {
#IBOutlet weak var lbl_message: UILabel!
static func loadViewFromNib() -> SystemMessage {
return NSBundle(forClass: self).loadNibNamed("SystemMessage", owner: nil, options: nil).first as! SystemMessage
}
}
This just gives you an easy way to instantiate your view from code with SystemMessage.loadViewFromNib(). File's Owner is probably being set incorrectly in this instance

Label in custom view not updating

I have created a custom UIView in xib with class InfoPopUpView. I add it to my viewController. It works well and the Custom Class xib is set in the File's Owner.
I can set the titleLbl variable in my first viewcontroller no problem.
My problem appears when I in another viewcontroller want to use the InfoPopUpView again but with a different title...
The title does not change/update. It is as if InfoPopUpView remembers the last title I set and dont care for changing it..
How can I initialise? the titleLbl variable in the InfoPopUpView Class so that I can change it later?
Any help is very much appreciated - thank you !
ViewController
var popUpView: InfoPopUpView!
popUpView = InfoPopUpView(frame: CGRectMake(0, 0, 300, 268))
popUpView.titleLbl.text = "MyFirstTitle"
view.addSubview(popUpView)
Custom Class
class InfoPopUpView: UIView {
var view: UIView!
#IBOutlet weak var okBtn: UIButton!
#IBOutlet weak var titleLbl: UILabel!
#IBOutlet weak var textView: UITextView!
#IBAction func didTapOkBtn(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("popUpController", object: nil)
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "InfoPopUpView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
}

Swift: Append Custom UIView programmatically

This is a near duplicate of this other SO question, however I'm running into some issues that it makes it appear like I'm not doing it correctly. For reference, I followed this excellent tutorial on creating re-usable views from a custom class and xib file (it's only a few minutes :) and I have no problems at all dragging that into another view onto my storyboard (as demonstrated at the end of the video)
Nevertheless for my case — I'm trying to call my custom class programmatically, and add it as a subview to one of my ScrollView instances.
class MainController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
first.directionalLockEnabled = true
first.pagingEnabled = true
var item = MyCustomView(frame: CGRectMake(0, 0, self.view.frame.width, 200))
self.scrollView.addSubview(item)
}
}
My Custom view looks like this:
import UIKit
class MyCustomView: UIView {
#IBOutlet var view: UIView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var dressTitle: UILabel!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSBundle.mainBundle().loadNibNamed("MyCustomView", owner: self, options: nil)
self.view.frame = bounds
self.addSubview(self.view)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
}
There is an associated .xib file with it too that has the label and image.
So my question is, my view never appears in my ScrollView. I've double checked the constraints on my scroll view... I can even append simple UIView's with obvious visible dimensions CGRectMake(0, 0, 100, 100)... and nothing ever gives. What am I missing?
With some messing around I accidentally got it to work by duplicating the loadFromNib method to a second initializer in the CustomView.swift file. As seen in the video tutorial posted in the original question it shows just one of the initializers.... but in my case I had to add extra code to the init(frame). Eg:
// once with a NSCoder
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSBundle.mainBundle().loadNibNamed("Item", owner: self, options: nil)
self.view.frame = bounds
self.addSubview(self.view)
}
// for this to work programmatically I had to do the same...
override init(frame: CGRect) {
super.init(frame: frame)
NSBundle.mainBundle().loadNibNamed("Item", owner: self, options: nil)
self.view.frame = bounds
self.addSubview(self.view)
}

Subviews of custom UIView are equal to nil even after view is loaded

I'm trying to render a custom view. The problem is that even after the view is loaded, its subviews are still equals to nil.. So not showing and impossible to configure. The custom view is setup through interface builder and all the outlets are linked to the properties you can see below.
Here is the custom UIView code :
import UIKit
class BadgeView : UIView
{
#IBOutlet weak var progressCircleView: CircleProgressView!
#IBOutlet weak var progressionValue: UILabel!
#IBOutlet weak var name: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Tell me if you need more informations.
How do you initialise BadgeView. If you are using storyboard or xib, the view should be initialised like this:
let nib = NSBundle.mainBundle().loadNibNamed("BadgeView", owner: self, options: nil)
let badgeView = nib[0] as! BadgeView

Resources