Custom UIButton throw multiple design errors - ios

I'm dealing with an XCode error that I don't understand, I've been looking for solutions for days now, and Pods update, XCode restart or anything else doesn't seem to throw the problem away ...
I have a custom class which extends from UIButton :
import Foundation
import UIKit
//#IBDesignable <-- COMMENTED BUT SAME ERRORS ...
class CustomCardButton: UIButton {
var nibName = "CustomCardButton"
#IBOutlet weak var btnImageView: UIImageView!
#IBOutlet weak var btnLabel: UILabel!
#IBInspectable var image: UIImage? {
get {
return btnImageView.image
} set(image) {
btnImageView.image = image
}
}
#IBInspectable var label: String? {
get {
return btnLabel.text
} set(label) {
btnLabel.text = label
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
let view = loadViewFromNib()
view.frame = self.bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
btnImageView.layer.cornerRadius = 60/2
btnImageView.layer.borderColor = UIColor(red: 5/255,
green: 66/255, blue: 38/255, alpha: 1).CGColor
btnImageView.layer.borderWidth = 2
btnLabel.font = UIFont.boldSystemFontOfSize(14.0)
btnImageView.userInteractionEnabled = true
btnLabel.userInteractionEnabled = true
view.userInteractionEnabled = true
addSubview(view)
}
func loadViewFromNib() -> UIButton {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: nibName, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIButton
return view
}
}
It's a well copy-pasted code from SO : Swift, Custom UIButton does not work when click
And my xib file looks like this :
I kept it simple for now, yesterday I tried it with a more complicated (Yeah 2 labels and an imageview Woooh, complicated for xcode ... ) and I had the same error ..
The errors are :
Pre-compilation :
After app launch it crashes and I have this :
30 let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIButton
Thread 1 : EXC_BAD ACCESS (code = 2, address = 0x16fc5bfd0)
31 let view = loadViewFromNib()
Thread 1 : EXC_BAD ACCESS (code = 2, address = 0x16fc5bfd0)
32 setup()
Thread 1 : EXC_BAD ACCESS (code = 2, address = 0x16fc5bfd0)
And I got those errors from 31 to 2840 ...
I saw it was an XCode bug, and we cannot do anything about that, but I really need a Custom button with 2 labels and an ImageVIew ...

You need your UIView's class to be just UIView, and not your custom class directly, your custom class is the File's owner in this case.
You instantiate this view when it loads, so if the custom class will be your class, it will run over and over and over again...
The nib:
Make a UIView with UIButton as sub view (If your view will be some UIView that you change it's class to be UIButton, your IBActions wont work, so better to work with UIButton as sub view)
And your File's owner is your custom class:
Now you can ctrl-drag IBOutlets to you File's owner, just ctrl drag form the File's owner to the desired view:
You can do the same with IBActions to the button
Your code will change a bit:
The custom class as been said is UIView:
#IBDesignable class CustomCardButton: UIView
And the loadViewFromNib method, will return UIView, and not UIButton:
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
}
That's it, you should see your IBDesignable in your storyBoard:
With IBInspectables:

Related

Why is my UILabel nil after creating UIView programmatically from XIB? (code attached)

Question: Why is UILabel nil here (code attached) in init after creating UIView programmatically from XIB? (refer to code below) That is the line label1.text = "TBC - It was updated" throws an error
Background: I want to programmatically create multiple custom views, multiple GCDateView's in this case. I want to use a XIB file to layout the custom view with an associated class to finalise customisations programmatically too, hence here I have a GCDateView.swift and a GCDateView.xib file.
Aside: As a 2nd aside question I note the view I create within the GCDateView from the xib file can't be directly allocated to be the main view (e.g. at the end of init I can't say self = gcDateViewView). Perhaps I need a separate question for this.
From within parent controller/view:
let dv = GCDateView()
GCDateView:
import UIKit
class GCDateView : UIView {
#IBOutlet weak var label1: UILabel!
func commonInit() {
// Programmtically use XIB file
if self.subviews.count == 0 {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "GCDateView", bundle: bundle)
let gcDateViewView : UIView = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
gcDateViewView.frame = self.bounds
gcDateViewView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(gcDateViewView)
}
**label1.text = "TBC - It was updated" // ** ERROR: label1 was Nil ****
}
override init(frame: CGRect) {
NSLog("GCDateView: Init frame")
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
NSLog("GCDateView: Init decoder")
super.init(coder: aDecoder)
commonInit()
}
}
IBOutlet instances are not initialized in init?(coder aDecoder: NSCoder).
This process is done in separate step.
There is method awakeFromNib where it's guaranteed that all IBOutlet instances are initialized:
override func awakeFromNib() {
super.awakeFromNib()
label1.text = "TBC - It was updated" // Won't crash.
}
To solve your second problem (i.e. avoid adding another instance of self type as self subview) I recommend to create class method that will create new instance of GCDateViewby loading it from xib.
Here is updated code:
import UIKit
class GCDateView : UIView {
#IBOutlet weak var label1: UILabel!
class func loadFromXIB() -> GCDateView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "GCDateView", bundle: bundle)
let gcDateView = nib.instantiate(withOwner: self, options: nil)[0] as! GCDateView
gcDateView.frame = self.bounds
gcDateView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
gcDateView.label1.text = "TBC - It was updated"
return gcDateView
}
}
Usage:
let dateView = GCDateView.loadFromXIB()

