How to create custom Xib UIView programatically in swift - ios

I need to create custom xib uiview programatically
Here i dont want to add any view in owner viewcontroller to give its class name as ReusableContainerview and show that... because i have around 30 viewcontrollers so i cant add view in all my 30 viewcontrollers
i just want to show custom view in my homevc without adding uiview in storybard but how to call programatically in homevc to show custom view.. please guide me
code: code for creating custom view, to use in all my screens
import UIKit
class ReusableContainerview: UIView {
#IBOutlet weak var containerView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit(){
var viewFromXib = Bundle.main.loadNibNamed("ReusableContainerview", owner: self, options: nil)![0] as! UIView
viewFromXib.frame = self.bounds
viewFromXib = ReusableContainerview(frame: CGRect(x: 0, y: 0, width: bounds.width * 0.9, height: 300))
addSubview(viewFromXib)
}
}
for eg i would like to call custom view like this in my home page but in my home screen i am not getting reuseView.. how to show reuseView in home without adding uiview in storyboard.. please guide me
import UIKit
var reuseView: ReusableContainerview?
class HomeVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
reuseView?.isHidden = false
}
}

Related

Instantiating a class and using properties makes app crash

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!

Reusable .xib + cocoa class components, how to auto size and style superview in storyboard?

I have implemented reusable button component using combination of .xib and cocoa class following this guide
It works, but there is an issue. In order to use it in one of my main View Storyboards I have to first drag in a normal view (referenced as superview in question title) and then apply my Button class, to make it a button.
This works, but initial view height and width alongside its white background persist, so I have to always manually rewrite those when I use my component, which in itself results in poor reusability.
Ideally, I'd like to drag in a view, set it to Button class and thats it, that view should instantly take buttons height and width and have transparent background. Is something like this achievable?
To light more context on this issue here are few useful bits of my implementation
1. Reusable component made as a .xib view and its own cocoa class
2. Contents of Button.swift
import UIKit
#IBDesignable class Button: UIView {
#IBOutlet var contentView: UIView!
#IBOutlet weak var label: UILabel!
#IBInspectable var buttonLabel: String? {
get {
return label.text
}
set(buttonLabel) {
label.text = buttonLabel
}
}
override init(frame: CGRect) {
super.init(frame: frame)
componentInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
componentInit()
}
private func componentInit() {
let bundle = Bundle(for: Button.self)
bundle.loadNibNamed("Button", owner: self, options: nil)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
}
3. Example usage (inside one of my main view storyboards) demonstrating how ordinary view is turned into a button, but has issues with height, width and background color
P.S ^ if its hard to tell what is going on in a gif above, I basically drag UIView into a story board and give it custom class attribute of Button, this turns that view into a button.
EDIT: Just to make it clearer, my question is: Can I apply width, height and transparent colour to my XIB's parent / super view? End goal here is to just drag in a view onto storyboard, give it custom class of a Button and thats it, it should be sized properly and have transparent background, as opposed to how it is at the moment (view doesn't get sized as button and has white background)
You have to pin your subviews in Button properly and also in Main.storyboard. Then your custom view will autosize. And clear color is working.
import UIKit
#IBDesignable
class Button: UIView {
#IBOutlet var contentView: UIView!
#IBOutlet weak var label: UILabel!
#IBInspectable
var backColor: UIColor = .clear {
didSet {
backgroundColor = backColor
contentView.backgroundColor = backColor
}
}
override var backgroundColor: UIColor? {
get {
return backgroundColor
} set {
}
}
#IBInspectable
var buttonLabel: String? {
get {
return label.text
}
set(buttonLabel) {
label.text = buttonLabel
}
}
override init(frame: CGRect) {
super.init(frame: frame)
componentInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
componentInit()
}
private func componentInit() {
let bundle = Bundle(for: Button.self)
bundle.loadNibNamed("Button", owner: self, options: nil)
addSubview(contentView)
contentView.frame = bounds
backgroundColor = backColor
contentView.backgroundColor = backColor
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
// for static height
contentView.heightAnchor.constraint(equalToConstant: 70).isActive = true
}
}
To ensure you CustomView would size itself properly, you can use bottom constraint >= 0. After testing reset to equals.

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: Add a xib into a UIView

