I am working off the base navigation template which has a master-view holding the navbar etc and finally loads a view inside it when the app is built.
I am wondering how to make it so the user can navigate several views inside this master view without adding anything to the navigation stack like you normally would when changing views inside a navigator app.
The way I plan to transition these views will be with a swipe gesture..
any help would be greatly appreciated, hopefully I have explained my problem well enough as its quite a hard thing to explain.
I'd go about this using a UIScrollView with pagination.
But in the case that doesn't work for you, try this:
- (void) swipedScreen:(id) sender {
//I'll leave getNewView to you to implement based on how you want to get the new view
UIView *newView = [self getNewView];
self.view = newView;
}
- (void) setupSwipeGestureRecognizer {
UISwipeGestureRecognizer *swipeGesture = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipedScreen:)] autorelease];
swipeGesture.direction = (UISwipeGestureRecognizerDirectionUp|UISwipeGestureRecognizerDirectionDown);
[window addGestureRecognizer:swipeGesture];
}
- (void) viewDidLoad {
[self setupSwipeGestureRecognizer];
}
And don't forget to remove the gesture recognizer wherever you feel appropriate.
Related
I'm quite new to iOS development and I am stuck. Currently I am using one tab controller to switch between two view controllers (list and map view). This made it easier to use storyboard to configure the look of the two views.
Now the requirements have changed and the app needs to have one view controller with a segmented control that on click, displays either the list or the map view. In order to do this, I would need to make one view controller that can display list/map view.
I understand how the segmented controller part works, but I'm just stuck on how I can go about having two views with one or the other displayed in the same area.
How can I go about having two views in one view controller (if possible, utilizing storyboard)?
Thanks in advance!
You should not have two main views in a single view controller, instead you need to create one view controller per view that you want to show. However you can certainly have multiple subviews in a single view controller, which may be what works for you.
There are a number of approaches to solve this the problem, the correct approach would be to create a container UIViewController, and add as its childs the 2 viewcontrollers you want to show, them simply set the view to the view controller you want to display, but that would probably be overly complicated since you mention you are new to iOS development.
Therefore an easy solution (not sure if you can implement this in storyboard - since I don't like it), would be to have a single view controller, with the tabs, and 2 subviews of the main view, then you can simply switch between views by doing something like this:
[self.view addSubview:view1];
//to switch
[view1 removeFromSuperview];
[self.view addSubView:view2];
alternatively, you do not really need to remove it from superview but just hide it, and then use bringSubViewToFront to show the view that you need.
If you want to use the other approach I would recommend looking for this video the WWDC 2011 video titled "Implementing UIViewController Containment". This other question should be useful to: UISegmented control with 2 views
Hope that helps.
Using storyboard api you can switch between 2 viewControllers
- (void)viewDidLoad {
[super viewDidLoad];
UIViewController *viewController = [self viewControllerForSegmentIndex:self.typeSegmentedControl.selectedSegmentIndex];
[self addChildViewController:viewController];
viewController.view.frame = self.contentView.bounds;
[self.contentView addSubview:viewController.view];
self.currentViewController = viewController;
}
- (UIViewController *)viewControllerForSegmentIndex:(NSInteger)index {
UIViewController *viewController;
switch (index) {
case 0:
viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"FirstViewController"];
break;
case 1:
viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
break;
}
return viewController;
}
- (IBAction)segmentChanged:(UISegmentedControl *)sender {
UIViewController *viewController = [self viewControllerForSegmentIndex:sender.selectedSegmentIndex];
[self addChildViewController:viewController];
[self transitionFromViewController:self.currentViewController toViewController:viewController duration:0.0 options:UIViewAnimationOptionTransitionNone animations:^{
[self.currentViewController.view removeFromSuperview];
viewController.view.frame = self.contentView.bounds;
[self.contentView addSubview:viewController.view];
} completion:^(BOOL finished) {
[viewController didMoveToParentViewController:self];
[self.currentViewController removeFromParentViewController];
self.currentViewController = viewController ;
}];
self.navigationItem.title = viewController.title;
}
This is in reference to iOS tutorial by Raywenderlich. Hope this helps
With Storyboard it is possible in this way.
Create UIViewController with UISegmentControl and UITableView+UITableViewCell added to it.
Now you want to add MKMapView as well, hoverer, if you simply try to place the MapView on the ViewController, it will be added as new TableView cell, which is not what we want.
That's why you should not do it so. Instead, MapView has to be added to Storyboard's List of ViewControllers
Adjust the size and origin of MapView to be the same as TableView ones.
Now, setHidden to YES for either TableView of MapView, create and synthesize outlets for them. Then in Segment control Value Changed method implement switching:
- (IBAction)switchView:(id)sender {
self.theTableView.hidden = !self.theTableView.hidden;
self.theMapView.hidden = !self.theMapView.hidden;
if (!self.theTableView.hidden) {
[self.theTableView reloadData];
}
}
I'm trying to mimic the facebook ios side menu and have it working however the issue I am having is that I cannot send the sidemenu to the back as discussed in another question on SO iphone facebook side menu using objective c. I'm not using the library suggested but instead using the code that was suggested. I have
- (void)viewDidLoad
{
NSLog(#"View Did Load is running");
activitySpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activitySpinner.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activitySpinner.center = self.view.center;
[self.view addSubview:activitySpinner];
SideMenuView *myDelegate = [[SideMenuView alloc] init];
[self setSideMenuDelegate:myDelegate];
//set the delegate's currentViewController property so that we can add a subview to this View.
[sideMenuDelegate setCurrentViewController:self];
//sideMenu = [[SideMenuView alloc] initWithNibName:#"SideMenuView" bundle:nil];
[self.view addSubview:myDelegate.view];
[self.view sendSubviewToBack:myDelegate.view];
[super viewDidLoad];
self.searchDisplayController.searchBar.scopeButtonTitles = nil;
[self fetchCustomers];
// Do any additional setup after loading the view, typically from a nib.
}
In my controller where I want the side menu but the view seems to get loaded into the current view instead of just going to the back so it can be seen when I slide the menu over.
Can someone help me get the myDelegate view to the back?
I am not entirely sure what you are trying to accomplish, so I have to guess. It sounds like you want to hide myDelegate.view behind self.view. It won't work this way.
sendSubviewToBack: sends the subview to the back of the view hierarchy of the sender, in your case, self.view. It will never send a subview below its superview.
You can instead add myDelegate.view as a subview to self.views superview, and put it behind self.view:
[[self.view superview] insertSubview:myDelegate.view belowSubview:self.view];
I've decided to just go with https://github.com/Inferis/ViewDeck and let that manage the views.
I have a simple app that have 3 views, HomeView, MenuView and GameView.
In the HomeView I have 2 buttons (Menu and Start Game). When the menu button is clicked, I open the MenuView using the following code:
- (IBAction)displayMenu:(id)sender{
MenuView *mv = [[MenuView alloc] init];
[self.view addSubView:[mv view];
[mv release];
}
In the MenuView, I have a button that will allow the user to return to the HomeView. When this button is clicked, I use the following code to return to the HomeView
- (IBAction)returnToHome:(id)sender{
HomeView* hv = [[HomeView alloc] init];
[self.view addSubView:[hv view];
[hv release];
}
The above code is working but is this the correct way of doing it? I was under the impression that when I call the addSubView, the view will be retain so If keep going back and forth between HomeView and MenuView, will i have multiple instance of HomeView and MenuView retained since I keep calling addSubView from each of the view?
Thank you.
You could use the UINavigationController, which will allow you to push UIViewControllers on to the stack.
Using the UINavigationController you will get an nice naviagtionbar in at the top of you screen and the back button.
You can find a nice example here:http://developer.apple.com/library/ios/#documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html
I found this way the most useful and convenient. When calling the new view use this:
HomeView* hv = [[HomeView alloc] init];
(here you can add a uninavigation controller)
[self presentModalViewController:hv animated:YES];
Then to dismiss this view and go back use this:
[self dismissModalViewControllerAnimated:YES];
#atbebtg:
There is a way to do that, infact there are several, since there not really is a "right way" to do it.
For me this works well:
[[self navigationController] setNavigationBarHidden:YES animated:NO];
This will hide the Navigation Bar, so the user can't go back to the last screen.
The other thing you could do is to create your own subclass of UIViewController and not support the button event, like this:
- (IBAction)done:(id)sender
{
//inform the user, that going back is not possible, for example with UIAlertView
//[self.delegate infoViewDidFinish:self];
}
However, this solution seems a bit odd, because the user expects a existing button to work.
Still, this would work.
Others have given answers that present modal view controllers or build a navigation stack. In most cases I would use one of these approaches. Yet, the simplest way to fix the code in the question is to just remove the menu view from the super view. Something like this:
- (IBAction)returnToHome:(id)sender{
[self.view removeFromSuperview];
}
I am using XCode to develop a Cocoa touch application for the iOS platform but have had trouble finding out how to get a swipe gesture implemented that would allow the user to swipe their finger left or right to change to a new ViewController (nib/xib file). I have done a swapView IBAction using a button and modal transitioning and I have read about Apple's TouchGestureRecognizer but I don't know how to implement a swipe action that would allow a view change.
I do NOT want to use a scroll view, as I have several dozen view controllers, that I want the user to be able to swipe through.
Here is an example:
First View Controller.xib:
SwipeRight- Go to second View Controller.xib
Second View Controller.xib:
SwipeLeft- Go to first View Controller.xib
SwipeRight- Go to third View Controller.xib
etc, etc
I have not used UISwipe/Touch Gestures before but I have used an IBAction method to switch views using a button with Modal Transitioning (see below):
-(IBAction)swapViews; {
SecondViewController *second2 =[[SecondViewController alloc initWithNibName:#"SecondViewController" bundle:nil];
second2.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:second2 animated:YES];
[second2 release];
}
Is using a swipe to do a similar method formatted differently? If so, how do I sort this out and format it.
Thank You
Edit - Answer as Per Comment on Question
Place this in your viewDidLoad
UISwipeGestureRecognizer *swipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeLeftDetected:)];
swipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeRecognizer];
[swipeRecognizer release];
Then add a selector as by pasting the following code into your main...
- (IBAction)swipeLeftDetected:(UIGestureRecognizer *)sender {
NC2ViewController *second2 =[[NC2ViewController alloc] initWithNibName:#"NC2ViewController" bundle:nil];
second2.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:second2 animated:YES];
[second2 release];
}
Then just make sure you import the otherViewController you are swapping to using
#import "SecondViewController"
at the top of your main file. Hope this helps.
End Edit
This sounds like a perfect time to use UIGestureRecognizer or, more specifically, UISwipeGestureRecognizer.
For more info on how to use them, read up in the Gesture Recognizers section of the Event Handling Guide.
Lets assume you want to swipe left to bring up another view from the right.
In the storyboard, drag and drop a swipe gesture recognizer. It will make an icon below the view controller; drag this icon and drop onto the ViewController you want to navigate to. This will add a segue, select custom segue. Then create a UIStoryboardSegue class. Add the following code:
- (void)perform {
UIViewController* source = (UIViewController *)self.sourceViewController;
UIViewController* destination = (UIViewController *)self.destinationViewController;
CGRect sourceFrame = source.view.frame;
sourceFrame.origin.x = -sourceFrame.size.width;
CGRect destFrame = destination.view.frame;
destFrame.origin.x = destination.view.frame.size.width;
destination.view.frame = destFrame;
destFrame.origin.x = 0;
[source.view.superview addSubview:destination.view];
[UIView animateWithDuration:0.5
animations:^{
source.view.frame = sourceFrame;
destination.view.frame = destFrame;
}
completion:^(BOOL finished) {
UIWindow *window = source.view.window;
[window setRootViewController:destination];
}];
}
I have got my own custom UIViewController, which contains a UIScrollView with an UIImageView as it's subview. I would like to make the image to auto rotate when device orientation changes, but it doesn't seem to be working...
In the header file, I've got;
#interface MyViewController : UIViewController <UIScrollViewDelegate> {
IBOutlet UIScrollView *containerView;
UIImageView *imageView;
}
These components are initialised in the loadView function as below;
containerView = [[UIScrollView alloc] initWithFrame:frame];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://..."]];
UIImage *image = [[UIImage alloc] initWithData:data];
imageView = [[UIImageView alloc] initWithImage:image];
[image release];
[containerView addSubview:imageView];
And I have added the following method, assuming that's all I need to make the view auto-rotate...
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
MyViewController loads fine with the image I've specified to grab from the URL, and the shouldAutorotate... function is being called, with the correct UIInterfaceOrientation, when I flip the device too.
However, didRotateFromInterfaceOrientation method do not get called, and the image doesn't seem to rotate itself...
Could someone please point out what I need to add, or what I have done wrong here?
Thanks in advance!
This may not be the right answer for you, because you don't specify the context that the UIViewController's in, but I just found an important gotcha in the Apple documentation that explains the similar problem I'm having.
Tab bar controllers support a portrait
orientation by default and do not
rotate to a landscape orientation
unless all of the root view
controllers support such an
orientation. When a device orientation
change occurs, the tab bar controller
queries its array of view controllers.
If any one of them does not support
the orientation, the tab bar
controller does not change its
orientation.
I've noticed that there are issues when rotating a UIView that's not the first or only view as a direct child of the main window.
So if your UIView is part of a Navigation Controller or a Tab View Controller, you'll also need to override shouldAutoRotateToInterfaceOrientation on the Navigation Controller or Tab View Controller.
Also: using [UIApplication setStatusBarOrientation] helps to work around things if/when you need to do it manually.
To make this kind of thing work in my application, I had to override
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
[self layoutSubviews];
}
and also layoutSubviews
- (void)layoutSubviews
{
NSLog(#"layoutSubviews called");
...recalc rects etc based on the new self.view.bounds...
}
I'm not sure that this is absolutely required, but it worked for me.
Sometimes, if you add a subview to a view, it's your responsibility to make sure that the methods are passed to the subview; a couple of days ago I wrote a short post about this. For example, if you have a UIViewController and add a UINavigationController as subview, you must add this code to the UIViewController if you want viewWillAppear:animated: to be called when UINavigationController.view appears:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[projectNavigationController viewWillAppear:animated];
}
It might be the case that the willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation method also need to be called by the superview; I am not really sure about this, but give it a try.
This is discussed in Apple Technical Q&A QA1688.
Sometimes if you stack multiple views on top of each other for some reason, the anotherController might not receive rotation event.
[myWindow addSubview:primaryViewController.view];
[myWindow addSubview:anotherController.view];
A lazy way (not a good design) to fix this is only add one subview on window, but initialize multiple controller on the app delegate. Then when you need to switch window, remove the current view and add the view you want
[self.view removeFromSuperview];
AppDelegate *dg = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[[dg window] addSubview:[[dg viewController] view]];
I just came across this having a similar problem. I have a series of view controllers/complex views, that all rotate perfectly and couldn't figure out why the new one I just added on wasn't rotating. After a LOT of trial and error, the reason was that I wasn't calling the init method (it's the standard init method) when allocating the view controller;
e.g. I was doing
m_timerViewController = [TimerViewController alloc];
instead of
m_timerViewController = [[TimerViewController alloc] init];
To expand on jonoogle's post. I had a similar error. My view has a nib and my custom init:
- (id)initWithCategory:(Category *)category inManagedObjectContext:context{
didn't include the call to init the nib.
self = [super initWithNibName:nil bundle:nil];
Adding that line made my view rotate like it is supposed to.
I copied this from this link
And it works for me.... Reason why i have added this here is to make it easy for others to find. It took me many hours to find this fix:
Make a new set of class files of the UIViewController type, go into the .h file of this class and change this line
#implementation MyTabBarController: UIViewController {}
#end
to something like this
#implementation MyTabBarController: UITabBarController{
}
Now go into the nib file and click on the UITabBarController object and go to it's identity tab, and make it Class MyTabBarController.
now in MyTabBarController.m make sure this is in it.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)io {
return YES;
}
You can probably get rid of everything else in there if you want.
just do this if you what to rotate from landscape to portrait!
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}