Utilizing IBOutlets/IBActions from external loaded Nib - ios

Forgive me for my noob questions, but I really need some help. I am learning IOS development with swift2. Unfortuantely, the tutorials I've followed/done don't really get into much advanced stuff when it comes to working with Views/Nib files.
Anyhow, I am trying to create a custom class that will load my Nib and I can work with the Outlets/Actions of the newly instantiated class of type UIView.
I was able to find some information that got me to being able to load my view properly and insert it into my tableHeaderView, but I am kind of lost. I need a little guidance.
ReserveHeaderCell.swift
class ReserveHeaderCell : UIView{
#IBOutlet weak var ReserveHeaderImageView: UIImageView!
class func instanceFromNib() -> UIView {
return UINib(nibName: "ReserveHeaderCell", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! ReserveHeaderCell
}
}
I set the class in my xib to be this class, further, the outlet refers to a UIImageView within the parent view.
ReserveViewController.swift contains:
let reserveHeaderFrame: CGRect = CGRectMake(0,0,self.view.frame.size.width, 164)
let rhCell = ReserveHeaderCell.instanceFromNib()
rhCell.frame = reserveHeaderFrame
ReserveTableView.tableHeaderView = rhCell
And this all works. The frame gets created, the xib is loaded and added to the view. Great, the problem is, I don't have access to my IBOutlets that are defined in the ReserveHeaderCell.swift file. Also, I really don't know if this is the correct way to accomplish what I am trying to do.
What I am trying to accomplish (done with obj-C)
- (id)initWithFrame:(CGRect)frame {
if (self) {
NSArray *nib = [[NSBundle mainBundle]
loadNibNamed:#"HeaderNib"
owner:self
options:nil];
self = [nib objectAtIndex:0];
// ACTIONS GO HERE
}
return self;
}
That way I have access to any of the IBOutlets that are created (in obj-c the H file, but for my case, it would be the class, right?)
using the obj-c method, I can simply call view.outlet = "" and get access to them.
Any help is appreciated. Any links to tutorials that could help is appreciated.
Thank you in advance.

You can use this code to init your nib file
let view = (NSBundle.mainBundle().loadNibNamed("MenuHeader", owner: self, options: nil)[0] as? UIView)
tableView.tableHeaderView = view
Then your MenuHeader will be a normal .xib file with your cell inside it, just remember to adjust the constraints appropriately to fit on all the screens that you want. If you already have a class for your UIView you have to connect your nib file with this class and then you can use "view.label.text" and then change your values. Remember to keep a reference of this view or just create a new one when you have to change the header information. If you want to track an action the best way would be through a delegate method or using the NSNotificationCenter.

Related

When subclassing UIView with a XIB file, why do I need to have a contentView property as a subview to display the XIB?

So I have a subclass of UIView - let's call it CustomView - with a XIB file for the layout of its subviews.
In interface builder I set the Custom Class of the top level view to CustomView and made a class method to load the XIB and return that top-level view when I need to use it elsewhere in the app.
CustomView.m
+(instancetype)newCustomView
{
CustomView *customView = [[[NSBundle mainBundle] loadNibNamed: #"CustomView" owner: nil options: nil] firstObject];
...
return customView;
}
ViewController.m
-(void)viewDidLoad
{
...
CustomView *firstCustomView = [CustomView newCustomView];
[self.view addSubview: firstCustomView];
}
This all works and I can access CustomView's outlets just fine, but it feels pretty hacky. Looking into the conventional way to initialise a UIView subclass with an associated XIB I found a single article using this same method, but the majority of tutorials use some variation of:
Set the File's Owner to the UIView subclass. (Don't set a custom class for the top-level view)
Add a contentView outlet to the class and set it to the top-level view in interface builder.
Make a method to load the Nib and add the contentView as a subview of the class.
Call this method in both initWithFrame: and initWithCoder:.
This works too, but I don't understand is why it's necessary to have a contentView property set to IB's top-level view as a subview. If CustomView is already a UIView why can't I just set itself to that top-level view? Wouldn't that be more straight forward? I feel like the more I try to understand it the less it makes sense.
Cheers for any help!

Is it okay to register all table view cells in a base table view class?

I think this is not an opinionated question, but rather a question made to know if this is a good practice or not in iOS.
I'm currently doing this registration of custom tableViewCells in every controller in my project.
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.tableHeaderView = UIView()
self.tableView.tableFooterView = UIView()
self.tableView.register(R.nib.customCellXX)
self.tableView.register(R.nib.customCellYY)
// and so on...
self.tableView.estimatedRowHeight = 150.0
self.tableView.rowHeight = UITableViewAutomaticDimension
}
I'm just wondering if I could just make a subclass of tableView and register there all the custom tableViewCells I have as well as put the auto-height of the tableView + tableHeaderView + tableFooterView. In this case, I'm reducing the line of codes in my controllers and avoiding redundancies.
EDIT: If this is a good practice, where can I put the registration of the custom cells in my subclassed BaseTableView?
It can be done in the following way:
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
self = [super initWithFrame:frame style:style];
if (self) {
[self registerClass:[MyTableViewCell class] forCellReuseIdentifier:#"reUseIdentifier"];
}
return self;
}
Later, you can subclass this UITableView or even use this in different controllers.
You COULD, as long as you call super.viewDidLoad(), inside of the child VC's overridden viewDidLoad methods. That said, subclassing VCs is generally bad practice in iOS.
If you use Interface Builder, the IBOutlets you wire up from your Storyboard to your .swift files, all have identifying values, and can cause nil reference crashes, and cause build errors.
If you're trying to use the same IBOutlets across multiple VCs, which you will necessarily do if you try to wire any UI element to your parent VC.
If you want all of your VCs to have some shared SIGNATURE (i.e., the same methods or properties), create a custom protocol. If you want them all to have access to the same UI elements in Interface Builder, learn to use Storyboards.
You can create a VC in a Storyboard, and instantiate it in code, easily.

Reusable Nibs in Storyboards

I'm looking for a way to make nibs that I can then embed and reuse inside a storyboard with all the IBOutlets correctly instantiated. I was able to get it working using a modified version of this tutorial.
The steps for getting this to work are:
Make a custom view subclass and corresponding nib
Set File's Owner to the custom view subclass
Add a UIView as a direct descendent of the nib's view (this is our Content View)
Create an IBOutlet in your view class called contentView of type UIView and content the content view in the nib to that outlet
In awakeFromNib do the following:
override func awakeFromNib() {
super.awakeFromNib() //call super
NSBundle.mainBundle().loadNibNamed("MyView", owner: self, options: nil)
self.contentView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.contentView)
self.addEdgeConstraintWithAttribute(.Left, withSubview: self.contentView)
self.addEdgeConstraintWithAttribute(.Top, withSubview: self.contentView)
self.addEdgeConstraintWithAttribute(.Right, withSubview: self.contentView)
self.addEdgeConstraintWithAttribute(.Bottom, withSubview: self.contentView)
}
This process works fine but I don't understand HOW works. And is there a better way of achieving the same thing?
This all magic comes from the awakeFromNib from NSNibAwaking protocol.
"The -awakeFromNib method is called for every object that is
instantiated when a nib file is loaded..." - e.James
Lets say, you made a class and nib with the name of MyView and done all the above steps, Now when you add a UIView in your storyboard and set its class to MyView and run the app. Here's what happens.
You storyboard loads all views and also calls awakeFromNib for your
MyView.
Your awakeFromNib then triggers the loading of nib named MyView.
You get the content view from Nib instantiated views and add it to storyboard instantiated view and set its constraints.
Notice the loadNibNamed method, you're sending self as owner, so
when the nib is unarchived and loaded, it sets all the IBOutlets to
the sent self, that's why your IBOutlet worked i.e self.contentView because contentView now has view from nib.

