How to make custom header view? - ios

I started a project with storyboards and it has a lot of views, each one of them has the same header (with an image, company name and two buttons). I want to do this once in the main View, and make it reusable for the other views.

You could create a containing view controller with your header. Then, add your current root view controller (the current initial view controller in your storyboard) to this containing view controller.

Create a custom view named HeaderView with XIB:
in HeaderView.h you have to define a protocol and the properties related to controls you want to display in header.
#protocol HeaderViewDelegate <NSObject>
- (void)onBackButtonTapped;
#end
#interface HeaderView : UIView
#property (nonatomic, strong) IBOutlet UIImageView *headerImageView;
#property (nonatomic, weak) id <HeaderViewDelegate> delegate;
-(IBAction)backButtonTapped:(id)sender;
#end
In Header.m:
#implementation HeaderView
-(IBAction)backButtonTapped:(id)sender
{
if (delegate != nil && [delegate respondToSelector:#selector(onBackButtonTapped)])
{
[delegate onBackButtonTapped];
}
}
#end
Now what you have to do is to create a BaseViewController and every controller should be extended from this controller in order to show the same header in all the views.
Let me know you have any questions regarding the implementation.

I have managed to create reusable views according to this post.
Create a class named SharedView which extends UIView
Create a nib with the same name
Set the ‘File’s Owner’ property in the nib to the SharedView class
Create an outlet from your view to the SharedView class, and call it ‘contentView’
Replace initWithCoder with the following:
-(void)awakeFromNib {
//Note that you must change #”BNYSharedView’ with whatever your nib is named
[[NSBundle mainBundle] loadNibNamed:#"BNYSharedView" owner:self options:nil];
[self addSubview: self.contentView];
}
In your ViewControllers create views, and set their class to SharedView
Another solution is to use Container View Controller, and as to your specific situation, I have tried to implement a demo for you, please try it!
Hope it could help!

Why don't you use NavigationController as rootviewControoler with leftBarbutton and rightbarButton
so that it can be in your whole app

Related

iOS MVC implementation with custom views

When the views are simple, their IBActions and IBoutlets are in viewcontroller, viewcontrollers assigns respective models to be loaded and viewcontroller get notified when models are prepared.
As My project contains lot of custom views for each viewcontroller, I want to implement actions in custom view itself and set data from controller (ViewController).
I should be able to use the same controllers and models for both iPhone and iPad where only UI changes.
I am concerned about how to pass data from view to viewcontroller and displaying data back on view when model changes?
Can anyone please suggest me to pass data between views <---> viewcontroller (controller) <---> model?
To do this I use Delegate design-pattern. It looks like this :
MyView.h
#protocol MyViewDelegate <NSObject>
- (void)customViewDidSomething;
#end
#interface MyView : UIView
#property (nonatomic, assign) id<MyViewDelegate> delegate
#end
MyView.m
- (void)userDidSomething {
[_delegate customViewDidSomething];
}
MyViewController.h
#import "MyView.h"
// ViewController has to implement the protocol
#interface MyViewController <MyViewDelegate>
#property (nonatomic, retain) IBOutlet MyView myView;
MyViewController.m
- (void)viewDidLoad { // Set the delegate somewhere
_myView.delegate = self
}
- (void)customViewDidSomething {
// Ok VC is aware that something happened
// Do something (tell subview to do something ?)
}
Instead of using different custom views, try using a UIViewController and then use the viewcontroller's view to display your UI. Also, this will also ensure that you will be able to communicate between the views and controller efficiently without confusion.

Custom Subview on a iOS view has null outlets

I have a view called FeedView, handled by FeedViewController.
I also have a XIB called "NearestStore" which is handled by a view controller named "NearestStoreViewController". NearestStore xib has labels, buttons, etc. In the view controller I have outlets that are connected to the subviews in NearestStore.xib.
NearestStore inherits from UIButton (so it's easier to handle click event).
On FeedViewController.xib I have a UIButton that has been set to be of type NearestStore.
So far so good. This is on my FeedViewController:
__weak IBOutlet NearestStoreButton *btn_nearestStore;
The outlet is connected on the xib to the outlet.
NearestStoreViewController has several outlets to subviews like:
#property (nonatomic, weak) IBOutlet UILabel *lbl_distance;
#property (nonatomic, weak) IBOutlet UIImageView *img_distance;
For some reason, on my FeedViewController the reference to btn_nearestStore is fine, but all the subviews are nil.
For example:
btn_nearestStore.lbl_distance
is nil
What am I missing?
This sounds exactly as the system is supposed to work. It is not easy to create a custom widget using xibs.
Here's how it works:
Your FeedViewController will preform xib loading for the corresponding FeedView.
During this load, it notices the NearestStoreButton subview. As a consequence, it creates such a view using the - (id)initWithCoder: message on the NearestStoreButton class. It will not magically notice the corresponding .xib nor the corresponding viewController.
If you need to use a xib within a xib, you need to do the loading manually for all subviews. Keep in mind that you somehow need to create/use the appropriate owners (view controllers) for these secondary xibs.
It's hard to tell from your description, but this sounds like a problem with "owner" of the loaded NearestStoreButton XIB. When you load a NIB, you give the loader an owner, and its this owner on which most outlet bindings and actions are made. If you're loading your NearestStoreButton with UINib, then when you call instantiateWithOwner:options:, make sure you pass the object on which the outlets should be set as the owner.
When are you calling the outlet? If you are trying to access the property in the initWithCoder method of the view, it's not guaranteed that object has been instantiated.
If you access your property in the awakeFromNib method within the view you should be able to get it. For instance, I have a custom view, and my code looks as such:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
//Don't style the subviews in here since they won't be initialized
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self styleViews];
}
- (void)styleViews
{
//access my properties and style them in here
}
Following post contains detailed explanation about creating custom views using Nib:
creating custom view using Nib
After I create the customInit as mentioned in the post, I am able to get the IBOutlets allocated.

iOS: Simple UIVIew not getting added to another UIView

I have a storyboard driven iOS application.
My objective is to add a small square view (defined as other XIB) to another UIView which is on main ViewController.
*// Please note that, I have assigned the Class names in Identity Inspector correctly*
On the main ViewController, I added a UIView (myHolderView) manually and added a UIView member in the ViewController interface.
//.h
#interface ViewController : UIViewController
#property(nonatomic, strong) IBOutlet UIView *myHolderView;
#end
And, I have connected this IBOutlet by "BlueString" :)
Now, I have added another XIB with single view - Called it MySquare.xib and added a label on it.
And, added a UIView class with a .h and .m files. And, "BlueString" connections are made.
#interface MySquare : UIView
#property(nonatomic, strong) IBOutlet UILabel *myLabel;
#end
Now, in the implementation:
#implementation ViewController
#synthesize myHolderView;
- (void)viewDidLoad
{
[super viewDidLoad];
MySquare *sq=[[MySqure alloc]init];
// Do any additional setup after loading the view, typically from a nib.
[self.myHolderView addSubview:sq];
}
When I run it in the simulator, it just shows the main view, but on it the square view is not coming.
I know that main view had loaded because... I changed the colour of it so that I could see.
Please help.
Probably you don't do in your MySquare.m file the view initialisation using your xib.
- (id) initWithFrame:(CGRect)frame {
NSLog(#"This is called if you add programatically this view");
self = [super initWithFrame:frame];
if (self) {
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:#"MyIBView" owner:self options:nil] objectAtIndex:0]];
}
return self;
}
If you plan to add your xib using Interface Builder, you have to implement similarly the initWithCoder method:
-(id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:#"MyIBView" owner:self options:nil] objectAtIndex:0]];
}
return self;
}
Plese go through in this checklist, it might help others too:
Checklist adding a custom view with Interface Builder layout (xib):
1) create a new xib (File->New->IOS/User Interface->View), name it (eg. MyIBView)
2) create a new class (File->New->IOS/Cocoa Touch->Objective-C class), make sure it is a Subclass of UIView! Name it MyIBView.
Now you have MyIBView.xib, MyIBView.h and MyIBView.m files added to your project
3) Edit your MyIBView.xib: select the File's owner and using the Identity inspector (3rd icon) select Class you created recently, MyIBView.
4) Select the top level View (if its a new xib, only this exists) and make sure using the Identity inspector that the Class is UIView (grey) and not overwritten
5) Now you can add IBOutlets and/or IBActions to your MyIBView.h file and you can do connections using Interface Builder in your MyIBView.xib
Using this view programatically in another view:
6a) Create the first method (initWithFrame) above in your MyIBView.m file if you would like to use this custom view programatically
7a) include your MyIBView.h file where you would like to add your custom view programatically
8a) now you can add your view:
MyIBView *myIBView = [[MyIBView alloc] initWithFrame:CGRectMake(0,0,100,100)];
Using this view in Interface Builder in another view:
6b) Create the second method (initWithCoder) above in your MyIBView.m file if you would like to use this custom view in Interface Builder
7b) In your another view drop an UIView to your view canvas. Adjust the size and modify the Class using the Identity inspector to MyIBView.
Please note that you cannot see design time your custom view, only after trying to run the code.
Hope it helps, and you can find your miss-configuration!

