UIView with Separated .h, .m and xib files - uiview

I want to create a UIView that will appear in several different UIViewControllers upon user action. For example, a "How To" pop view that whenever the user clicks on a "?" button it pops holding the relevant information about the specific action the user is interested in. The view may also have other elements like a "done" button and a UITextView that will hold the text, etc...
Intuitively, it makes me think of creating a separated UIView with .h, .m and xib files and have each UIViewController that requires this UIView will simply alloc+init it and do [self.view addSubView:flexUIView] or insert it with animation. Well... it doesn't work...
I couldn't find any tutorial that explains how to do something like that.
Is anyone familiar with this approach and have some directions?
If not, what is the common approach for such a scenario?

Alloc init will not load the nib of your custom UIView as in ViewControllers.
You should load the nib using the below code after alloc init
Suppose you have CustomView.h, CustomView.m, CustomView.xib
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self options:nil];
yourView = (CustomView *)[nib objectAtIndex:0];

I also found this tutorial that also has a link to the projects files and it gives a better idea regarding to how to do this:
Creating Reusable UIViews

Here's a tutorial I wrote for creating a custom UIView with .xib, .h, .m files. I've added two sample projects showing Interface Builder and programmatic approaches.
https://github.com/PaulSolt/CompositeXib

Related

Showing a content of a UIView loaded from a xib in a storyboard

Current Setup
I have a subclass of a UIView that loads a content from a xib file - call it a XibOwnerClass. Also, among other classes, there is a class called Triangle which helps me to create triangles with or without a border, with different stroke or fill color etc. That class is designable in storyboard and some of its properties are defined as IBInspectable.
Currently, in my xib file, I use this triangle view and setup its inspectable properties through IB. And that is really cool and convenient... So if I look into xib, I will actually see the triangle view among other views.
So, lets go further. In order to use this XibOwnerClass, I drag the UIView element to the storyboard, and change its custom class to XibOwnerClass, so I get my designable properties specific for XibOwnerClass. So, I can setup all things in IB and when I run the app, everything works.
The problem
Even if this works, I wonder if there is a way, to have multiple views (of class XibOwnerClass) dragged on a storyboard, and to be able to configure all of them individually trough Interface Builder?
Currently when I drag the UIView and change its custom class to XibOwnerClass I see nothing. I mean, the view is there, and it has its standard properties + inspectable properties. But I can't see triangles defined in xib in this new view. Is there a way to do this?
I am aware that xib is reused in my case (and it is meant to be used like that), so if I change something in a xib, all views that load from it will be affected. But is there a way multiple views to load from the same xib, but when loaded, to see them & setup them individually?
Here is how I load from xib:
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
if ((self = [super initWithCoder:aDecoder])){
UIView *myView = [[[NSBundle mainBundle] loadNibNamed:#"MyXib" owner:self options:nil] firstObject];
if (myView){
myView.frame = CGRectMake(myView.frame.origin.x, myView.frame.origin.y, self.frame.size.width, self.frame.size.height);
[self addSubview:myView];
//Initialize other stuff
[self commonInit];
}
}
return self;
}
Then in awakeFromNib: I use the values of inspectable properties...
EDIT:
Also I don't have initWithFrame: implemented, but I thought that it is not needed if load from nib (because it is not executed). But maybe this is needed while design time ?
can't really say what's wrong when you don't share class codes. but i guess you didnt use prepareForInterfaceBuilder. in your XibOwnerClass override prepareForInterfaceBuilder and call setups for view inside it for example change background colour or so.
override func prepareForInterfaceBuilder() {
//prepare setups or things to show in storyboard
}
Okay, I have solved it. It turned out that I shouldn't use Main Bundle but rather bundle for that class, like described here.
So here is the code:
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
#Mohammadalijf's answer in general is correct. It is just not a direct solution for my issue. But later on, when I fixed bundle thing, I used prepareForInterfaceBuilder like he said, and initialized things there so everything was available at design time.
Some useful stuff related to all this can be found here as well.

Creating a xib file template

I have created a xib file like the following:
I need to make this into a template as I don't want to have to recreate this multiple times. How can i go about doing this ?
Thank you in advance.
Regards..
1 - Add a sub view class, named whatever you want, I'll call it MyTamp.
2 - Connect your xib to properties in that class.
3 - Whenever you need you tamplate in the code use:
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"MyTamp"
owner:self
options:nil];
MyTamp* myView = [ nibViews objectAtIndex: 0];
4 - Use the properties you've set to update the view then added to whatever view you want.
Hope that helped :)
I would recommend you to read this article: Embedding custom-view Nibs in another Nib: Towards the holy grail
They explain really good how to approach this issue.

