Xib file renders when called by my page view controller but not as a tab - swift

I'm rendering a Xib file which is a subview in a view controller, the view controller is called by setViewController in a pageviewcontroller . It works great. I then placed the same Xib sub view in a view controller called by my tab view controller and it doesn't render.
To try and get to the bottom of this weird occurrence I have removed all code such that there is no class attached to the UIviewcontroller parent of the subview Xib in both cases. It is now a pure IB implementation of the Xib and the Xib classes are identical.
So in my comparison between the two implementations the only difference is one is opened from a page view controller and the other from a tab. I even copied the working view controller just so I knew they were identical.
Has anyone had this experience (where the same Xib doesn't render in one workflow compared to another) or have an idea as to why the two implementations cause different results??
Update: I took it one step further. Now there is absolutely no code. The only thing that is in the Xib is an unattached label. It only renders when the parent view is called from a page view controller! Wtf
Signin View Xib. (Works when instantiated from UIPageViewController:
SigninView.swift
class SigninView: UIView, GIDSignInUIDelegate, GIDSignInDelegate {
var view: UIView!
var nibName: String = "SigninView"
var delegate: SigninViewDelegate?
#IBOutlet weak var googleButton: GIDSignInButton!
#IBOutlet weak var signoutButton: UIButton!
override init(frame: CGRect) {
// properties
super.init(frame: frame)
// Set anything that uses the view or visible bounds
setup()
}
override func awakeFromNib() {
print("AWAKING!")
}
#IBAction func tapSignOut(sender: AnyObject) {
GIDSignIn.sharedInstance().signOut()
UserManager().logout()
self.updateDisplay()
}
required init?(coder aDecoder: NSCoder) {
// properties
super.init(coder: aDecoder)
// Setup
setup()
}
func setup() {
view = loadViewFromNib()
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().delegate = self
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
addSubview(view)
self.updateDisplay()
}
func updateDisplay() {
self.googleButton.layer.hidden = false
}
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: nibName, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
func signIn(signIn: GIDSignIn!, presentViewController viewController: UIViewController!) {
//present vc on delegate
let delegateVC = self.delegate as! UIViewController
delegateVC.presentViewController(viewController, animated: true, completion: nil)
}
func signIn(signIn: GIDSignIn!, dismissViewController viewController: UIViewController!) {
print("Dismiss view controller")
let delegateVC = self.delegate as! UIViewController
delegateVC.dismissViewControllerAnimated(true, completion: nil)
self.updateDisplay()
}
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!, withError error: NSError!) {
var success = false
if (user != nil) {
print("signed in? UID: \(user.userID), email: \(user.profile.email), name: \(user.profile.name), pic: \(user.profile.imageURLWithDimension(150))")
UserManager().createUser(user)
LocalDataStorage().saveContext()
success = true
} else {
print("User not signed in.")
}
self.updateDisplay()
if self.delegate != nil && self.delegate!.signInComplete != nil {
self.delegate!.signInComplete!(success)
}
}
func signIn(signIn: GIDSignIn!, didDisconnectWithUser user: GIDGoogleUser!, withError error: NSError!) {
print("disconnected")
}
}
Working UIViewController that contains working Signin Subview
Working call from UIPageViewController
let vc = UIStoryboard(name: "Intro", bundle: nil).instantiateViewControllerWithIdentifier("IntroPage2")
setViewControllers([getSlide(index)],direction: .Forward,animated: true,completion: nil)
UIViewController called from tab view that does not render signin subview

I am posting AN answer but really it's more of a work around. After hours of trying to finagle the views I get the code working consistently. To get it working created a new Xib and new VC file for the Signin module. I copied all the code from my original Signin class to the new one and it worked. No code was altered.
So if anyone comes across the gnarly painful situation give that a shot! This could very well be an XCode bug.

Related

Unable to access UIView subclass IBOutlets From TabBarController

