Swift 3 - Access Variable From Instance Of XIB In Another Class - ios

I have been trying to figure this out for days and haven't had much luck :(
What I want to do is set the variable inside of an instance of a XIB (called BottomNav) that already exists in another ViewController, called "curX". I have come the closest with the following:
class Util: NSObject {
class func loadNib() {
let nib: BottomNav = Bundle.main.loadNibNamed("BottomNav", owner: self, options: nil)!.first as! BottomNav
nib.curX = 10
}
}
Here is the BottomNav Class:
class BottomNav: UIView {
#IBOutlet var view: UIView!
#IBOutlet weak var homeBtn: UIView!
#IBOutlet weak var scroller: UIScrollView!
#IBOutlet weak var scrollerContent: UIView!
var curX = 32
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
Bundle.main.loadNibNamed("BottomNav", owner: self, options: nil)
self.addSubview(self.view)
}
}
This passes the compiler with no warnings, but when it's ran I get a "this class is not key value coding-compliant for the key" error. This usually appears when there's an outlet that no longer exists, but this is definitely not the case, I've tested it with multiple XIBs that are already on the app, and had no problem loading in the first place via storyboards. I only get this error with "loadNibNamed".
Am I even on the right path here? My thought is maybe my Util class doesn't have access to Bundle or something?

class BottomNav: UIView {
#IBOutlet var view: UIView!
#IBOutlet weak var homeBtn: UIView!
#IBOutlet weak var scroller: UIScrollView!
#IBOutlet weak var scrollerContent: UIView!
var curX = 32
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit(){
Bundle.main.loadNibNamed("BottomNav", owner: self, options: nil)
guard let content = view else { return }
content.frame = self.bounds
content.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.addSubview(content)
}
}
And when calling the instance, try the following.
let instance = BottomNav()
instance.curX = 30

Swift 3.0
I Think you get you solution from this. All the best
var view: UIView!
override init(frame: CGRect) {
// call super.init(frame:)
super.init(frame: frame)
// 3. Setup view from .xib file
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
// call super.init(coder:)
super.init(coder: aDecoder)
// 3. Setup view from .xib file
xibSetup()
}
// MARK: - UI setup
func xibSetup() {
let nib = UINib(nibName: "BottomNav", bundle: nil)
view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
// use bounds not frame or it'll be offset
view.frame = bounds
// Make the view stretch with containing view
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(view)
}

Related

How to use custom view with in swift?

I want to add a custom view in TableViewHeader. But when I run the following code it creates a Cycle and app stuck for any user interaction.
import UIKit
class ExpandableView: UIView {
#IBOutlet weak var userImgView: UIImageView!
#IBOutlet weak var userNamelbl: UILabel!
#IBOutlet weak var countLbl: UILabel!
#IBOutlet weak var rightArrowImgView: UIImageView!
var isExpanded = false
var contentView: UIView!
#IBOutlet weak var checkMarkView: UIImageView!
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
public override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpView()
}
private func setUpView() {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "ExpandableView", bundle: bundle)
self.contentView = nib.instantiate(withOwner: self, options: nil).first as? UIView
addSubview(contentView)
contentView.center = self.center
contentView.autoresizingMask = []
contentView.translatesAutoresizingMaskIntoConstraints = true
}
}
I am using it as follows:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let frame = CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 60)
let expandabelView = ExpandableView(frame: frame)
return expandabelView
}
And it shows following error on run.
There may be many other ways, but I recommend you to make your ExpandableView reusable to improve performance.
First of all, simplify your ExpandableView class:
import UIKit
class ExpandableView: UITableViewHeaderFooterView {
#IBOutlet weak var userImgView: UIImageView!
#IBOutlet weak var userNamelbl: UILabel!
#IBOutlet weak var countLbl: UILabel!
#IBOutlet weak var rightArrowImgView: UIImageView!
var isExpanded = false
#IBOutlet weak var checkMarkView: UIImageView!
}
Please do not miss that the superclass is UITableViewHeaderFooterView, not UIView.
Second, check the settings of your ExpandableView.xib:
The Custom View setting of the defined view needs to be ExpandableView.
When you cannot choose ExpandableView from the pull down list, you may need to input manually. Do not forget to check Inherit Module From Target.
The Custom View setting of the File's Owner needs to be empty.
If there's some class already set, remove it manually.
Confirm all Outlets are connected properly to your ExpandableView.
(You should better reconnect them all, after you modified your xib.)
You may need to re-structure your view hierarchy and/or constraints.
Third, modify your view controller holding the table view.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.delegate = self
tableView.dataSource = self
//...
let nib = UINib(nibName: "ExpandableView", bundle: nil)
tableView.register(nib, forHeaderFooterViewReuseIdentifier: "ExpandableView")
tableView.estimatedSectionHeaderHeight = 60
tableView.sectionHeaderHeight = UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let expandabelView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "ExpandableView")
// Frame size should be represented with constraints.
return expandabelView
}
When you create custom UIView, it should follow this if you want to use it init with frame.
class CustomView: UIView {
//This should be contentview of your xib
#IBOutlet var view: UIView!
let nibName = "CustomView"
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
private func xibSetup()
{
Bundle.main.loadNibNamed(nibName, owner: self, options: nil)
self.view.autoresizesSubviews = true
self.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.view)
self.view.frame = self.frame
}
}
Since you need to load view from nib, just load it and then add subview to your view. Then set content view's frame and set autoresizing mask of content view correctly
private func setUpView() {
Bundle.main.loadNibNamed("ExpandableView", owner: self, options: nil)
addSubview(contentView)
contentView.frame = bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}

