Remove nib from superview - ios

I am simply trying to remove a view from it's superview when a respective button is pressed, but am failing to do so.
The view is a custom nib I created, called RequestLocationView
Called from viewDidLayoutSubviews:
func initBeaconServices() {
locationManager = CLLocationManager()
if locationManager.responds(to: #selector(CLLocationManager.requestWhenInUseAuthorization)) {
requestView = RequestLocationView(frame: UIScreen.main.bounds)
requestView.setupView()
requestView.alpha = 0
requestView.delegate = self
self.navigationController?.view.addSubview(requestView)
UIView.animate(withDuration: 0.5, delay : 1, options: .curveEaseOut, animations: {
self.requestView.alpha = 1
}, completion: nil)
}
locationManager.delegate = self
locationManager.pausesLocationUpdatesAutomatically = true
let uuid = UUID(uuidString: "869A6E2E-AE14-4CF5-8313-8D6976058A7A")
// Create the beacon region to search for with those values.
beaconRegion = CLBeaconRegion(proximityUUID: uuid!, identifier: "com.dejordan.Capiche")
}
Here's the code for the RequestView:
class RequestLocationView: UIView {
#IBOutlet weak var acceptButton: UIButton!
#IBOutlet weak var rejectButton: UIButton!
var lView: UIView!
weak var delegate: HandleLocationPermissionDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
private func xibSetup() {
lView = loadViewFromNib()
lView.frame = bounds
lView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
lView.translatesAutoresizingMaskIntoConstraints = true
addSubview(lView)
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
let nibView = nib.instantiate(withOwner: self, options: nil).first
return nibView as! UIView
}
func setupView() {
acceptButton.layer.cornerRadius = acceptButton.frame.height / 2
}
func fadeIn(completion: #escaping (Bool) -> Void) {
UIView.animate(withDuration: 1.3, animations: {
self.alpha = 1
}, completion: completion)
}
func fadeOut(completion: ((Bool) -> Void)?) {
UIView.animate(withDuration: 1.3, animations: {
self.alpha = 0
}, completion: completion)
}
#IBAction func acceptPressed(_ sender: UIButton) {
delegate?.allowPermissions()
}
#IBAction func rejecttPressed(_ sender: UIButton) {
delegate?.denyPermissions()
}
}
Here are the delegate methods in the parent viewController:
func allowPermissions() {
requestView.fadeOut {(finished) in
if finished {
self.locationManager.requestWhenInUseAuthorization()
self.requestView.removeFromSuperview()
}
}
}
func denyPermissions() {
requestView.fadeOut {(didFinish) in
self.requestView.removeFromSuperview()
}
}
And this is what happens when you click reject... You can see the background slightly fading but that's it, and the view stays where it is.
I've been scratching my head for a while on this one, and have scoured many Stack Overflow posts to no avail...

It's possible the CLLocationManager code is interacting in a not-so-friendly manner.
If moving the RequestLocationView initialization into viewDidLoad corrects the issue, that's likely the cause.
To get the proper radius on your acceptButton, try adding a layoutSubviews handler to your custom view:
override func layoutSubviews() {
acceptButton.layer.cornerRadius = acceptButton.bounds.height / 2
}

Related

Delegate in UIView

I present modally UIViewController with workout. When user dismiss this VC I need to hold unfinished workout and present small UIView with size exactly the same as UITabBar in all others view controllers. This UIView has a button which should instantiate unfinished workout. The problem is that delegate doesn't work.
class BackToWorkoutButton: UIView {
weak var delegate: BackButtonWorkoutDelegate?
var routine: Routine!
init(frame: CGRect, routine: Routine) {
self.routine = routine
super.init(frame: frame)
customInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func customInit() {
let xibView = Bundle.main.loadNibNamed("BackToWorkoutButton", owner: self, options: nil)?.first as! UIView
xibView.frame = self.bounds
addSubview(xibView)
}
#IBAction func backButtonTapped(_ sender: UIButton) {
// THIS DELEGATE DOESN'T WORK
delegate?.backToWorkout()
}
}
class WorkoutCoordinator {
func instantiateUnfinishedWorkout() {
let window = UIApplication.shared.keyWindow!
guard let tabBar = navigationController.tabBarController else { return print("Tabbar nil") }
let height = tabBar.tabBar.frame.height
let child = NewWorkoutCoordinator(routine: temporaryRoutine, navigationController: navigationController, removeCoordinatorWith: removeChild, workoutViewBarProtocol: self)
let view2 = BackToWorkoutButton(frame: CGRect(x: 0, y: (tabBar.tabBar.frame.origin.y) - height, width: window.frame.width, height: height), coordinator: child, routine: routine)
view2.delegate = self
tabBar.view.addSubview(view2)
}
}