Load UIView with Nib and UIView class

I've tried this class
https://github.com/autresphere/ASDepthModal
i want it to popup like it does but i want to be able to set the labels programmatically, since i need the to change depending on what day it is.
I'm using storyboard, so i've created a .xib and uiview.h and uiview.m. In my main UIViewController i have:
xibContents = [[NSBundle mainBundle] loadNibNamed:#".xib" owner:self options:nil];
testView = [xibContents lastObject];
in my .xib i have set the file owner to my uiview class, this create a problem: NSUnknownKeyException
When i set the uiiew inside my .xib to my uiview class the application will load and i can open it just like it should, but i'm not able to change the state of the label programmatically? I'm complety lost here!
Typically speaking, UIViews do not have access to IBOutlets. Apple kind of intended xibs to only be assigned to UIViewControllers.
However, you can load a view from a xib in two ways:
1) Create an extra xib to use in your UIViewController. Set the File's Owner to your view controller, and the class name of the view to your custom view class. In interface builder, this is under "custom class". You can set the view as a IBOutlet, and iOS will create an instance of your custom class when your UIViewController loads the xib and sets itself as owner (like you tried above, but only from within a controller class)
2) Load a xib in a UIView class, and set self to the resultant object:
- (id)init {
self = [super initWithFrame:CGRectMake(0, 0, 1024, 352)];
if (self) {
NSArray* nib = [[NSBundle mainBundle] loadNibNamed:#"TCSNumberKeyPad" owner:self options:nil];
[[nib objectAtIndex:0] setFrame:CGRectMake(0, 0, 1024, 352)];
self = [nib objectAtIndex:0];
}
return self;
}
In either case, you will need to retrieve your label via code rather than IBOutlet properties. You can find your label in the subviews property:
UILabel* myLabel = [self.subviews objectAtIndex:0];
Actually got this to work. I took the wrong approach. I found it simpler to just create the view and populate it with background image and labels when the button got clicked. would have been simple to do it in a UI designer, but this wasn't that hard actually.
Thanks to the people who helped me :)
The file's owner should be the view controller, not the view itself. The view can have outlets to the labels. The view should be set to your custom class in your nib.

Loading custom class UIView from NIB (IBOutlets nil)

I'm trying to modularize a complex UI in several xib's.
I want to programaticly load each additional xib from file.
The xib has controls which are connected to the custom view code.
Then I try to load the xib the following way inside the main view controller:
WIFClientData *clientDataView;
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"WIFClientData" owner:self options: nil];
clientDataView = [nibViews objectAtIndex:0];
[clientDataView configure];
The problem is that when I try to configure the view:
-(void)configure
{
self.name.label.text = #"Name";
self.mobile.label.text = #"Mobile";
}
name and mobile are nil
I currently don't have File's owner connect, or set to any custom class. I only have the view which is layed in the xib, set to the custom class.
I have been reading a lot of things regarding to this but haven't found the answer yet.
Can someone help me?
Nuno
If you have a custom class WIFClientData and set a top-level view's class to WIFClientData in nib (which you then get via [nibViews objectAtIndex:0]), have IBOutlets connected to that view (not to file owner) then this should work. (works for me for custom cells)
Maybe you've misaligned something or mistyped?

Resources