how to make a view not programmatically as cameraoverlay view - objective-c

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];
}

Related

Adding a header view to every view in an application

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.

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.
}

Replacing Storyboard Segue with pushViewController causes strange behaviour

I can't seem to figure this out for the life of me. I have a custom table view cell, in that cell I have a few buttons configured. Each button connects to other view controllers via a storyboard segue. I've recently removed these segues and put a pushViewController method in place. Transition back and forth across the various views works as expected however the destination view controller is not displaying anything! I have some code below as an example.
Buttons have this method set:
[cell.spotButton1 addTarget:self action:#selector(showSpotDetails:) forControlEvents:UIControlEventTouchUpInside];
// etc...
[cell.spotButton4 addTarget:self action:#selector(showSpotDetails:) forControlEvents:UIControlEventTouchUpInside];
// etc...
showSpotDetails Method contains this code:
- (void)showSpotDetails:(id)sender
{
// determine which button (spot) was selected, then use its tag parameter to determine the spot.
UIButton *selectedButton = (UIButton *)sender;
Spot *spot = (Spot *)[spotsArray_ objectAtIndex:selectedButton.tag];
SpotDetails *spotDetails = [[SpotDetails alloc] init];
[spotDetails setSpotDetailsObject:spot];
[self.navigationController pushViewController:spotDetails animated:YES];
}
The details VC does receive the object data.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"spotDetailsObject %#", spotDetailsObject_.name);
}
The NSLog method below does output the passed object. Also, everything in the details view controller is as it was. Nothing has changed on the details VC. It just does not render anything ever since I removed the segue and added the pushViewController method. Perhaps I am missing something on the pushViewController method? I never really do things this way, I try to always use segues...
Any suggestions?
Welcome to the real world. Previously, the storyboard was a crutch; you were hiding from yourself the true facts about how view controllers work. Now you are trying to throw away that crutch. Good! But now you must learn to walk. :) The key here is this line:
SpotDetails *spotDetails = [[SpotDetails alloc] init];
SpotDetails is a UIViewController subclass. You are not doing anything here that would cause this UIViewController to have a view. Thus you are ending up a with blank generic view! If you want a UIViewController to have a view, you need to give it a view somehow. For example, you could draw the view in a nib called SpotDetails.xib where the File's Owner is an SpotDetails instance. Or you could construct the view's contents in code in your override of viewDidLoad. The details are in the UIViewController documentation, or, even better, read my book which tells you all about how a view controller gets its view:
http://www.apeth.com/iOSBook/ch19.html
The reason this problem didn't arise before is that you drew the view in the same nib as the view controller (i.e. the storyboard file). But when you alloc-init a SpotDetails, that is not the same instance as the one in the storyboard file, so you don't get that view. Thus, one solution could be to load the storyboard and fetch that SpotDetails instance, the one in the storyboard (by calling instantiateViewControllerWithIdentifier:). I explain how to do that here:
http://www.apeth.com/iOSBook/ch19.html#SECsivc

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.

tableView footer strange behavior

Given: I have a bit of a "pop up" view that I put over my tableView within my UITableViewController. I put it there like this:
[self.navigationController.view addSubview:self.hoverView];
Problem: I cannot see this hoverView when I add a tableView footer view. Seemingly unrelated yes?
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
[self.tableView.tableFooterView addSubview:someLabel];
These appear to be mutually exclusive. I can have one, but not the other. To see what's going on, I print subview descriptions like so:
for (UIView *sub in self.navigationController.view.subviews)
{
NSLog([sub description]);
}
When the table footer view is added, this code prints nothing. Otherwise, I see the expected output of some navigationController internal views. What did I miss?!
Don't add the hoverView to the UINavigationController's view. Instead of using a UITableViewController, use a UIViewController whose view contains a UITableView as a subview. (See here for details of how to implement the rest of the UITableViewController's functionality in your UIViewController). Then add your hoverView to that view, i.e., make it a sibling of the UITableView, but a later sibling so it appears above it.
This may not solve your problem but I'd say it's your best bet: a UINavigationController is not designed to have its view manipulated directly, it's designed to have view controllers pushed onto its stack and add subviews to its view accordingly.