Why is my SurperView unresponsive when I remove a UIView SubView?

I made a subview appear on my iOS app when a button is pressed. Then, I call a custom UIView from its class, menuView, and display that UIView on my main storyboard
The menuView appears fine on my Main Storyboard, but once I remove the subview, my SuperView (original view for main class for main view controller) is unresponsive, and I can't interact with anything.
What's wrong?
My Custom "menuView" UIView Class:
import UIKit
#IBDesignable class menuView: UIView {
var view:UIView!
#IBOutlet weak var label: UILabel!
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "menuView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
addSubview(view)
}
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
xibSetup()
}
}
Main View Controller Class:
var myMenuView:menuView!
#IBOutlet weak var menuButtonOutlet: UIBarButtonItem!
#IBAction func buttonPressed(sender: AnyObject) {
if menuButtonOutlet.title == "Menu" {
if (myMenuView != nil) {
self.myMenuView.view.removeFromSuperview()
}
self.myMenuView = menuView(frame: self.view.bounds)
self.myMenuView.alpha = 0.75
self.view.addSubview(myMenuView)
menuButtonOutlet.title = "Back"
} else {
self.myMenuView.view.removeFromSuperview()
menuButtonOutlet.title = "Menu"
}
}
Short answer:
self.myMenuView.view.removeFromSuperview()
should become
self.myMenuView.removeFromSuperview()
in both places you have this line in buttonPressed.
Explanation:
Your myMenuView is a container in which you place the view you instantiate from your xib. In buttonPressed you remove only this inner(xib) view inside myMenuView and not myMenuView itself. Thus myMenuView remains on screen and swallows all the touches.

Swift, Custom UIButton does not work when click

I have a custom UIButton created with xib file. When i use my custom button on my view, it does not work when i press to it.
My RoundBtn.swift file:
import UIKit
#IBDesignable class RoundBtn: UIButton {
var nibName = "RoundBtn"
#IBOutlet weak var btnImageView: UIImageView!
#IBOutlet weak var btnLabel: UILabel!
#IBInspectable var image: UIImage? {
get {
return btnImageView.image
} set(image) {
btnImageView.image = image
}
}
#IBInspectable var label: String? {
get {
return btnLabel.text
} set(label) {
btnLabel.text = label
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
let view = loadViewFromNib()
view.frame = self.bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
btnImageView.layer.cornerRadius = 60/2
btnImageView.layer.borderColor = UIColor(red: 5/255,
green: 66/255, blue: 38/255, alpha: 1).CGColor
btnImageView.layer.borderWidth = 2
btnLabel.font = UIFont.boldSystemFontOfSize(14.0)
btnImageView.userInteractionEnabled = true
btnLabel.userInteractionEnabled = true
view.userInteractionEnabled = true
addSubview(view)
}
func loadViewFromNib() -> UIButton {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: nibName, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIButton
return view
}
}
My RoundBtn.xib file:
View where i used my custom button:
I enabled userInteractionEnabled on all view components. When i click to my custom button, it does not work. I tried by defining on click programmatically and by defining action segue (show).
#IBAction func myCartBtnPressed(sender: AnyObject) {
print("my cart btn pressed")
}
I think this is caused because you're adding a couple of UIViews subviews to your current UIButton with userInteractionEnabled, which means that they're handling the user input.
If you do:
view.isUserInteractionEnabled = false
The RoundBtn itself will get all the touch events, instead of this UIViews that you have on top.
Referring to #fdiaz's answer.
Swift 4 notation.
Conversely, if you attach some UIButton (or user interactive view) to an UIImageView, you must turn its isUserInteractionEnabled = true to let the touches go through the hierarchy up down to the UIButton.

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

How to load custom UIView from xib using Swift 2?

The task is to create custom UIView that loads UI from .xib file using Swift. How do I do that?
I tried to do that using the code:
import UIKit
#IBDesignable class CustomView: UIView {
var view: UIView!
#IBOutlet weak var label: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
func xibSetup() {
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: "CustomView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
}
It runs, but crashes with EXC_BAD_ACCESS and shows me the message:
warning: could not load any Objective-C class information. This will significantly reduce the quality of type information available.
Actually, I need to translate code given here http://qnoid.com/2013/03/20/How-to-implement-a-reusable-UIView.html to Swift 2.0, but I have no idea how to do this.
Try
let yourView = NSBundle.mainBundle().loadNibNamed("youViewNibName", owner: self, options: nil).first as! YourView
Do it in your ViewController class, then you can access the view in required init with coder, where you should call xibSetup().
Is it correct, that your class is called CustomView and you're loading CustomView.xib where the class for the view in xib is CustomView?
If that is so you probably do not understand what you're doing

Resources