how to add UIView with code? Without using storyboard - swift

I am a beginner and am only able to add elements via storyboard.
Now I would like to know how to add UIView code to the screen without using a storyboard.
I need to place it at the very top, full width of the screen, replacing the NavigationBar with a UIView
import UIKit
class ChatsNavBar: UIView {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var statusLabel: UILabel!
#IBOutlet weak var imageUser: UIImageView!
#IBOutlet weak var backButton: UIButton! {
didSet {
backButton.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
#IBOutlet weak var navView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
let bundle = Bundle(for: ChatsNavBar.self)
bundle.loadNibNamed("ChatsNavBar", owner: self, options: nil)
navView.translatesAutoresizingMaskIntoConstraints = false
addSubview(nameLabel)
addSubview(navView)
addSubview(statusLabel)
addSubview(imageUser)
addSubview(backButton)
nameLabel.frame = self.bounds
nameLabel.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
#IBAction func backAction(_ sender: Any) {
}
}
class ChatsViewController: MessagesViewController {
init(user: MUser, chat: MChat) {
self.user = user
self.chat = chat
super.init(nibName: nil, bundle: nil)
title = "\(chat.friendUsername) \(chat.friendUserSurname)"
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
configureMessageInputBar()
let navController = UIView()

Here is how you can place it over entire screen
let viewOverLay = ChatsNavBar()
navigationController.view.addSubview(viewOverLay)
viewOverLay.frame = navigationController.navigationBar.bounds

Related

I can't present a segue to another VC from a UIView class

I have a UIView class which i set it up as a pop-up that user needs to write a price and press a button to pay that amount, The problem is I can't set up the button to make a segue to another VC which I will use to make the payment.
I am getting errors which doesn't confront to the UIView Class because there is no method matching...
Code of popUpView class:
class popUpView: UIView, UITextFieldDelegate {
#IBOutlet weak var popUpViewInterface: UIView!
#IBOutlet weak var infoLabelPopUp: UILabel!
#IBOutlet weak var closeButtonPopUp: UIButton!
#IBOutlet weak var priceButtonPopUp: UIButton!
#IBOutlet weak var priceInfoPopUp: UILabel!
#IBOutlet weak var priceTextFieldPopUp: UITextField!
#IBOutlet weak var payNowPopUp: UIButton!
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup(frame:CGRect(x: 0, y: 0, width: frame.width, height: frame.height))
}
func xibSetup(frame: CGRect){
let view = loadXib()
view.frame = frame
addSubview(view)
popUpViewInterface.layer.cornerRadius = 10
}
func loadXib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "popUpView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil).first as? UIView
return view!
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
priceTextFieldPopUp.resignFirstResponder()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
priceTextFieldPopUp.resignFirstResponder()
return true
}
func cornerRadiusSetUp() {
let radius = 5
priceTextFieldPopUp.layer.cornerRadius = CGFloat(radius)
}
#IBAction func payNowToWebView(_ sender: Any) { // This is where the segue needs to happen but I am getting different kind of errors.
let myColor = UIColor.red
priceTextFieldPopUp.layer.borderColor = myColor.cgColor
priceTextFieldPopUp.layer.borderWidth = 1.0
}
}

Can't add a UIView loaded from XIB as a subView of a ViewController view

