xib convert to programmatically view - swift

I need to use custom views instead of views created with xib file.
How do I convert and use nib/xib file to programmatically view ?
class CustomCell: UITableViewCell {
var item: ViewModelItem? {
didSet {
titleLabel?.text = item?.title
}
}
override func awakeFromNib() {
super.awakeFromNib()
selectionStyle = .none
}
#IBOutlet weak var titleLabel: UILabel?
static var nib:UINib {
return UINib(nibName: identifier, bundle: nil)
}
static var identifier: String {
return String(describing: self)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
accessoryType = selected ? .checkmark : .none
}
}
I added it instead of xib. How should i send this?
I want the xib file to be programmatically added and controllable.
I want the xib file to be programmatically added and controllable.
class CustomCellView: UIView {
convenience init(frame: CGRect,checkboxTag:Int?) {
self.init(frame: frame)
self.addCustomView(checkboxTag: checkboxTag)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func addCustomView(checkboxTag : Int?) {
var checkbox: UIButton = ClickingCheckbox()
if((checkboxTag) != nil)
{
checkbox.tag = checkboxTag!
}
self.addSubview(checkbox)
}
}
class ClickingCheckbox: UIButton {
convenience init() {
self.init(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
self.setImage(UIImage(named: "unfilled.png"), for: UIControlState.selected)
self.setImage(UIImage(named: "filled.png"), for: UIControlState.normal)
self.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
}
#objc private func buttonTapped() {
self.isSelected = !self.isSelected
}
}

When you are using view from Nib, it calls AwakeFromNib() method, custom views from code are using Init() methods.
To convert you Xib to programmatically code you need to setup all UI inside Init() method and move all initialConfiguration methods from AwakeFromNib() to Init()

Related

How to navigate push view controller from UIView in Swift

How to navigate PushViewController from UIView class to New View Controller, Till now what I tried with delegate/ protocol but still no success from button click, below is the swift code what I tried to navigate from uiview class
protocol MyViewDelegate {
func didTapButton()
}
class Ticket : UIView {
static let instance = Ticket()
#IBOutlet var parentView: UIView!
#IBOutlet weak var ticketView: UIView!
var delegate : MyViewDelegate?
override init (frame: CGRect) {
super.init(frame: frame)
Bundle.main.loadNibNamed("Ticket", owner: self, options: nil)
}
#IBAction func btnHistoryTicket(_ sender: ThemeButtonTicket) {
}
#IBAction func btnNewTicket(_ sender: ThemeButtonTicket) {
func buttonTapAction() {
delegate?.didTapButton() // Expect to navigate
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showTicket(){
UIApplication.shared.windows.first{$0.isKeyWindow}?.addSubview(parentView)
}
}
class ViewController: UIViewController, MyViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let ticket = Ticket()
ticket.delegate = self
}
func didTapButton() {
let vc = kMainStoryboard.instantiateViewController(withIdentifier: "SubmitTicketVC") as! SubmitTicketVC
self.navigationController?.pushViewController(vc, animated: true)
}
}

Call to IBOutlet in custom UIView from another class

I am looking for a way to refer to the IBOutlet variable in a custom class UIView, from another class. I found layoutSubviews, but each change only works on the first call, and not on each subsequent call. Thanks for help!
ViewController class:
var SB = StatusBar()
SB.update(1)
SB.update(2)
SB.update(3)
StatusBar class:
class StatusBar: UIView {
#IBOutlet var view: UIView!
#IBOutlet weak var label: UILabel!
var ActualStatus: Int!
required init?(coder: NSCoder) {
super.init(coder: coder)
xibSetup()
}
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
func update(status) {
ActualStatus = status
self.layoutSubviews()
}
override func layoutSubviews() {
super.layoutSubviews()
label.text = ActualStatus
}
func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight]
addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of:self))
let nib = UINib(nibName: "StatusBar", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
}
Result: label.text is 1, change only works on the first call

Passing data from UIViewController to Custom UIView with XIB

I have the following swift file which controls a xib file
import UIKit
protocol SelectProfile: class {
func selectionUp(id: Int, selected: Bool)
}
class Component: UIView {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var selection: UIView!
var selected: Bool = false
var profileId: Int = 6
weak var delegate: SelectProfile?
#IBAction func selProfile(_ sender: Any) {
selected = !selected
selection.isHidden = !selected
delegate?.selectionUp(id: profileId, selected: selected)
}
let nibName = "Component"
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view)
selection.isHidden = true
name.text = String(profileId)
}
func loadViewFromNib() -> UIView? {
let nib = UINib(nibName: nibName, bundle: nil)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}
In my UIViewController where I want to interact with the component I have the following code (shortened for brevity)
import UIKit
class Home: UIViewController, SelectProfile {
#IBOutlet weak var profileComponent: Component!
func selectionUp(id: Int, selected: Bool) {
print(id, selected)
}
override func viewDidLoad() {
profileComponent.delegate = self
profileComponent.profileId = 2
}
}
My delegate works nicely and I'm able to change the values for IBOutlets but I can't pass a value to variables. This line doesn't pass the data to my xib profileComponent.profileId = 2.
It seems like I have an issue with initialization of the xib but I don't know how to fix it.
The viewDidLoad of controller is called after view's init, so when you change profileId as you do, it is changed, but not reflected anywhere.
I assume the following, at least, missed:
var profileId: Int = 6 {
didSet {
name.text = String(profileId)
// call delegate here if needed as well
}
}

