Adding a header view to every view in an application - objective-c

I have a header that I would like to add to every view in my program. Rather than manually do this I've been trying to find a way to insert this "header view" above every view that gets loaded. In the sample below, I tried using the insertSubView method but this inserts the entire view and not just the tiny bit of header content I'm interested in.
[self.view insertSubview:self.headerController.view aboveSubview:self.indexController.view];
[self.view insertSubview:self.indexController.view atIndex:0];
Does anyone know how to do this correctly? I'm using Xcode 5.

You're inserting the views of the headerController and indexController on your other view controllers' views. Generally, though, you want to be careful about keeping your view hierarchy synchronized with your view controller hierarchy. The importance of this is discussed in some detail in WWDC 2011 video Implementing UIViewController Containment.
So, generally you'd instantiate the appropriate indexController and headerController, call addChildViewController, add the subviews, and then call didMoveToParentViewController for each of those two view controllers. For more information, see Creating Custom Container View Controllers section of the View Controller Programming Guide for iOS. But you app's view controllers might do something like:
HeaderController *headerController = ... // instantiate however is appropriate
[self addChildViewController:headerController];
headerController.view.frame = [self frameForHeaderController]; // define the frame/constraints as appropriate
[self.view addSubview:headerController];
[headerController didMoveToParentViewController:self];
You'd then repeat that process for the indexController.
Just make sure to (in addition to doing the custom container calls) either set the frame of the view controller's frame or define auto-layout constraints that will dictate the placement of the view (like you do for any programmatically added view). If you don't specify the frame, it may well end up being CGRectZero, which is obviously not what you intended.
You might, though, consider flipping this around. Make a custom container view controller that includes your header and any other UI elements that persist for every view. Then make your app's view controllers as child controllers of that container "parent" view controller. If either the header controller's view or the index controller's view navigates your app between various content view controllers, this sort of structure might make more sense.
I think if you watch that WWDC video and/or review the Creating Custom Container View Controllers, this will make more sense.

I'd suggest to create a base view from which you will subclass for every views that need this header.
You could have a BaseView.h that would look like:
#interface BaseView : UIView
#property (nonatomic) UIView *someHeaderView;
#end
And the implementation (.m) looking like this:
#implementation BaseView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.headerView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(frame), 150.0)];
[self.view addSubview:self.headerView];
}
return self;
}
#end
Then have every views that requires a header subclass it:
#interface SomeViewRequiringHeader : BaseView
#end

Try reversing the order of the two:
[self.view insertSubview:self.indexController.view atIndex:0];
[self.view insertSubview:self.headerController.view aboveSubview:self.indexController.view];
Also, since headerController is a UIViewController, it probably has a nib file with a default size of full screen. If you want to only be a certain height, you have to modify that nib file with the appropriate height.

Related

Passing data between multiple uiview in uiscrollview

I have a ViewController that contains a uiscrollview and I create 2 uiview as pages inside the uiscrollview with paging and scrolling enabled:
_scrollView.frame = CGRectMake(_scrollView.frame.origin.x, _scrollView.frame.origin.y, _scrollView.frame.size.width, _scrollView.frame.size.height);
_scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width, _scrollView.frame.size.height*PAGES_NUMBER);
_scrollView.delegate = self;
[self.view addSubview:_scrollView];
//PAGE 1
genderViewController = [[GenderViewController alloc]init];
genderViewController.view.frame = CGRectMake(0.0,
_scrollView.frame.size.height*0,
_scrollView.frame.size.width,
_scrollView.frame.size.height);
[_scrollView addSubview:genderViewController.view];
//PAGE 2
ageViewController = [[AgeViewController alloc]init];
ageViewController.view.frame =CGRectMake(0.0,
_scrollView.frame.size.height*1,
_scrollView.frame.size.width,
_scrollView.frame.size.height);
[_scrollView addSubview:ageViewController.view];
The thing is that I'm wondering how can I pass data between this 2 uiview.
For example, I have a UITextField in the first page "genderViewController" and I want to show this content in and UILabel in the second page "ageViewController" once I scroll to the second page.
I know how to pass data between 2 different view controllers in a navigation view, but I don't know how to do this at this stage.
I would appreciate if you guys could help me with that.
Thanks!
It is pretty much the same as it is in a navigation view. Aparently your two view controllers are nested in that scroll view which is owned by a third, lets say the master, view controller.
The most clean way of implementing this would be if the view controller with the UITextField (genderViewController) would call any delegate method that you define of the master view controller. For that the master would set itself as delegate of genderViewController (you may need to add a delegate protocol and a setter).
If you want to implement it as in the books, then declare the callback/delegate method in a protocol which the genderViewController conforms to.
Upon this method being called your master view controller would call a setter of the ageViewController. Within this setter ageViewController will set the value of the UILable
Delegate methods maybe suitable for some specific operations. But I would find it - conceptually - more appropriate to use Key/Value Observing in such cases.
Documentation can be found at: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOCompliance.html
For your case, ageViewController can observe changes for a keyPath (say textField.text) in genderViewController.