I have a UIView Class with xib. I try to add it in another ViewControllers as a popover view. I have outlet connections. But when I run the application it crashed and stated
This class is not key value coding-compliant for the key btnAbtUs
I think the problem is should select delegate. I may using wrong way to add this xib. How can I correct it?
Here is my code.
my UIView subclass
class MoreView: UIView {
#IBOutlet var containerView: UIView!
#IBOutlet weak var btnAboutUs: UIButton!
override public func awakeFromNib() {
super.awakeFromNib()
}
override init(frame: CGRect) {
super.init(frame: frame)
loadViewFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib()
}
func loadViewFromNib() {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "MoreView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.insertSubview(view, at: 0)
commitInit()
}
private func commitInit(){
containerView.translatesAutoresizingMaskIntoConstraints = true
self.btnAboutUs.addTarget(self, action: #selector(self.clickAboutUs(_:)), for: .touchUpInside)
}
class func instanceFromNib() -> UIView {
return UINib(nibName: "MoreView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
}
#objc func clickAboutUs(_ sender: Any) {
print("tap")
}
}
in UITabBarController
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
let moreView = MoreView.instanceFromNib
if let navigationController = viewController as? UINavigationController,
navigationController.viewControllers.contains(where: { $0 is MoreViewController }) {
moreView().frame.origin.y = 100
self.view.addSubview(moreView())
return false
} else {
moreView().removeFromSuperview()
return true
}
}
Finally I found the issue. This is the right way to add a UIView as a SubView controller of a UIViewController.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let navigationController = viewController as? UINavigationController,
navigationController.viewControllers.contains(where: { $0 is MoreViewController }) {
let mySubView : MoreView
mySubView = MoreView(frame: CGRect(x: 0, y: 0, width: 375, height: 667) )
self.view.addSubview(mySubView)
return false
} else {
return true
}
}
You might have copied the XIB and forgot to remove/add a connection with the IBOutlet. Please check.
It indicates that an already connected Interface Builder object is removed/renamed in its owner's source (File's Owner).
You may forgot to remove a connection with the IBOutlet. Please check on on show connection inspector.First click on file inspector of xib then click on show connection inspector. Shown in image.
Remove the broken outlets.

Add UIViewController to UICollectionViewCell

I read a lot of Stackoverflow but I could not figure out how to do it.
I wand to add a UIViewController to a UICollectionViewCell with four different cell, the View of one of the cells is quite complex and the data changes constantly, therefor I want to add a controller to this view.
I could not figure out how to add UIViewController to the Cell as there is no addChildViewController.
My ViewController:
class ViewController1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
My CollectionViewCell:
class CollectionViewCell1: UICollectionViewCell {
lazy var viewController: ViewController1 = {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ViewController1") as! ViewController1
return vc
}()
override init(frame: CGRect) {
super .init(frame: frame)
print("Initiatetd")
setUpViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been impemented")
}
func setUpViews() {
addSubview(viewController.view)
let views: [String: Any] = ["viewController": viewController]
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[viewController]|", options: [], metrics: nil, views: views))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[viewController]-|", options: [], metrics: nil, views: views))
}
}
I am getting this error:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[App. ViewController1
nsli_superitem]: unrecognized selector sent to instance
0x7fb08df28ea0'
You can't add a view controller to a cell like that. For doing what you want you need to use a container view. The documentation says that:
Container View defines a region within a view controller's view subgraph that can include a child view controller.
Just add a container view to your cell (from Storyboard).
To connect a container view to your View Controller (where you have cells) you need to create an embed segue. To send data to the View Controller in prepare(forSegue:) you check the destination and do what you need.

Hide Custom View UIButton From UIViewController Class