Changing custom view's frame on orientation change in subclass using Swift

I am loading a nib file of a custom UIView Subclass into my viewcontroller. the problem here is , when i change orientation to landscape, the view still shows in portrait frames. I want it's frame to be set in landscape mode also.
below is my code:-
class CustomPopUpView: UIView {
#IBOutlet weak var vWtitle: UIView!
#IBOutlet weak var lblTitle: UILabel!
#IBOutlet weak var lblContent: UILabel!
#IBOutlet weak var vwAlertSub: UIView!
#IBOutlet weak var btnCenter: UIButton!
var view: UIView!
func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(view)
lblTitle.adjustsFontSizeToFitWidth = true
view.backgroundColor = UIColor.black.withAlphaComponent(0.6)
self.showAnimate()
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "CustomPopUpView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
override func layoutSubviews(){
super.layoutSubviews()
print("LayoutSubViews...............")
// view.frame = bounds
}
func showAnimate()
{
self.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.alpha = 0.0;
UIView.animate(withDuration: 0.25, animations: {
self.alpha = 1.0
self.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
});
}
func removeAnimate()
{
UIView.animate(withDuration: 0.25, animations: {
self.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.alpha = 0.0;
}, completion:{(finished : Bool) in
if (finished)
{
self.removeFromSuperview()
}
});
}
init(frame: CGRect, titleTxt:String, contentText: String, leftButtonText:String, rightButtonText:String) {
super.init(frame: frame)
xibSetup()
lblTitle.text = titleTxt
lblContent.text = contentText
if leftButtonText.characters.count > 0 {
btnLeft.isHidden = false
btnLeft.setTitle(leftButtonText,for: .normal)
}
else {
btnLeft.isHidden = true
}
if rightButtonText.characters.count > 0 {
btnRight.isHidden = false
btnRight.setTitle(rightButtonText,for: .normal)
}
else {
btnRight.isHidden = true
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
#IBAction func btnCenterTapped(_ sender: Any) {
self.removeAnimate()
}
Do I need to set the frame once again in layoutsubviews method? Or is there any other method for UIView orientation change? (just like we have for viewcontroller's orientation method "viewWillLayoutSubviews")
I think set the frame once again not correct since auto-layouts came out.
in swift
override func layoutSubviews() {
super.layoutSubviews()
self.setNeedsDisplay()
}
since layoutSubviews() is a View Method .
or you can observe in viewDidLoad NSNotificationCenter.defaultCenter().addObserver(self, selector: "functionThatYouWantTriggeredOnRotation", name:
UIDeviceOrientationDidChangeNotification, object: nil)
Alternatively you can just provide the override for viewWillTransition and not have to add an observer for the UIDeviceOrientationDidChangeNotification.
override func viewWillTransitionToSize(size: CGSize,
withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
{
if UIDevice.currentDevice().orientation.isLandscape {
//do your thing
}
}
This method will help you for grabbing orientation change.So if you want to update something during landscape as for your nib height or something, change your code by using this method.
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.currentDevice().orientation.isLandscape.boolValue {
print("Landscape")
//Change your code here for landscape
} else {
print("Portrait")
//Change your code here for portrait
}
}
Swift 3 version:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
}
Swift 4.2 version:
This is the one way to deal with this problem, inside your UIView subclass.
First you need to set observer in your UIView subclass.
override init(frame: CGRect) {
super.init(frame:frame)
NotificationCenter.default.addObserver(self, selector: #selector(updateLayout), name: UIDevice.orientationDidChangeNotification, object: nil)
//UI setup
}
Than in your targer function you can set new frame for your custom view.
#objc func updateLayout(){
if UIDevice.current.orientation.isLandscape {
self.frame = CGRect(.....)
} else {
self.frame = CGRect(......)
}
}
Dontr forget to remove your obsserver in deinit method of your UIView.
deinit {
NotificationCenter.default.removeObserver(self,
name: UIDevice.orientationDidChangeNotification,
object: nil)
}

How to implement an independent custom UIView with nib file in Swift?

Many times I would like to implement custom UIViews, but I always have the same problem : my IBOutlets are nil when I want to use them.
I tried everything I could see on the web, but nothing works...
class ImagePickerView: UIView, ImagePickerDelegate {
#IBOutlet weak var imageViewSelected: UIImageView!
//MARK: - Initializers
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let tapGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.onAddPicturePressed))
tapGesture.minimumPressDuration = 0
self.addGestureRecognizer(tapGesture)
}
override func awakeFromNib() {
super.awakeFromNib()
//imageViewSelected = UIImageView()
}
//MARK: - ImagePickerDelegate
func wrapperDidPress(_ imagePicker: ImagePickerController, images: [UIImage]) {}
func doneButtonDidPress(_ imagePicker: ImagePickerController, images: [UIImage]) {
self.imageViewSelected.image = images[0] // (the limit is set to 1 image)
imagePicker.dismiss(animated: true, completion: nil)
}
func cancelButtonDidPress(_ imagePicker: ImagePickerController) {
imagePicker.dismiss(animated: true, completion: nil)
}
//MARK: - Events
func onAddPicturePressed(gesture: UITapGestureRecognizer) {
if gesture.state == .began {
self.backgroundColor = CustomsColors.gray
} else if gesture.state == .ended {
self.backgroundColor = CustomsColors.lightGray
}
showImagePicker()
}
//MARK: - Utils
func showImagePicker() {
let imagePickerController = ImagePickerController()
imagePickerController.delegate = self
imagePickerController.imageLimit = 1
Configuration.mainColor = CustomsColors.green
Configuration.cancelButtonTitle = "Annuler"
Configuration.doneButtonTitle = "OK"
Configuration.noImagesTitle = "Pas d'images disponibles"
self.parentViewController?.present(imagePickerController, animated: true, completion: nil)
}
}
I tried self = Bundle.main.loadNibNamed("ImagePickerView", owner: self, options: nil)?.first as! ImagePickerView but I can't asign self.
I want something really simple, but I don't know how to do it...
Follow following steps in which view controller you want to add nib:
Make Object:
var priceView : FV_ViewToShowPrice!
Give Frame:
priceView = Bundle.main.loadNibNamed("FV_ViewToShowPrice", owner: self, options: nil)![0] as! FV_ViewToShowPrice
priceView.frame = self.view.bounds
Add in view:
self.view.addSubview(priceView)