I want to implement a popup notification into my app when data was being updated successfully or not. To do that I:
created a .xib file: Screenshot
created a class where I load that NIB:
import UIKit
class PopupView: UIView {
static let instance = PopupView()
#IBOutlet weak var backgroundView: UIView!
#IBOutlet weak var popupView: UIVisualEffectView!
#IBOutlet weak var symbol: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
Bundle.main.loadNibNamed("PopupView", owner: self)
popupView.layer.cornerRadius = 20
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showPopup(title: String, message: String, symbol: UIImage, on viewController: UIViewController) {
self.titleLabel.text = title
self.descriptionLabel.text = message
self.symbol.image = symbol
guard let targetView = viewController.view else { return }
backgroundView.frame = targetView.bounds
targetView.addSubview(backgroundView)
}
In the above class I created a showPopup method where defined a backgroundView frame to be equal to ViewController bounds.
When I call that method in desired ViewController I receive the behaviour where my popupView shows itself and then went off the screen straight away (black area in the GIF): GIF
Would you be so kind to help me understand and fix the reason why the popupView went off the screen and not just equal to a ViewController bounds.
The Code after Shawn Frank's answer
PopupView class:
import UIKit
class PopupView: UIView {
#IBOutlet weak var popupView: UIVisualEffectView!
#IBOutlet weak var symbol: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func configure() {
if let views = Bundle.main.loadNibNamed("PopupView", owner: self) {
guard let view = views.first as? UIView else { return }
view.frame = bounds
addSubview(view)
}
}
func showPopup(title: String, message: String, symbol: UIImage, on viewController: UIViewController) {
titleLabel.text = title
descriptionLabel.text = message
self.symbol.image = symbol
popupView.layer.cornerRadius = 20
popupView.clipsToBounds = true
viewController.view.addSubview(self)
UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveLinear) {
self.popupView.center.y += 40
} completion: { _ in
UIView.animate(withDuration: 0.3, delay: 3.0, options: .curveLinear) {
self.popupView.center.y -= 80
} completion: { _ in
self.popupView.removeFromSuperview()
}
}
}
}
Call in desired ViewController:
let width = CGFloat(10)
let midX = (self.view.frame.width - width) / 2.0
let frame = CGRect(x: midX, y: 0, width: width, height: 135)
let popup = PopupView(frame: frame)
popup.showPopup(title: "Test", message: "Tested super test", symbol: UIImage(named: "checkmark.circle.fill")!, on: self)
Constraints in xib: Screenshot
Current result: GIF
I do not use XIB and storyboard too much these days so I also had to refresh my memory and I used this tutorial
This is the custom PopupView class, I prefer not to use singleton for this but it is my personal preference
class PopupView: UIView {
private let xibName = "PopupView"
#IBOutlet weak var visualEffectBackground: UIVisualEffectView!
#IBOutlet weak var titleLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func configure() {
if let views = Bundle.main.loadNibNamed(xibName, owner: self),
let popupView = views.first as? UIView {
popupView.frame = bounds
addSubview(popupView)
}
}
func showPopup(title: String,
on viewController: UIViewController) {
guard let targetView = viewController.view else { return }
titleLabel.text = title
layer.cornerRadius = 20
clipsToBounds = true
targetView.addSubview(self)
}
}
XIB is set up like this:
Then in the view controller:
#IBAction func didTapPopUp(_ sender: Any) {
// Give your own width, height etc
let width = CGFloat(180)
let midX = (view.frame.width - width) / 2.0
let frame = CGRect(x: midX, y: 100, width: width, height: 80)
let popup = PopupView(frame: frame)
popup.showPopup(title: "Hello", on: self)
}
Gives me this result:
Update based on artexhibit (OPs) comments
The custom view can get its frames in 3 ways that I can think of:
Directly from XIB
By providing frame during programmatic initialization
From the frame set when creating the view in storyboard
To make the view work for all these scenarios, we should not do any frame adjustment inside the custom view and leave it to the parent / container / superview
So I made the following changes to work for all scenarios:
class PopupView: UIView {
private static let xibName = "PopupView"
#IBOutlet weak var visualEffectBackground: UIVisualEffectView!
#IBOutlet weak var titleLabel: UILabel!
init() {
super.init(frame: .zero)
configure()
}
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func initializeWithNib() {
var popupView = UIView()
if let views = Bundle.main.loadNibNamed(PopupView.xibName,
owner: self),
let view = views.first as? UIView {
popupView = view
}
frame = popupView.bounds
addSubview(popupView)
}
private func initializeWithFrame() {
if let views = Bundle.main.loadNibNamed(PopupView.xibName,
owner: self),
let popupView = views.first as? UIView {
popupView.frame = bounds
addSubview(popupView)
}
}
private func configure() {
if frame == .zero {
initializeWithNib()
}
else {
initializeWithFrame()
}
layer.cornerRadius = 20
clipsToBounds = true
}
func showPopup(title: String,
on viewController: UIViewController) {
guard let targetView = viewController.view else { return }
print(frame)
titleLabel.text = title
targetView.addSubview(self)
}
}
Now this has the flexibility to work with all 3 scenarios:
// In code
#IBAction func didTapPopUp(_ sender: Any) {
// Default initializer
let popup = PopupView()
var originX = (view.frame.width - popup.frame.width) / 2.0
popup.frame.origin = CGPoint(x: originX, y: 100)
popup.showPopup(title: "Hello", on: self)
// Frame initializer
let popupWidth: CGFloat = 200
let popupHeight: CGFloat = 100
originX = (view.frame.width - popupWidth) / 2.0
let originY = (view.frame.height - popupHeight) / 2.0
let popupFrame = CGRect(x: originX,
y: originY,
width: popupWidth,
height: popupHeight)
let popupTwo = PopupView(frame: popupFrame)
popupTwo.showPopup(title: "Frames", on: self)
}
From storyboard
This gives the following results

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