Actually i have a Custom view with two button, and i want to hide it at runtime through UIViewController , So i don't get any exact thing to hide that button from UIViewcontroller class
Here is my CustomView class,
import UIKit
class BottomButtonUIView: UIView {
#IBOutlet weak var btnNewOrder: UIButton!
#IBOutlet weak var btnChat: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
// MARK: init
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
if self.subviews.count == 0 {
setup()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
func setup() {
if let view = Bundle.main.loadNibNamed("BottomButtonUIView", owner: self, options: nil)?.first as? BottomButtonUIView {
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
}
}
#IBAction func btnOrderNowClick(_ sender: Any) {
let VC1 = StoryBoardModel.orderDeatalStoryBord.instantiateViewController(withIdentifier: "NewOrderViewController") as! NewOrderViewController
VC1.isPush = false
let navController = UINavigationController(rootViewController: VC1) // Creating a navigation controller with VC1 at the root of the navigation stack.
let currentController = getCurrentVC.getCurrentViewController()
currentController?.present(navController, animated:true, completion: nil)
}
#IBAction func btnChatNowClick(_ sender: Any) {
}
func getCurrentViewController() -> UIViewController? {
if let rootController = UIApplication.shared.keyWindow?.rootViewController {
var currentController: UIViewController! = rootController
while( currentController.presentedViewController != nil ) {
currentController = currentController.presentedViewController
}
return currentController
}
return nil
}
}
I set it to UIView in StoryBoard, and then I create outlet of that view,
#IBOutlet weak var viewBottmNewOrder: BottomButtonUIView!
Now i want to hide btnNewOrder from UIViewcontroller class but when i use
viewBottmNewOrder.btnNewOrder.isHidden = true it cause null exception, Please do need full answer.
Please don't do like that. The required init(coder aDecoder: NSCoder) will call a lot of times when the BottomButtonUIView created from xib. And your custom view will look like:
[BottomButtonUIView [ BottomButtonUIView [btnNewOrder, btnChat]]].
So when you access to btnNewOrder like that:
viewBottmNewOrder.btnNewOrder it will null.
I think you should add your custom view in viewDidLoad of your `UIViewController'.

Open viewcontroller with textfield inside view xib file

I will explain the process before asking my question:
I have a tableviewcontroller, but i needed a textfield was always present at the hearing, to solve this problem i create a xib file with textfield inside and call the view with viewForHeaderInSection; at this point all is perfect.
i call the view whith this code:
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return NSBundle.mainBundle().loadNibNamed("createComment", owner: nil, options: nil)[0] as? createComment
}
My problem is next:
I want that when capturing text field starts a new window opens, something like what makes facebook to create a comment.
My code in createComment is:
class createComment: UIView, UITextFieldDelegate {
#IBOutlet weak var commentTextField: StyleComentTextField!
weak var controller:tableComentVC!
var currentController = tableComentVC()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
self.commentTextField.delegate = self
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
commentTextField.resignFirstResponder()
if(textField == commentTextField){
print("ok i tab in commentTextField")
let storyBoard: UIStoryboard = UIStoryboard (name: "Main", bundle: nil)
self.controller?.navigationController!.pushViewController(storyBoard.instantiateViewControllerWithIdentifier("writeComment") as! writeComment, animated: true)
resign()
return false
}else{
return true
}
}
}
but this code dont work, the print in code works correctly, but i don't know why dont open the next viewcontroller.
Thanks!

delegate is always nil

I have a custom UIView class called MyView and a View Controller.
When the user taps a button on the UIView, I want to call a function on the view controller. I'm trying to achieve this through delegation
custom UIClass
#objc protocol MyViewDelegate{
optional func expandCollapse()
}
class MyView: UIView, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDelegate{
weak var delegate:MyViewDelegate?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
if self.subviews.count == 0 {
loadNib()
}
}
override init(frame:CGRect){
super.init(frame: frame)
loadNib()
}
func loadNib(){
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "MyView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! MyView
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.addSubview(view);
}
#IBAction func expandit(sender: AnyObject) {
//this is where it fails. delegate is nil
delegate!.expandCollapse!()
}
}
My View Controller
class ViewController2: UIViewController, MyViewDelegate {
#IBOutlet weak var theview: UIView!
var myview : MyView?
override func viewDidAppear(animated: Bool) {
super.viewWillAppear(animated)
myview = MyView(frame: CGRectMake(0,0,theview.frame.size.width,theview.frame.size.height))
self.theview.addSubview(myview!)
myview!.delegate = self
}
func expandCollapse() {
viewheight.constant = 172
UIView.animateWithDuration(0.5) {
self.view.layoutIfNeeded()
}
}
In the UIView, the delegate is always nil. What am I missing?
Using delegation for this is simply unsuitable. You are fighting UIKit design patterns.
The whole situation is very simple.
You have your ViewController.
Then you have your totally independent custom view.
Essentially, you want somehow to route the TouchUpInside event from the button to get to viewController.
If your Custom view contains a button, then the accessibility level of this button is internal by default. Looking at the code, I assume you created the button in Interface builder. Make an outlet from the custom view class to the button, so that there is a programatically accessible reference to it.
Your view controller declares an instance of this custom view. Then, in viewDidLoad you have to use the target-action pattern.
self.customView.button.addTarget(target: self, action: "expandCollapse", forControlEvents: .TouchUpInside)
That's basically all there is to it.
I'm not entirely confident of my ARC understanding, but I believe the issue is that your delegate is a weak reference and there's nothing keeping a reference to the delegate after it's set, so it' deallocated.
Replace it with this and I believe it will work:
var delegate:MyViewDelegate?
Try assigning the delegate to "myview" before adding it to "theview"
The problem is in the loadNib() member function.
You're creating two instances of "MyView". The second instance being added as a subview.
You're setting the delegate in one instance and referring to a nil delegate in the other instance.
Try using a static class method like below to create one instance of "MyView"
class func loadFromNib() -> MyView? {
guard let myView = Bundle.main.loadNibNamed("MyView", owner: nil, options: nil)?.first as? MyView else {
assertionFailure("Failed to load nib for 'MyView'!")
return nil
}
return myView
}
Doing it this way, you won't need the custom init()s either.
Hope that helps!