UICollectionView subclass to be reused in multiple viewControllers

I want to create a self-contained UICollectionView subclass (acting as its own data source and delegate) so that I could load it in different viewControllers. Here's what I have so far:
CustomCollectionView.h
#interface CustomCollectionView : UICollectionView <UICollectionViewDataSource, UICollectionViewDelegate>
#property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
#end
CustomCollectionView.m
#import "SSCalendarView.h"
#implementation SSCalendarView
#synthesize collectionView;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
[self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"Identifier"];
[self addSubview:collectionView];
}
return self;
}
// Below are UICollectionViewDataSource and UICollectionViewDelegate methods
#end
CustomCollectionView.xib
Contains only one view - UICollectionView. It's class is set to CustomCollectionView
File's Owner's class is also set to CustomCollectionView
File's Owner is UICollectionView's delegate and data source
I understand that I have quite a few things wrong here. But perhaps we could use this as a starting point.
My questions are:
How to implement this sub-class correctly? I want to load the view fully from xib
To begin with, and aside from potential MVC violation (this sub-class would do it all), can a UICollectionView be its own data source and delegate?
If above is possible, how do I correctly create an instance of this subclass to use in my view controllers?
There already exists an object which you can use for this purpose - UICollectionViewController. This can be subclassed and added to any view controller (as a child view controller) and already contains a collection view which it is the datasource and delegate for.
The problems with your current approach are:
As you point out, you're putting too much responsibility on one object by having a view be its own datasource and delegate
File's owner for a xib can't be an object from within the xib. When you load the xib, the object you send to the owner argument is the file's owner. I've no idea what you actually end up with using the code you currently have.
I'm not sure why you insist on using a xib anyway - what does this give you, except the headache of an extra file and the complexity of nib loading? Just create a collection view controller, you can specify the layout and register cells in the init and viewDidLoad methods.
First of all making view to act like view controller is violation of MVC, ask you've said so - so you shouldn't probably do it.
Theoretically it's possible to force view to act as delegate & data source but I wouldn't recommend it.
If you still want to do it - just assign delegate & data source to self:
self.dataSource = self;
self.delegate = self;
and adopt UICollectionViewDelegate and UICollectionViewDataSource protocols in .h file