I would like to add a custom UIView. The class definition of my custom view is:
class UserCoinView: UIView {
#IBOutlet weak var userName: UILabel!
#IBOutlet weak var coinView: UIView!
#IBOutlet weak var coinAmount: UILabel!
#IBOutlet weak var coinIcon: UILabel!
override func drawRect(rect: CGRect) {
let smartCoins = SmartShopperUtil.getSmartShopper().smartCoins
if smartCoins != nil && smartCoins >= 0 {
coinAmount.text = String(smartCoins!)
coinView.backgroundColor = SmartShopperUtil.getSmartCoinBackgroundColor(SmartShopperUtil.getSmartShopper().smartCoins!)
}
userName.text = SmartShopperUtil.getSmartShopperNameWithFullName(SmartShopperUtil.getSmartShopper().name)
coinIcon.text = AEVIcons.AEV_SMART_COIN
}
}
I have added a View in the ViewController I want to add this view, and I have set the custom class of this view as UserCoinView. After that, I have made a connection to the ViewController, and in this ViewController I have no idea what to do in order to display my custom UIView.
Thanks in advance for your help.
There is couple of ways you can do this.
Add as subview programmatically.
If you use autolayout, better place for that is viewDidLayoutSubviews method.
var myCustomView: UserCoinView? // declare variable inside your controller
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if myCustomView == nil { // make it only once
myCustomView = NSBundle.mainBundle().loadNibNamed("UserCoinView", owner: self, options: nil).first as? UserCoinView
myCustomView.frame = ...
self.view.addSubview(myCustomView) // you can omit 'self' here
// if your app support both Portrait and Landscape orientations
// you should add constraints here
}
}
Add as subview in InterfaceBuilder.
You simply need put an empty view to you controller inside the storyboard, and assign your class for this view in Identity Inspector. After that, you can drag-n-drop outlets to your controller classes if you need one.
As for me, I prefer the second method because you don't need to hardcode frame / create constraints programmatically, just add autolayout.
You can try This
override init(frame: CGRect) {
super.init(frame: frame)
loadViewFromNib ()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib ()
}
func loadViewFromNib() {
let view = UINib(nibName: "CreditCardExperyView", bundle: NSBundle(forClass: self.dynamicType)).instantiateWithOwner(self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.addSubview(view);
}
// Call subview
let creditCardView : CreditCardExperyView = CreditCardExperyView(frame: CGRect(x: 0, y: UIScreen.mainScreen().bounds.size.height - 280, width: UIScreen.mainScreen().bounds.size.width, height: 280))
selfView.addSubview(creditCardView)
Add it to a UIViewController's or UITableViewController's content view (or some other view in the view controller's view hierarchy) as a subview.
in Latest swift -> for example:
let headerView = Bundle.main.loadNibNamed("SectionHeaderView", owner:
self, options: nil)?.first as? SectionHeaderView
self.view.addSubview(headerView!)
headerView?.frame = CGRect(x:0, y: 0, width: view.frame.width, height: 50.0)
I'm pretty new myself, but this should work.
Adding:
viewControllerName.addSubview(userCoinView)
Removing:
userCoinView.removeFromSuperview()
You can also use generic function. For project use make it global
struct GenericFunctions {
static func addXIB<T>(xibName: String) -> T? {
return Bundle.main.loadNibNamed(xibName, owner: self, options: nil)?.first as? T
}
}
Use this generic function like:-
if let cell: StudentStatusTableViewCell = GenericFunctions.addXIB(xibName: "StudentStatusTableViewCell") {
return cell
} else {
return UITableViewCell()
}
Benefit of generic is you can use this function for adding View, Tableviewcell and any other element. make sure you are using type in call like let cell: StudentStatusTableViewCell otherwise compiler won't infer the type.
Happy coding :)

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)
}

Resources