Exc_Bad_Access While loading CustomView from Xib

IBOutlets in CustomView are coming as nil.
I have created Custom view(xib).
Please find images for more information.
class TextFieldView: UIView {
#IBOutlet var contentView: TextFieldView!
#IBOutlet weak var customTextField: UITextField!
#IBOutlet weak var rightButton: UIButton!
#IBOutlet weak var placeHolderLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override func awakeFromNib() {
super.awakeFromNib()
}
func commonInit()
{
let bundle = Bundle(for: type(of: self))
bundle.loadNibNamed("TextFieldView", owner: self, options: nil)
customTextField.backgroundColor = UIColor.green
NSLog("Called")
}
Still throwing an error with exc_bad_access.(In loadNibNamed line of code)
Swift 3 Version
This is the solution that worked for me. Make sure your file object in IB has the TextFieldView Class assigned to it (not the UIView itself). Also make sure all your IBOutlets are connected to the right place or you risk running into trouble.
class TextFieldView: UIView {
#IBOutlet var contentView: TextFieldView!
#IBOutlet weak var customTextField: UITextField!
#IBOutlet weak var rightButton: UIButton!
#IBOutlet weak var placeHolderLabel: UILabel!
override init (frame: CGRect) {
super.init(frame: frame)
commonInit()
}
//This lets us use TextFieldView in IB
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
Bundle(for: TextFieldView.self).loadNibNamed("TextFieldView", owner: self, options: nil)
//Positioning content so it fills view space
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addSubview(contentView)
}
You need something like:
/// This view must be connected with the main view of the XIB.
#IBOutlet private var view: UIView!
/// This method is used when creating an `ApexView` with code.
override init(frame: CGRect)
{
super.init(frame: frame)
initView()
}
/// This method is called when instantiated from a XIB.
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
initView()
}
private func initView()
{
/// Fill in the name of your XIB. I assumed it `"TextFieldView"`.
Bundle.main.loadNibNamed("TextFieldView", owner: self, options: nil)
self.view.frame = CGRect(x: 0.0, y: 0.0, width: self.width, height: self.height)
self.addSubview(self.view!)
...
}

iOS - Cannot get an #IBDesignable .xib file to show up on storyboard or simulator

I have set the file owner to be UserBannerView.
The UIView in the storyboard that uses this .xib file is also set to be UserBannerView.
I am not getting any errors, yet it won't show up in either the simulator or the storyboard.
import UIKit
#IBDesignable class UserBannerView: UIView {
var view: UIView!
#IBOutlet weak var userCreditsLabel: UILabel!
#IBOutlet weak var userRenewDateLabel: UILabel!
#IBOutlet weak var userImage: circlePP!
#IBOutlet weak var userMembershipBtn: UIButton!
#IBOutlet weak var userNameLabel: UILabel!
var nibName:String = "UserBannerView"
public override init(frame:CGRect){
super.init(frame: frame)
print("UserBannerView init from frame")
setup()
}
public required init?(coder aDecoder:NSCoder){
super.init(coder: aDecoder)
print("UserBannerView init from decode")
setup()
}
override func awakeFromNib() {
print("UserBannerView awakeFromNib")
// setup()
}
func setup() {
print("UserBannerView:setup")
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "UserBannerView", bundle: bundle)
self.view = nib.instantiate(withOwner: self, options: nil).first as! UIView
self.view.frame = bounds
self.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.addSubview(self.view)
}
}
Try implementing your awakeFromNib method and add prepareForInterfaceBuilder method
override func awakeFromNib() {
super.awakeFromNib()
self.setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setup()
}
Also make sure that your class is setted as fileOwner in your xib file
Hope this helps

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 custom control is not showing when being used in view controller (code attached)

When I try to use this custom control within my main view controller, by dragging a UIView onto screen in IB and setting it to "CustomerControlView", it doesn't actually show it.
Question - What is wrong with the code I have here?
Background - So wanting to basically:
a) design a customer control in IB
b) so I'm assuming I create the NIB file and then create a UIView file, so this is what I've done
Screenshot of NIB & swift file
Code
import UIKit
class CustomControlView: UIView {
// #IBOutlet var icon: UIImageView!
// #IBOutlet weak var view: UIView!
#IBOutlet weak var button: UIButton!
#IBOutlet weak var text1: UITextField!
#IBOutlet weak var text2: UITextField!
override init(frame: CGRect) {
print("override init(frame: CGRect) ")
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
print("required init?(coder aDecoder: NSCoder)")
super.init(coder: aDecoder)
// let arr = NSBundle.mainBundle().loadNibNamed("CustomControlView", owner: nil, options: nil)
// let v = arr[0] as! UIView
// self.view.addSubview(v)
}
}
Snapshot showing how I have included the custom view into my main viewController view:
Just add #IBDesignable above the class line to tell Xcode to compile it before showing it in storyboard. There is a great discussion on this subject here: http://nshipster.com/ibinspectable-ibdesignable/
Also, you need to make sure that the NIB is being loaded:
override init(frame: CGRect) {
print("override init(frame: CGRect) ")
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
print("required init?(coder aDecoder: NSCoder)")
super.init(coder: aDecoder)
xibSetup()
}
func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "CustomNumberPad", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}

Resources