how to make a view not programmatically as cameraoverlay view

I need to create a cameraoverlay view(to add on ZBar sdk reader), my question is how can I create all objects I need not programmatically. right now I create all objects programmatically and then add them to myView, and use myView as cameraoverlay view.
[self.myView addSubview: myImage];
[self.myView addSubview: self.mylabel];
[self.myView addSubview: myButton];
reader.cameraOverlayView=self.myView;
I tried to add another control view and added a view to it then made a tabbar and tried this code which doesn't work:
TestViewController *test=[[TestViewController alloc]init];
reader.cameraOverlayView=test.testView;
making objects programmatically is hard for me is this possible to find source code for objects which created in xcode, for example when I create a custom button in xcode can I find source code which has generated for this button and just copy it in my program.
There are several approaches that will work
Create a custom conainer view in code. Add the zbar view, and any custom views (scanner graphics, etc on top). This is what i recently did.
Create a xib-baased view, and include the zbar view in that. See the 'object' component.
Load custom views from xibs and add them.
If you have the time, I recommend you invest to learn how to create views programmatically.
This involves:
in your view contoller override the loadView method.
return a custom sub-class of UIView that contains sub-views. These include the zbar view and your overlays.
learn how to use the layoutSubviews method on UIView. Hint set your child view's frame relative to thecparent views bounds.
There are loads of custom components on Github. Check out DCSwitch or CMPoptip, or read up on contributions to ManiacDev.
* As requested - adding subviews after successful scan *
- (void)readerView:(ZBarReaderView*)view didReadSymbols:(ZBarSymbolSet*)symbols fromImage:(UIImage*)image
{
for (ZBarSymbol* symbol in symbols)
{
[self presentScannedOverlay];
[_scanInProgressOverlay setAnimating:NO];
[_readerView stop];
[_delegate didScanPayload:symbol.data];
break;
}
}
- (void)presentScannedOverlay
{
//Be sure to override layoutSubviews, so that you can position the view below,
//relative to its parent. . if you already know the size of the parent, just replace
//CGRectZero with CGRectMake(some values)
_scannedOverlayView = [MyOverlayView alloc] initWithFrame:CGRectZero];
[self addSubView:scannedOverlayView];
}

How to embed nibs programmatically?

