How to create simple menu? - iphone

I'm trying to create simple (facebook like) menu in my app. I've found several questions, most of them had accepted answer, but usually answer is to use some programs done by developers.
These programs are often in old version of xCode (one, that didn't use storyboards) and those, that were done in storyboard, were too complicated for me to implement in my app.
So I found one question, which had as an answer something like this:
1. Create your menu view controller (UITableViewController for me)
2. Hide this controller in your initial view controller :
MenuViewController *menView = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
[self.view sendSubviewToBack:menView.view];
3 Create a button (maybe pan gesture later), in it's method, do something like this:
-(IBAction)menuButtonPressed:(id)sender
{
CGRect destination = self.navigationController.view.frame;
if (destination.origin.x > 0) {
destination.origin.x = 0;
} else {
destination.origin.x +=254.5;
}
[UIView animateWithDuration:0.25 animations:^{
self.navigationController.view.frame = destination;
} completion:^(BOOL finished)
{
self.view.userInteractionEnabled = !(destination.origin.x > 0);
}];
}
What this does I guess, it's that it basically moves your view to the right by fixed length. I suppose, that your view, that you sendedToBack (your UITableView) should be exposed and interactable. However, all it does for me, is moving top view to the right, and leaving blank black-colored screen behind.
Now I think, that my problem is either bad instantiation of menuView, or just that I understanded this guide wrong.
If anyone knows, how to deal with this, I would be very thankful for an answer. Maybe there is some already done app, that is as easy as this to understand and hopefully easier to implement in my code :)
Thanks!

I would do this using container views in storyboard. You can size the left one how you like, then add a right one next to it, and in the inspector change its width to 320 -- most of it will go off the screen to the right, and it will resize its embedded controller to be full screen size. You can delete the view controller that you get with the left container view, then drag out a table view controller, and connect it from the left view with an embed segue (it will be the only choice when you control drag from the container view). I added two swipe gesture recognizers (one left and one right) to the main controller's view and connected them to the 2 methods in the main controller. Then in the main controller I have this code in the .h:
#interface ViewController : UIViewController
#property (weak,nonatomic) IBOutlet UIView *rightView;
#property (assign,nonatomic) CGRect rightRect;
#end
And in the .m, only this:
#implementation ViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.rightRect = self.rightView.frame;
}
-(IBAction)revealSidebar:(id)sender {
[UIView animateWithDuration:.3 animations:^{
self.rightView.frame = self.view.window.frame;
}];
}
-(IBAction)hideSidebar:(id)sender {
[UIView animateWithDuration:.3 animations:^{
self.rightView.frame = self.rightRect;
}];
}
#end

Related

When re-creating a UIViewcontroller's view, how do I force the rotation?