addTarget() does not work on custom extensions

I made a custom UIView() and i'm trying to add a okButton to it.
However. I have added the button but when showing my UIView in one of my ViewControllers, But the button won't work. This is my code:
class Luna: UIView {
let luna = UIView()
let okButton = UIButton()
typealias completionHandler = (_ success:Bool) -> Void
// Not yet implemented
open var touchOutsideToHide: Bool = true;
private var screenWidth: CGFloat {
return UIScreen.main.bounds.width
}
override init(frame: CGRect) {
super.init(frame: frame)
okButton.setTitleColor(.blue, for: .normal)
okButton.setTitle("Okay", for: .normal)
okButton.titleLabel?.font = UIFont(name: "avenirnext-demibold", size: 16)
let gesture:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.pressButton(_:)))
okButton.addGestureRecognizer(gesture)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
//The target function
#objc func pressButton(_ sender: UIButton){ //<- needs `#objc`
// .. //
print("btn hit")
}
I do not get btn hit in the console and the tap isn't registered
the button is visible, it's added as a subview:
Screenshot
Button have default touch gesture, you don't need to add it. just add target as follow to fix your issue. Create protocol of your customer class and declare delegate for it in same class as follow.
protocol LunaDelegate: class {
func okButtonTapped(sender: UIButton)
}
class Luna: UIView {
let luna = UIView()
let okButton = UIButton()
typealias completionHandler = (_ success:Bool) -> Void
weak var delegate: LunaDelegate?
// Not yet implemented
open var touchOutsideToHide: Bool = true;
private var screenWidth: CGFloat {
return UIScreen.main.bounds.width
}
override init(frame: CGRect) {
super.init(frame: frame)
okButton.setTitleColor(.blue, for: .normal)
okButton.setTitle("Okay", for: .normal)
okButton.titleLabel?.font = UIFont(name: "avenirnext-demibold", size: 16)
okButton.addTarget(self, action: #selector(pressButton(sender:)), for: .touchUpInside)
}
#IBAction func pressButton(sender: UIButton) {
if let selfDelegate = self.delegate {
selfDelegate.okButtonTapped(sender: sender)
}
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Then after implement your code as follow to use your custom view. When your button will pressed then delegate method will be call in your view controller.
Usage:
class LunaViewController: UIViewController, LunaDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let luna = Luna(frame: CGRect.init(x: 100, y: 100, width: 100, height: 40))
luna.delegate = self
self.view.addSubview(luna)
}
// MARK:- Luna Delegate
func okButtonTapped(sender: UIButton) {
print("OK Button Pressed From Luna View")
}
}
This is the proper solution which I have already using in my current project. I hope this will help you.

Set CustomView delegate before init

Although I could create a func setImages () in my CustomView class and call this after initializing myCustomView, I would like to understand if there's a cleaner way to set a view's delegate so that it is accessible when being initialized.
My main ViewController contains
class Main: UIViewController, CustomViewDelegate {
var imagesArray:[UIImage] = [Image1,Image2,Image3,Image4,Image5]
var myCustomView = CustomView()
override func viewDidLoad() {
super.viewDidLoad()
myCustomView.delegate = self
myCustomView = CustomView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
//this causes init of CustomView, but delegate is now nil and button images don't load
}
}
My CustomView file contains
var buttonsArray = [Button1,Button2,Button3,Button4,Button5]
override init(frame: CGRect) {
super.init(frame : frame)
for n in 0..< buttonsArray.count {
buttonsArray[n].setImage(delegate?.imagesArray[n], for: .normal)
}
}
You can create a new initializer which takes a frame and a delegate type and set the delegate before you set images to buttons
init(frame: CGRect,sender: CustomViewDelegate) {
super.init(frame : frame)
self.delegate = sender
for n in 0..< buttonsArray.count {
buttonsArray[n].setImage(delegate?.imagesArray[n], for: .normal)
}
}
For that, you have to confirm your viewController for the delegate(obviously).Call your customView like this in viewController:
class ViewController: UIViewController, CustomViewDelegate {
var myCustomView: CustomView!
override func viewDidLoad() {
myCustomView = CustomView(frame: self.view.bounds, sender: self)
}
}
hope it helps!