I have a UIViewController with an UIScrollView in it.
Then I created a new UIView subclass with some properties that I want to programmatically add in this UIScrollView until my data ends.
Something that should look like this picture:
I have read other topics that does that by creating a view and setting its class to the custom view subclass, but I want to do it programmatically and its not working.
how would I do that?
From your image it looks like you're looking to load views from a nib and add them as subviews of your UIScrollView. To do this have a look at the UINib documentation.
You want to create your nib and set it's main view to be an instance of your UIView subclass then load the nib in viewDidLoad of your viewController, and add the nib's views as subivews of your scrollview (which I'm assuming is a subview of your viewController's view).
You can instantiate a nib with instantiateWithOwner:options:.
This method unarchives each object, initializes it, sets its
properties to their configured values, and reestablishes any
connections to other objects
To get the array of views from a nib you do something similar to:
UINib *myNib = [UINib nibWithNibName:#"myNib" bundle:[NSBundle mainBundle]];
NSArray *viewsFromNib = [myNib instantiateWithOwner:nil options:nil];
I'll assume we're inside a UIViewController and we're somewhere in (or after) viewDidLoad. You would then use the array from above and add the views as subviews of your scrollview. You may need to set the frames of these views to place them properly, but that should be trivial.
UIView *aView = [viewsFromNib objectAtIndex:0];
[self.scrollView addSubview:aView];
I hope that sets you in the right direction.
Edit:
If you want more information you may need to read deeper into how nibs work to manage your expectation. Linked with the UINib documentation is the 'Resource Programming Guide' in particular the nib section
André, this can be done with relative ease. make sure to import the class that you want to embed. then to create them with your normal
ClassName *subview=[[ClassName alloc]init];
[subview.view setFrame:CGRectMake(x,y,width,height)];
[self.view addSubview:subview.view];
which will add it to the x,y coordinates you specify with the size specified by your width, height. you can do this in the viewDidLoad or whenever you need them to be created.

Change self.view's class type

I have a app out for testing right now that's almost completely done - just a few bug fixes left. Unfortunately, the customer decided that they'd like the entire main page of the app to be inside of a scroll view (so that there's more room for the table at the bottom). I already have everything set up and I don't really want to move everything and change references. Is there an easy way to change the class of the main view to a scroll view? I've already tried changing the class in IB and setting the class type in the init method. If there isn't I'll probably just throw the top section of the view into a nib file and load it as a custom cell.
-EDIT- I ended up changing the class type in IB and then doing
[(UIScrollView *) self.view setScrollEnabled:YES];
[(UIScrollView *) self.view setContentSize:CGSizeMake(0,2000)];
in viewDidLoad. Thanks for the help, wish I could accept all your answers.
When you are referring to [self view], I am going to assume you mean in a view controller. The view of a view controller can be any view that derives from UIView. Thus a scrollview is completely acceptable.
I don't really want to move everything and change references.
what would you have to move? why would you have to change references? Only thing you should need to do is add a scroll view to your view controller, set the view controllers view to it, and add the current view as a subview to the new scroll view. No references need to be changed, nothing has to be moved.
Refer to loadView method in documentation of view controller.
Here is a simple (untested!) example
- (void)loadView {
UIScrollView *scrollView = [[UIScrollView alloc] init] autorelease];
//Set the properties of scrollview appropriately....
self.view = scrollView;
}
Now the root view of your view controller will be a scroll view.
Note
- As the documentation states, do not do this if you are using interface builder to initialize your views/view controller. I could not tell from your description if this was the case or not. If it is, you should be able to change the view type in interface builder.
You need to set the contentSize property of your scrollview.
Since you are using IB, the easiest way to do this is to put all your UI elements into a view and add this single view to your scroll view. In the viewDidLoad method, set the content size of the scrollview to be the same size as the view that contains all your UI.
As an aside, there are much easier ways to reference views than walking down the view hierarchy, as you seem to be doing. viewcontroller.view.something.tableview. Add a connection to the tableview from your view controller in IB and it doesn't matter where that tableview is in the view hierarchy. You'll always be able to reach it from viewcontroller.tableview, no matter how you rearrange your nibs.
I think you have to use a pointer with proper type. Example for Google Maps: let's say you changed you base view's class to GMSMapView.
MapViewController.h
#property GMSMapView *mapView;
MapViewController.m
-(void)awakeFromNib{
[super awakeFromNib];
self.mapView = (GMSMapView*)self.view;
// ... etc.
}

iPhone subview design (UIView vs UIViewController)

I'm designing a simple Quiz application. The application needs to display different types of QuizQuestions. Each type of QuizQuestion has a distinct behavior and UI.
The user interface will be something like this:
alt text http://dl.getdropbox.com/u/907284/Picture%201.png
I would like to be able to design each type of QuizQuestion in Interface Builder.
For example, a MultipleChoiceQuizQuestion would look like this:
alt text http://dl.getdropbox.com/u/907284/Picture%202.png
Originally, I planned to make the QuizQuestion class a UIViewController. However, I read in the Apple documentation that UIViewControllers should only be used to display an entire page.
Therefore, I made my QuizController (which manages the entire screen e.g. prev/next buttons) a UIViewController and my QuizQuestion class a subclass of UIView.
However, to load this UIView (created in IB), I must[1] do the following in my constructor:
//MultipleQuizQuestion.m
+(id)createInstance {
UIViewController *useless = [[UIViewController alloc] initWithNibName:#"MultipleQuizQuestion" bundle:nil];
UIView *view = [[useless.view retain] autorelease];
[useless release];
return view; // probably has a memory leak or something
}
This type of access does not seem to be standard or object-oriented. Is this type of code normal/acceptable? Or did I make a poor choice somewhere in my design?
Thankyou,
edit (for clarity): I'd like to have a separate class to control the multipleChoiceView...like a ViewController but apparently that's only for entire windows. Maybe I should make a MultipleChoiceViewManager (not controller!) and set the File's Owner to that instead?
You're on the right track. In your QuizController xib, you can create separate views by dragging them to the xib's main window rather than to the QuizController's main view. Then you can design each view you need according to your question types. When the user taps next or previous, remove the previous view and load the view you need based on your question type using -addSubview on the view controller's main view and keep track of which subview is currently showing. Trying something like this:
[currentView removeFromSuperView];
switch(questionType)
{
case kMultipleChoice:
[[self view] addSubview:multipleChoiceView];
currentView = multipleChoiceView;
break;
case kOpenEnded:
[[self view] addSubview:openEndedView];
currentView = openEndedView;
break;
// etc.
}
Where multipleChoice view and openEndedView are UIView outlets in your QuizController connected to the views you designed in IB. You may need to mess with the position of your view within the parent view before you add it to get it to display in the right place, but you can do this with calls to -setBounds/-setFrame and/or -setCenter on the UIView.
Yeah, IB on iPhone really wants File's Owner to be a UIViewController subclass, which makes what you want to a bit tricky. What you can do is load the nib against an existing UIViewController instead of instantiating one using the nib:
#implementation QuizController
- (void) loadCustomViewFromNib:(NSString *)viewNibName {
(void)[[NSBundle mainBundle] loadNibNamed:viewNibName owner:self options:nil];
}
#end
That will cause the runtime to load the nib, but rather than creating a new view controller to connect the actions and outlets it will use what you pass in as owner. Since we pass self in the view defined in that nib will be attached to whatever IBOutlet you have it assigned to after the call.