Tapgesture and touch events not working which created by custom uiview with xib?

I am trying to show some content with custom UIView as pop up, so far I done it good, I want to release the pop up by touching Pop up view, I cannot get it as success, I am new to swift language, can you please help me with this, All code only copied from Google only,
Code For Custom UIView
import UIKit
class PopUpView: UIView {
var view = UIView()
var _myTap: UITapGestureRecognizer?
// this name has to match your class file and your xib file
#IBOutlet weak var lblContent: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func awakeFromNib()
{
self.userInteractionEnabled = true
}
func _myHandleTap(sender: UITapGestureRecognizer) {
if sender.state == .Ended {
print("_myHandleTap(sender.state == .Ended)")
sender.view!.backgroundColor
= UIColor(red: CGFloat(drand48()), green: CGFloat(drand48()), blue: CGFloat(drand48()), alpha: 1.0);
self .hideInView()
}
}
func setup() {
// setup the view from .xib
view = loadViewFromNib()
view .userInteractionEnabled = true
self .userInteractionEnabled = true
view.backgroundColor = UIColor.clearColor()
_myTap = UITapGestureRecognizer(target: self
, action: #selector(_myHandleTap(_:)))
_myTap!.numberOfTapsRequired = 1
view.addGestureRecognizer(_myTap!)
}
func loadViewFromNib() -> UIView {
// grabs the appropriate bundle
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "PopUpView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
func showInView(currentView:UIView,content:String) {
view.frame = currentView.bounds
currentView .userInteractionEnabled = true
lblContent.text = content
UIApplication.sharedApplication().keyWindow!.addSubview(view)
view.alpha = 0
UIView.animateWithDuration(0.2, delay: 0.2, options: [.CurveEaseInOut], animations: {
self.view.alpha = 1
}, completion: nil)
}
func hideInView() {
view.alpha = 0
UIView.animateWithDuration(0.2, delay: 0.2, options: [.Repeat, .CurveEaseInOut], animations: {
self.view.alpha = 0
self.view .removeFromSuperview()
}, completion: nil)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self .hideInView()
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
Accessing it from my UIViewController
#IBAction func doForgotAction(sender: AnyObject) {
// warning: could not load any Objective-C class information. This will significantly reduce the quality of type information available.
let custom = PopUpView()
custom .showInView(self .view, content: "welcome")
}
Finally i got my answer,
The main problem is events are not received, whether its created by xib or programmatically
I have changed all code to below
import UIKit
class PopUpView: UIView {
var view = UIView()
// this name has to match your class file and your xib file
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var lblContent: UILabel!
#IBOutlet weak var _myTap: UITapGestureRecognizer!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func awakeFromNib()
{
super.awakeFromNib()
}
#IBAction func tapGestureAction(sender: AnyObject) {
self .hideInView()
}
func setup() {
// setup the view from .xib
view = loadViewFromNib()
view.frame = bounds
self .userInteractionEnabled = true
view.backgroundColor = UIColor.greenColor()
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
addSubview(view)
}
func loadViewFromNib() -> UIView {
// grabs the appropriate bundle
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "PopUpView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
func showInView(currentView:UIView,content:String) {
self.frame = currentView.bounds
self .layoutIfNeeded()
lblContent.text = content
UIApplication.sharedApplication().keyWindow!.addSubview(self)
self.alpha = 0
UIView.animateWithDuration(0.2, delay: 0.2, options: [.CurveEaseInOut], animations: {
self.alpha = 1
}, completion: nil)
}
func hideInView() {
self.alpha = 1
UIView.animateWithDuration(0.2, delay: 0.2, options: [.Repeat, .CurveEaseInOut], animations: {
self.alpha = 0
self .removeFromSuperview()
}, completion: nil)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self .hideInView()
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
All are working good after changing this follow line
func setup() {
// setup the view from .xib
view = loadViewFromNib()
view.frame = bounds
self .userInteractionEnabled = true
view.backgroundColor = UIColor.greenColor()
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
addSubview(view)
}
Now i can get tapgesture and touchevents.
You have to create the outlet action method in your Custom View Class and and the touch and tap gesture in this class. Now in order to receive the effect of these touches and taps, you have to create a delegate and add the delegate method to your view controller in order to receive the touches and taps.

How to initialize/instantiate a custom UIView class with a XIB file in Swift

I have a class called MyClass which is a subclass of UIView, that I want to initialize with a XIB file. I am not sure how to initialize this class with the xib file called View.xib
class MyClass: UIView {
// what should I do here?
//init(coder aDecoder: NSCoder) {} ??
}
I tested this code and it works great:
class MyClass: UIView {
class func instanceFromNib() -> UIView {
return UINib(nibName: "nib file name", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as UIView
}
}
Initialise the view and use it like below:
var view = MyClass.instanceFromNib()
self.view.addSubview(view)
OR
var view = MyClass.instanceFromNib
self.view.addSubview(view())
UPDATE Swift >=3.x & Swift >=4.x
class func instanceFromNib() -> UIView {
return UINib(nibName: "nib file name", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
}
Sam's solution is already great, despite it doesn't take different bundles into account (NSBundle:forClass comes to the rescue) and requires manual loading, a.k.a typing code.
If you want full support for your Xib Outlets, different Bundles (use in frameworks!) and get a nice preview in Storyboard try this:
// NibLoadingView.swift
import UIKit
/* Usage:
- Subclass your UIView from NibLoadView to automatically load an Xib with the same name as your class
- Set the class name to File's Owner in the Xib file
*/
#IBDesignable
class NibLoadingView: UIView {
#IBOutlet weak var view: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
nibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
nibSetup()
}
private func nibSetup() {
backgroundColor = .clearColor()
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
view.translatesAutoresizingMaskIntoConstraints = true
addSubview(view)
}
private func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
let nibView = nib.instantiateWithOwner(self, options: nil).first as! UIView
return nibView
}
}
Use your xib as usual, i.e. connect Outlets to File Owner and set File Owner class to your own class.
Usage: Just subclass your own View class from NibLoadingView & Set the class name to File's Owner in the Xib file
No additional code required anymore.
Credits where credit's due: Forked this with minor changes from DenHeadless on GH. My Gist: https://gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8
As of Swift 2.0, you can add a protocol extension. In my opinion, this is a better approach because the return type is Self rather than UIView, so the caller doesn't need to cast to the view class.
import UIKit
protocol UIViewLoading {}
extension UIView : UIViewLoading {}
extension UIViewLoading where Self : UIView {
// note that this method returns an instance of type `Self`, rather than UIView
static func loadFromNib() -> Self {
let nibName = "\(self)".characters.split{$0 == "."}.map(String.init).last!
let nib = UINib(nibName: nibName, bundle: nil)
return nib.instantiateWithOwner(self, options: nil).first as! Self
}
}
And this is the answer of Frederik on Swift 3.0
/*
Usage:
- make your CustomeView class and inherit from this one
- in your Xib file make the file owner is your CustomeView class
- *Important* the root view in your Xib file must be of type UIView
- link all outlets to the file owner
*/
#IBDesignable
class NibLoadingView: UIView {
#IBOutlet weak var view: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
nibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
nibSetup()
}
private func nibSetup() {
backgroundColor = .clear
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.translatesAutoresizingMaskIntoConstraints = true
addSubview(view)
}
private func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
let nibView = nib.instantiate(withOwner: self, options: nil).first as! UIView
return nibView
}
}
Universal way of loading view from xib:
Example:
let myView = Bundle.loadView(fromNib: "MyView", withType: MyView.self)
Implementation:
extension Bundle {
static func loadView<T>(fromNib name: String, withType type: T.Type) -> T {
if let view = Bundle.main.loadNibNamed(name, owner: nil, options: nil)?.first as? T {
return view
}
fatalError("Could not load view with type " + String(describing: type))
}
}
Swift 4
Here in my case I have to pass data into that custom view, so I create static function to instantiate the view.
Create UIView extension
extension UIView {
class func initFromNib<T: UIView>() -> T {
return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as! T
}
}
Create MyCustomView
class MyCustomView: UIView {
#IBOutlet weak var messageLabel: UILabel!
static func instantiate(message: String) -> MyCustomView {
let view: MyCustomView = initFromNib()
view.messageLabel.text = message
return view
}
}
Set custom class to MyCustomView in .xib file. Connect outlet if necessary as usual.
Instantiate view
let view = MyCustomView.instantiate(message: "Hello World.")
Swift 3 Answer: In my case, I wanted to have an outlet in my custom class that I could modify:
class MyClassView: UIView {
#IBOutlet weak var myLabel: UILabel!
class func createMyClassView() -> MyClassView {
let myClassNib = UINib(nibName: "MyClass", bundle: nil)
return myClassNib.instantiate(withOwner: nil, options: nil)[0] as! MyClassView
}
}
When in the .xib file, make sure that the Custom Class field is MyClassView. Don't bother with the File's Owner.
Also, make sure that you connect the outlet in MyClassView to the label:
To instantiate it:
let myClassView = MyClassView.createMyClassView()
myClassView.myLabel.text = "Hello World!"
Swift 5.3
Create a class named NibLoadingView with the following contents:
import UIKit
/* Usage:
- Subclass your UIView from NibLoadView to automatically load an Xib with the same name as your class
- Set the class name to File's Owner in the Xib file
*/
#IBDesignable
class NibLoadingView: UIView {
#IBOutlet public weak var view: UIView!
private var didLoad: Bool = false
public override init(frame: CGRect) {
super.init(frame: frame)
self.nibSetup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.nibSetup()
}
open override func layoutSubviews() {
super.layoutSubviews()
if !self.didLoad {
self.didLoad = true
self.viewDidLoad()
}
}
open func viewDidLoad() {
self.setupUI()
}
private func nibSetup() {
self.view = self.loadViewFromNib()
self.view.frame = bounds
self.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.translatesAutoresizingMaskIntoConstraints = true
addSubview(self.view)
}
private func loadViewFromNib() -> UIView {
guard let nibName = type(of: self).description().components(separatedBy: ".").last else {
fatalError("Bad nib name")
}
if let defaultBundleView = UINib(nibName: nibName, bundle: Bundle(for: type(of: self))).instantiate(withOwner: self, options: nil).first as? UIView {
return defaultBundleView
} else {
fatalError("Cannot load view from bundle")
}
}
}
Now create a XIB & UIView class pair, set XIB's owner to UIView class and subclass NibLoadingView.
You can now init the class just like ExampleView(), ExampleView(frame: CGRect), etc or directly from storyboards.
You can also use viewDidLoad just like in UIViewController. All your outlets and layouts are rendered in that moment.
Based on Frederik's answer
Below code will do the job if anyone wants to load a custom View with XIB Programmatically.
let customView = UINib(nibName:"CustomView",bundle:.main).instantiate(withOwner: nil, options: nil).first as! UIView
customView.frame = self.view.bounds
self.view.addSubview(customView)
create view from .xib
let nib = UINib(nibName: "View1", bundle: nil) //View1 is a file name(View1.swift)
if let view = nib.instantiate(withOwner: self, options: nil).first as? UIView {
// logic
}
//or
if let view = Bundle.main.loadNibNamed("View1", owner: self, options: nil)?.first as? UIView {
// logic
}
Since .xib can contains several view, that is why you are working with array here(.first)
For example
Create View1.xib
Create View1.swift where set owner(loadNibNamed()) in code to create the instance of class("View1")
Set File's Owner in View1.xib as View1. Allows to connect outlets and actions
import UIKit
class View1: UIView {
#IBOutlet var contentView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.commonInit()
}
private func commonInit() {
if let view = Bundle.main.loadNibNamed("View1", owner: self, options: nil)?.first as? UIView {
addSubview(view)
view.frame = self.bounds
}
}
}
Notes
if we move Custom Class from File's owner to Container View we get error(loop). It is because of:
System init instance from Container View where we init it again in commonInit()
.loadNibNamed
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ff7bf6fbfc8)
override func draw(_ rect: CGRect)
{
AlertView.layer.cornerRadius = 4
AlertView.clipsToBounds = true
btnOk.layer.cornerRadius = 4
btnOk.clipsToBounds = true
}
class func instanceFromNib() -> LAAlertView {
return UINib(nibName: "LAAlertView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! LAAlertView
}
#IBAction func okBtnDidClicked(_ sender: Any) {
removeAlertViewFromWindow()
UIView.animate(withDuration: 0.4, delay: 0.0, options: .allowAnimatedContent, animations: {() -> Void in
self.AlertView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
}, completion: {(finished: Bool) -> Void in
self.AlertView.transform = CGAffineTransform.identity
self.AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
self.AlertView.isHidden = true
self.AlertView.alpha = 0.0
self.alpha = 0.5
})
}
func removeAlertViewFromWindow()
{
for subview in (appDel.window?.subviews)! {
if subview.tag == 500500{
subview.removeFromSuperview()
}
}
}
public func openAlertView(title:String , string : String ){
lblTital.text = title
txtView.text = string
self.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
appDel.window!.addSubview(self)
AlertView.alpha = 1.0
AlertView.isHidden = false
UIView.animate(withDuration: 0.2, animations: {() -> Void in
self.alpha = 1.0
})
AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
UIView.animate(withDuration: 0.3, delay: 0.2, options: .allowAnimatedContent, animations: {() -> Void in
self.AlertView.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
}, completion: {(finished: Bool) -> Void in
UIView.animate(withDuration: 0.2, animations: {() -> Void in
self.AlertView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
})
})
}

Resources