Nib from different class

I have tried several other examples, on this site and others, but for the life of me I cannot get this to work.
I have a NIB file which is a part of the class "ViewController2". I need to use a few of the views in this NIB file in my "ViewController1" class.
Each time I call
[[NSBundle mainBundle]loadNibNamed:#"ViewController2" owner:self options:nil];
it causes a crash, saying that one of my UIButtons is not KVC compliant, but I have properly linked all the buttons and outlets, to no avail.
Any help would be extremely appreciated! Thank you in advance!
that is faster at least 3 times than NSBundle:
ViewController2 *_viewController2 = [[[UINib nibWithNibName:#"ViewController2" bundle:nil] instantiateWithOwner:nil options:nil] objectAtIndex:0];
it will load the nib file and it will create a new instance of the ViewController2 class.
NB: if you'd like to use an already existing instance of the ViewController2 class for the nib, you will need to set that instance as owner.
When you load a nib and set the owner: property to self, iOS tries to wire up the outlets with KVC. If you don't have a UIButton with a keyPath that matches the one in the xib file inside of the class you're currently in, you'll get the crash. You need to set owner: to nil.

Reuse UI (xib files)

In my app I have two screens - first to show a user profile, the second - to edit the profile information. They are similar. I have completed the xib file for the first screen.
What's the best way to reuse it on second screen?
You should encapsulate the related elements as a custom view class. You can tackle this problem by creating views with code instead of just xibs, and I would recommend this.
But, if you would prefer to use a xib, you can create one that models the stuff you want to reuse. And then in your view controller call some code like this:
UIView* aView = [UIView alloc] initWithFrame .....];
[[NSBundle mainBundle] loadNibNamed:#"MyReusableComponent" owner:aView options:nil];
UILabel* someLabel = aView.injectedLabel; //this is alive after loading the xib
[self.view addSubView:aView];
When you create your xib, your need to set the Files Owner to a class that will respond to the setters for the properties that will be injected. (Eg your new view class). This way you can wire up the references.
For more information, look at Apple's examples of loading table cells from a xib - this is the same technique. When you load a xib and specify the owner, it will inject the values from the xib into the owner, in this case a custom view.
Dou you mean that you enter the view controller's edit mode and reuse those those elements you have created ?
Enabling Edit Mode in a View Controller
You could use UITextFields (instead of UILabels you may have logically used for show) that you change in appearance, and switch enabled on/off. As a minimal example:
Show:
self.textField.borderStyle = UITextBorderStyleNone;
self.textfield.enabled = NO;
Edit:
self.textField.borderStyle = UITextBorderStyleBezel;
self.textfield.enabled = YES;
You could of course do more on appearance, than just these basics.
In Xcode: Go to file > duplicate.
Then name your duplicated xib something like "editProfile" This will give a duplicate of your first xib that you can adjust as necessary

How to get a reference to an object in a .nib file?

so far I have done most view programming in code, but it really is cumbersome to position buttons perfectly in code. I searched for some time, maybe I'm using the wrong words for my search? Anyway, I created a UI in a nib file, it has several buttons. I loaded the .nib file into my view like this:
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"UCMenuView" owner:self options:nil];
UIView *menuView = [nibViews objectAtIndex:0];
self.delegate = (id<UCMapviewDelegate>)delegate;
// another controller does the view switching, so I'm sending him the view
[self.delegate pushView:menuView];
This is 'fine'. But I don't understand how I get a pointer to my buttons. I know about IBOutlet, but how do I connect them to the elements in the .nib? How would I connect this #property (strong, nonatomic) IBOutlet UIButton *calendar;
to a specific button in the .nib?
I tried to ctrl-drag (from that little points on the left side of the #property, but that does not work (from neither side). Excuse me if this is an easy question, but I couldn't find a clear explanation
thanks for your help
Instead of just a UIView *menuView, you need to create a custom
UIView subclass for that, e.g. UCMenuView. Define your properties on this custom
class.
Open your .xib file in the editor, select the File's Owner in
the left column and set its Custom Class in the identity
inspector to UCMenuView.
Right-click the File's Owner in the left column, and connect
your IBOutlets.
Check out this tutorial:
http://developer.apple.com/library/ios/#documentation/IDEs/Conceptual/Xcode4TransitionGuide/InterfaceBuilder/InterfaceBuilder.html
or
http://www.hongkiat.com/blog/ios-development-guide-part2-your-first-app/
Kind regards,
Bo
See Apple's documentation: Creating and Connecting an Outlet and more generally Designing User Interfaces in Xcode.

Resources