I have a view-controller that I'm re-using (for memory limitation reasons.) So, rather than push a new UIViewController, I just set a few parameters, then force this VC to reload its view. The code is something like this (triggered by a Notification callback):
- (void) reloadView: (NSNotification*) note
{
// Save parameters for reloaded view
UIWindow *window = (UIWindow*) self.view.superview;
//CGAffineTransform xfrm = self.view.transform; // Doesn't do what I want.
// Trash this one & reload the view
[self.view removeFromSuperview];
self.view = nil;
// force view reload
if (self.view == nil)
{
NSLog(#"%s ***** Why didn't self.view reload?!", __PRETTY_FUNCTION__);
}
else
{
// restore params
//self.view.transform = xfrm; // Boo-hoo!
[window addSubview: self.view];
}
}
Everything works fine except that the app is landscape only, and the newly reloaded view is added to the Window as portrait.
I tried forcing the old view's transform onto the new view but, oddly, it gave the rotation but a goofy translation offset.
Is there a way to tell a UIViewController "do your rotation, now"...?
EDIT:
I added this rather silly hack:
// restore params
self.view.transform = xfrm;
self.view.center = CGPointMake(window.bounds.size.width / 2., window.bounds.size.height / 2.);
[window addSubview: self.view];
which gives the desired result, but I'm really displeased with having such a thing in my code base. Surely there's a better way to do this?!?!
Thanks!
EDIT:
After some discussion with JPH, then answer turned out to be "don't do things that way." See comments for some of the details and the redesign that took place.
Your problem is in using this:
[window addSubview: self.view];
From the documentation:
If you add an additional view controller's UIView property to UIWindow
(at the same level as your primary view controller) via the following:
[myWindow addSubview:anotherController.view];
this additional view controller will not receive rotation events and
will never rotate. Only the first view controller added to UIWindow
will rotate.
https://developer.apple.com/library/ios/#qa/qa2010/qa1688.html
I would much prefer a design with a root view controller and the subviews being added to the root view controller's view.
Another option is to NOT kill the view and re-add it, but rather update everything that needs to be updated in that view. I am not sure I understand why you would want to kill a view and re-add it right away.

Whats the difference between a destination vc and a source vc?

im trying to learn how to perform custom segues so i can have a game menu, and when i searched on the web it seems that you have to make a custom segue class, and override -(void) perform and in that method you have to specify a made up destination vc and a source vc. and establish its location and stuff. This was the one of the code things i saw on the internet.
#implementation FromTopReplaceSegue
-(void)perform{
UIViewController *dst = [self destinationViewController];
UIViewController *src = [self sourceViewController];
[dst viewWillAppear:NO];
[dst viewDidAppear:NO];
[src.view addSubview:dst.view];
CGRect original = dst.view.frame;
dst.view.frame = CGRectMake(dst.view.frame.origin.x, 0-dst.view.frame.size.height, dst.view.frame.size.width, dst.view.frame.size.height);
[UIView beginAnimations:nil context:nil];
dst.view.frame = CGRectMake(original.origin.x, original.origin.y, original.size.height, original.size.width);
[UIView commitAnimations];
[self performSelector:#selector(animationDone:) withObject:dst afterDelay:0.2f];
}
- (void)animationDone:(id)vc{
UIViewController *dst = (UIViewController*)vc;
UINavigationController *nav = [[self sourceViewController] navigationController];
[nav popViewControllerAnimated:NO];
[nav pushViewController:dst animated:NO];
}
#end
i guess this is supposed to make a segue that appears from the top going down or something. but i have a few questions like, what is a source and dest vc, and also in the part where the code says dst.view.fram=CGRectMake(dst.view.frame.origin.x, 0-dst.view.frame.size.height,
what the heck is that 0 there, and shouldnt that be dst.view.frame.origin.y ?
well, anyways i wired this up and created a push segue from a button, but when i did this the segue only came to about 3/4 down from the top of the screen, and the bottom 1/4 was showing the bottom of my root view. and also when i tried pressing a button on my new vc after the segue the program crashes.
Any Info would help Please!
This is one of those things that aren't fully explained it looks like. Judging from your code, it looks like the "source view controller" is the root view controller(or parent view controller) and the "destination view controller" is the DetailViewController(or the child view controller).
Not sure if this confuses you more. Anyways, the parent/root view controller is the main view controller and it handles displaying of child view controllers. What I find interesting is that the creator subclassed the view of another class rather than presenting it.
Now 0 - self.view.frame.size.height = -320 on the y axis. Think of your main view as a piece of paper and think of the child view as a second piece of paper. Imagine placing the two pieces of paper one above the other(not on top.) The screen will only display the bottom piece of paper until you animate the "paper above it" down. Basically, he plans to animate from the top rather than the bottom(I guess I answered my own curiosity.)

UIPicker specific question(iphone)

I am starting to learn to use the UIPicker in an application. what I want to do is that when the user clicks on the zipcode textbox, I want the uipicker to pop up and display list of zipcodes available. Just like a drop down in c# or VB.net. Do I have to create a new view and place the uipicker there?
Thanks
You need a view for placing a text box. It does not matter if it needs to be a new view or the old view - just place the text box in the view you require.
make sure that
textField.inputView = yourPickerView;
In order to have a UIPickerView appearing in your app you do not need an additional view.
Assuming you are in a UIViewController:
#interface MyController : UIViewController {
UIPickerView* mPicker;
}
-(void)showPicker;
-(void)hidePicker;
#end
-(void)showPicker {
[self.view addSubview:mPicker];
mPicker.center = CGPoint // set out of sight
[UIView beginAnimations:nil context:nil];
// do your transformations
[UIView commitAnimations];
}
-(void)hidePicker {
// do your exit animations
}
You can also add a delegate function to the second animations to remove the mPicker from the superview.
For more information have a look at the UIView Reference.

Nested ViewControllers and their memory imprint? Improvements if this is very bad?

#Jonah pointed me to the following explanation he has provided:
Question on View Controller's view
I currently have a MainViewController whose main(root?) view contains an Add Record UIButton which is linked to the following method and is dependent on which page the user currently has open in a UIScrollView:
#pragma mark -
#pragma mark Add View Support
- (void)add:(id)sender {
MyAddViewController *controller = nil;
// Dynamically creating and allocating the Add View Controller
if (dataViewPageControl.currentPage == 0) {
controller = [[MyAddViewController alloc] initWithAddType:1];
} else {
controller = [[MyAddViewController alloc] initWithAddType:0];
}
controller.delegate = self;
// Slide the new AddViewController's view in from the right
CGPoint initialCenter = self.view.center;
controller.view.center = CGPointMake(initialCenter.x + 320, initialCenter.y);
[self.view addSubview:controller.view];
[UIView beginAnimations:#"animation" context:NULL];
[UIView setAnimationDuration:0.35];
[UIView setAnimationDelegate:controller];
controller.view.center = CGPointMake(initialCenter.x, initialCenter.y);
[UIView commitAnimations];
}
MainViewController is the delegate for MyAddViewController (which has a CANCEL/CLOSE button) and when it's close button is clicked the MyAddViewController is released through the close method in this MainViewController.
As you can see, MyAddViewController's view becomes a subview to the current root view (Bad? Bad? Bad?) MyAddViewController's view is as a FULL size view (320x416) but whose top half is transparent, so that MainViewController's view can be seen below on the top half and the bottom half contains the AddViewController's view and textfields etc....
Based on what I've read from #Jonah just now at the link at the very top this sort of design is BAD, BAD, BAD. I've noticed when testing in Instruments that MyAddViewController sometimes lags when sliding in. I'll click the ADD button to fire this method and there is sometimes a 3 - 5 sec delay before the view slides in. Some sort of delay in all the memory management and cleanup?
I'm assuming I can fix this by making a single MyAddViewController property inside MainViewController which will then prevent me from allocating and deallocating on every button click. That should make things smoother? My only worry is that this will cause a greater memory imprint at application load time. Instruments seems to show a 516KB allocation every time I click ADD for the first time in my app.
Any recommendations though on design and how to improve this? The reason I like having separate view controllers with nested views like this is because MyAddViewController has a lot of logic with Core Data and such and MainViewController is pretty busy enough already as it is. Trying to keep things modular.
Thanks in advance!
I think the best solution is to redesign your MyAddViewController to be a controller which inherits from NSObject rather than UIViewController. The MyAddViewController can then manage a subview of your MainViewController allowing you to keep your controller logic nicely encapsulated without abusing UIViewController.
I've tried to describe why nesting the views of multiple custom UIViewController classes is problematic here: http://blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers/
An add view controller is the perfect type of thing to use modal presentation for. Instead of handling all that animation yourself, just present it like this:
[self presentModalViewController:controller animated:YES];
Edit
Then I would recommend making MyAddViewController a property on MainViewController and write a custom accessor. Additionally I would defer setting it's addType (if possible) until the point at which you will actually animate the view in. This way we can just have one generic MyAddViewController hanging around, instead of having to create a new one every time the detailPageControl changes.
- (MyAddViewController *)addViewController
{
if (!addViewController)
addViewController = [[MyAddViewController alloc] init];
}
Then before you animate it...
self.addViewController.addType = 0;
if (dataViewPageControl.currentPage == 0) {
self.addViewController.addType = 1;
}

iPhone Modal View Smaller that the screen

I'm trying to do something that shouldn't be that complicated, but I can't figure it out.
I have a UIViewController displaying a UITableView. I want to present a context menu when the user press on a row. I want this to be a semi-transparent view with labels and buttons.
I could use an AlertView, but I want full control on the format of the labels and buttons and will like to use Interface Builder.
So I created my small view 250x290, set the alpha to .75 and create a view controller with the outlets to handle the different user events.
Now I want to present it.
If I use presentModalViewController two (undesired) things happen
1) the view covers all of the screen (but the status bar).
2) It is semi-transparent, but what I see "behind" it its not the parent view but the applications root view.
Ive tried adding it as a subview, but nothing happens, so Im not doing something right:
RestaurantContextVC* modalViewController = [[[RestaurantContextVC alloc] initWithNibName:#"RestaurantContextView" bundle:nil] autorelease];
[self.view addSubview:modalViewController.view];
Is it possible to do what I want?
Thanks in advance.
Gonso
I'm coding similar thing. My approach include.....
Not using dismissModalViewControllerAnimated and presentModalViewController:animated.
Design a customized full sized view in IB. In its viewDidLoad message body, set the background color to clearColor, so that space on the view not covered by controllers are transparent.
I put a UIImageView under the controllers of the floating view. The UIImageView contains a photoshoped image, which has rounded corners and the background is set to transparent. This image view serves as the container.
I uses CoreAnimation to present/dismiss the floating view in the modal view style: (the FloatingViewController.m)
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor clearColor]];
[UIView beginAnimations:nil context:nil];
[self.view setFrame:CGRectMake(0, 480, 320, 480)];
[UIView setAnimationDuration:0.75f];
[self.view setFrame:CGRectMake(0, 0, 320, 480)];
[UIView commitAnimations];
}
wangii
Thats pretty much the solution I found.
I load the view with loadNibNamed and then just add it on top with addSubView, like this:
//Show a view on top of current view with a wait indicator. This prevents all user interactions.
-(void) showWaitView{
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"WaitView" owner:self options:nil];
#ifdef __IPHONE_2_1
waitView = [ nibViews objectAtIndex: 0];
#else
waitView = [ nibViews objectAtIndex: 1];
#endif
CGFloat x = self.view.center.x - (waitView.frame.size.width / 2);
CGFloat y = self.view.center.y - (waitView.frame.size.height / 2);
[waitView setFrame:CGRectMake(x,y,waitView.bounds.size.width,waitView.bounds.size.height)];
[self.view addSubview:waitView];
}
Could you elaborate on points 3 and 4?
What I did to give the view the round rect aspect is put it inside a round rect button.
This code will actually allow you to have a small floating view, but if the view is smaller that its parent, the user could interact with the visible part of the parent.
In the end I create my view with the same size, but kept the code just in case.
Gonso
I would strongly consider using a navigation controller to slide in your subview instead of overlaying it. This is the expected model and any small benefit you may think you'll get by doing it your own way will be greatly offset by the principle of (least) surprise.
If you really really have to do it this way, I believe the trick is to add the first table view as a subview of a transparent "holding" view that the view controller maintains. Then add your new sub view as another subview of that.
Again, if you really want to do this, instead of adding a transparent "holding" view, since this pop-up is essentially modal, I would make it a subview directly of the window.
You might want to put in a transparent black shield behind it to prevent touches on the background and focus input on the popup.
But seriously, consider either popping a controller on the stack or using that alert view. Unless you've hired a $$ designer, it's probably not going to look appropriate on the iPhone.
What I did was create a UIViewController on top of my UINavigation controller in my app delegate and made it a property of a singleton object for convenience:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//--- create root navigation controller
self.window.rootViewController = self.navigationController;
//--- create view controller for popups:
popupViewController = [[BaseViewController alloc] init];
popupViewController.view.backgroundColor = [UIColor clearColor];
popupViewController.view.hidden = true; //for rendering optimisation
[self.window addSubview:popupViewController.view];
[AppState sharedInstance].popupViewController = self.popupViewController;
//--- make all visible:
[self.window makeKeyAndVisible];
return YES;
}
At any point in my app, I can then call e.g.
MyViewController * myVC = [[UIViewController alloc] init];
//... set up viewcontroller and its view...
// add the view of the created view controller to the popup view:
[AppState sharedInstance].popupViewController.view.hidden = false;
[[AppState sharedInstance].popupViewController.view addSubview:myVC.view];
The BaseViewController used on the top just inherits from UIViewController and sets up a full-screen view:
//----- in BaseViewController implementation
- (void)loadView {
//------- create root view:
CGRect frame = [[AppState sharedInstance] getScreenFrame];
rootView = [[VCView alloc] initWithFrame:frame];
rootView.backgroundColor = [UIColor whiteColor];
self.view = rootView;
}