Button on subview untouchable after add to view program

I try to add a subview to a view, but after added, the button on subview is untouchable. I have check the status of button, user interaction is enable. and after add subview, the UI of subview are show up correctly.
The subview class:
class ErrorInfoView: UIView {
#IBOutlet var containerView: UIView!
#IBOutlet weak var messageLabel: UILabel!
var callback: (()->())?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
Bundle.main.loadNibNamed("ErrorInfoView", owner: self, options: nil)
containerView.frame = self.bounds
containerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
containerView.backgroundColor = UIColor.clear
addSubview(containerView)
}
func configure(message: String, callback: #escaping ()->()) {
self.messageLabel.text = message
self.callback = callback
}
#IBAction func pressRetryButton(_ sender: UIButton) {
self.callback?()
}
}
The parent view:
let errorView = ErrorInfoView(frame: self.tableViewContainer.frame)
errorView.configure(message: error) {
// do something, but not be called
}
self.tableViewContainer.addSubview(errorView)
self.tableViewContainer.bringSubviewToFront(errorView)
errorView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = errorView.centerXAnchor.constraint(equalTo: self.tableViewContainer.centerXAnchor)
let verticalConstraint = errorView.centerYAnchor.constraint(equalTo: self.tableViewContainer.centerYAnchor)
self.tableViewContainer.addConstraints([horizontalConstraint, verticalConstraint])

Custom NSView with NSStackView

I am trying to implement NSStackView with always the same custom view. So I have define my xib file, linked everything inside my class, associated the class, ... but I am sure I am missing something because the content inside the view don't appear. Thx very much for your help.
My storyboard:
My xib file:
The result when I run the below code:
My file is like :
class CustomView: NSView, NSTableViewDelegate, NSTableViewDataSource {
#IBOutlet weak var segmentControlOutlet: NSSegmentedControl!
#IBOutlet weak var tableViewOutlet: NSTableView!
}
class ViewController: NSViewController, NSStackViewDelegate {
#IBOutlet weak var stackViewOutlet: NSStackView!
override func viewDidLoad() {
super.viewDidLoad()
stackViewOutlet.delegate = self
let newView = CustomView()
stackViewOutlet.addArrangedSubview(newView)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
I have found the answer, you need to initiate properly the view. To do so :
class CustomView: NSView, NSTableViewDelegate, NSTableViewDataSource {
#IBOutlet var viewOutlet: NSView!
#IBOutlet weak var segmentControlOutlet: NSSegmentedControl!
#IBOutlet weak var tableViewOutlet: NSTableView!
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
Bundle.main.loadNibNamed("CustomView", owner: self, topLevelObjects: nil)
addSubview(viewOutlet)
viewOutlet.frame = self.bounds
viewOutlet.autoresizingMask = [.height, .width]
}
required init?(coder: NSCoder) {
super.init(coder: coder)
Bundle.main.loadNibNamed("CustomView", owner: self, topLevelObjects: nil)
addSubview(viewOutlet)
viewOutlet.frame = self.bounds
viewOutlet.autoresizingMask = [.height, .width]
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
}
Please take car that when you load the nib name, "CustomView" is the name of your xib file ;)