How can I load nib from UIViews in iOS 4?

How can I load nib from a UIView in iOS 4 ?
I don't have a UIViewController, since I'm adding this custom view to a parent view.
I want to initialize this custom view from a nib file. Which initializer should I use ? initWithFrame or initWithCoder ?
So far, I've only assigned the custom class in the interface builder. (See pic: http://cl.ly/7pmj). Is this necessary ? If so, it is still not working, why is it not enough ?
NB I've found this related question in StackOverflow. However, it doesn't explain how to initialize the view itself with the nib.
I need to customize drawRect method, for this view, so I can't just add a subview from a nib file. I need the view itself being initialized from the nib file.
thanks
Yes, it's a must to change the custom view class in your nib file.
Make sure two things:
1. Go to yourCustomView.nib file, select the view in object, then make sure the custom class is correct under the identity inspector.
2. In the youCustomView.m file, make sure you have this part:
- (id)initWithCoder:(NSCoder*)coder
{
if ((self = [super initWithCoder:coder]))
{
//add custom change after init from nib.
}
return self;
}
Use the method from the related question (NSBundle loadNibNamed) and make sure the owner in that call is an object that has an IBOutlet for the view in question.
Hook that iboutlet up to the view in interface builder (you will need to set the class of the owner in IB to the class of the owner in your load nib call).
The nib will load the view into that iboutlet variable.
In .h file:
IBOutlet UIView *myView;
in .m file:
[[NSBundle mainBundle] loadNibNamed:#"viewToLoad" owner:self options:nil];
[someOtherView addSubView:myView];
The rest is in IB.

Resources