I have a tabbed application project in Xcode and I'm trying to create a back button for one of the viewcontrollers in order to go back to the previous view. You can get to this viewcontroller from 2 different viewcontrollers so I'm wondering if it's possible to pop the viewcontroller in order to go back to the correct view controller(the one you were previously on). This code doesn't seem to work with tabBarController.
-(IBAction) goBack: (id)sender{
[self.tabBarController popViewControllerAnimated: YES]; }
NSArray *array = [self. tabBarController viewControllers];
[self.tabBarController popToViewController:[array objectAtIndex:2] animated:YES];
Think this one works . Havent tested it . Please let me know
check this.
id controller = [self.tabBarController selectedViewController];
if([controller isKindOfClass:[UINavigationController class]])
{
[controller popViewControllerAnimated:YES];
}
NSArray *array = [self. tabBarController viewControllers];
for (UIViewController *controller in array) {
if ([controller isKindOfClass:[ReachViewControllerName class]]) {
[self.navigationController popToViewController:controller
animated:YES];
break;
}
}
May be it ll helps.
Related
I am new to swift i am using storyboard and have used navigationcontrollers to connect from one viewcontroller to another. I want to send the name of the image clicked to the next viewcontroller which is connected modally in storyboard from the imageView. I searched lot about transferring data from oneviewcontroller to another viewcontroller connected with navigationcontroller modally but no solution was available. Please let me know if any of the code is required as i dont know how to go ahead with it. I know this might be silliest question but posting this after searching a lot on net.
EDIT according to #uraimo reply.
Do i need to provide name to every segue i created on storyboard?.
I have 2 fixed images on viewcontrollerA and i have placed a uibutton with transparent background and no text on each of them and then ctrl drag to navigation controller of viewcontrollerB for presenting modally and unwinding the backbutton i.e. UIBarButtonItem to viewcontrollerA by ctrl drag the back button of viewcontrollerB to exit of the viewcontrollerB and unwinding it.
This is how i have created navigation from any of the image click out of 3 images of viewcontrollerA to viewcontrollerB and back to viewcontrollerA on back button click of viewcontrollerB.
Please let me know if i am doing anything wrong and will your prepareForSegue code be useful in accomplishing my task.
Basically, both using IB or when you do it programmatically, you have to configure your new viewcontroller with all the data it needs before the segue is performed (or the controller is presented via code).
In your case, just set the image name (your custom view controller class YourViewController should have a specific String property to hold this value) overriding prepareForSegue in the current view controller class:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "yourModalSegueIdentifier" {
let imgName= (sender as! UIImageView)
let destination = segue.destinationViewController as UINavigationController
let yourController = destination.topViewController as YourViewController
yourController.imageName= <name here>
}
}
This solves the passing data question.
But in your case, you need the name of the clicked image, and that can be only obtained adding a click event through a UIGestureRecognizer to the UIImageView.
So, you'll need a uigesturerecognized that on click will perform the segue you've created. And also, you will not be able to get the name of the image asset (the one you use the creating an UIImage using imageNamed:) because it's not available anymore, and yo'll have to use the accessibilityIdentifier.
This makes everything way more complicated, it seems it could be done for the most part graphically here and here(but it's not really clear how to do it), but it's usually done via code.
Simplifying it a bit using a global variable:
var currentImage = ""
func viewDidLoad(){
...
...
let aTap = UITapGestureRecognizer(target: self, action: Selector("imageTapped:"))
aTap.numberOfTapsRequired = 1
//For every image, configure it with recognizer and accessibilityId:
firstImage.userInteractionEnabled = true
firstImage.addGestureRecognizer(aTap)
firstImage.accessibilityIdentifier = "firsImage"
...
}
func imageTapped(recognizer:UITapGestureRecognizer) {
let imageView = recognizer.view as! UIImageView
currentImage = imageView.accessibilityIdentifier
self.performSegueWithIdentifier("yourModalSegueIdentifier", sender: self)
}
And change this:
yourController.imageName= <name here>
to this:
yourController.imageName= currentImage
Update:
Do i need to provide name to every segue i created on storyboard?
Yes, it's the only way to identify them, every UIStoryboardSegue has an identifier. But remember, segues are not the only way to go from a controller to another, if you do it completely programmatically (no segues) you usually call "presentViewController". Segues are a storyboard concept.
Again, regarding the segue name/identifier, you didn't need it until now because you never referenced that segue from your code, you need it for both prepareForSegue and performSegueWithIdentifier. Just select the segue and give it a name on the right inspector pane.
The structure you describe seems ok, the only thing it's that i'm not so sure that the UIButtons are really needed, try with a modal segue from the imageview or directly from the viewcontroller to the destination view controller.
Update 2:
If you are starting and need a free course that will teach you the basics and also make you build a few interesting ios apps i recommend hackingwithswift.
check out how I did this
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch(segue.identifier ?? "") {
case "AddItem":
let destination = segue.destination as? UINavigationController
guard let itemViewController = destination?.topViewController as? ItemViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
itemViewController.collection = collection
case "EditCollection":
guard let collectionViewController = segue.destination as? EditCollectionViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
collectionViewController.collection = collection
default:
fatalError("Unexpected Segue Identifier; \(segue.identifier)")
}
}
I have this code in a separate method called CommonMethods:
- (void) displayAlert: (NSString *)alertTitle andData: (NSString *) alertMessage andTag: (int) tag andVC: (UIViewController *) vc {
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:alertTitle
message:alertMessage
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Cancel", #"Cancel action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
NSLog(#"Cancel action");
}];
UIAlertAction *okAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"OK", #"OK action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
NSLog(#"OK action");
}];
[alertController addAction:cancelAction];
[alertController addAction:okAction];
[vc presentViewController:alertController animated:YES completion:nil];
}
When I call this method from any view controller, using this code:
CommonMethods *cm = [CommonMethods new];
[cm displayAlert:NSLocalizedString(#"Client not selected.",nil)
andData:NSLocalizedString(#"You must select a client to create the appointment",nil)
andTag:2
andVC:self];
I always get the above error, even tho' vc is not nil.
What is causing this, and how do I fix it?
Apparently, the iOS 8.x comment led to a solution, though why Xcode didn't flag the use of a SDK 8 class for iOS 7 deployment is anyone's guess.
Still having issues with pushing a new UIView on rotation. I am following an example that I found. It's SO freaking close. It wigs out with an error at this point with the error of;
'NSInvalidArgumentException', reason: 'Application tried to present a nil modal view controller on target .'
controller nvs = [self.storyboard instantiateViewControllerWithIdentifier:#"LandscapeView"];
[self.navigationController pushViewController:nvs animated:YES]
Clearly, I'm missing something here. What I'm trying to achieve is a different view pushed onto the stack when the user rotates their device. Like calendar.
Entire statement
LandscapeViewController * nvs;
- (void)updateLandscapeView
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && self.presentedViewController == nil)
{
if (!self.landscapeViewController)
//self.landscapeViewController = [[LandscapeViewController alloc] initWithNibName:#"LandscapeView" bundle:nil];
//Set the location of the where you want the view to go i.e. the next view controller
nvs = [self.storyboard instantiateViewControllerWithIdentifier:#"LandscapeView"];
//Push the view onto the device
[self.navigationController pushViewController:nvs animated:YES];
if ([self respondsToSelector:#selector(presentViewController:animated:completion:)])
[self presentViewController:self.landscapeViewController animated:YES completion:NULL];
else
[self presentModalViewController:self.landscapeViewController animated:YES];
}
else if (deviceOrientation == UIDeviceOrientationPortrait && self.presentedViewController != nil)
{
if ([self respondsToSelector:#selector(dismissViewControllerAnimated:completion:)])
[self dismissViewControllerAnimated:YES completion:NULL];
else
[self dismissModalViewControllerAnimated:YES];
}
}
Thanks in advance!
Jeremy
Got it. Rather then pushViewController pushing nvs which was set outside the statement. I referenced it directly in using self.landscapeViewController.
Below is the code. Hope this helps someone in the future.
self.landscapeViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"LandscapeView"];
[self.navigationController pushViewController:self.landscapeViewController animated:YES];
I have an item detail view which I would like to use for two purposes:
1) to create a new item
2) to edit an existing item
When editing, the view will be pushed onto the navigation stack, getting the nav bar from it's parent.
On item creation, I want to present the view modally, but still have a navigation bar at the top, with "Done" and "Cancel" buttons.
What I don't want is to ever see the view with two nav bars, or none.
How would I implement this?
In order to accomplish this I:
Removed the nav bar from my view.
When launching modally, first created a nav controller, and then displayed the nav controller modally with my view as the root view (even though I didn't plan on pushing anything else onto the stack). So changed this:
StoreDetailView *storeDetailView = [[StoreDetailView alloc] initWithNibName:#"StoreDetailView" bundle:nil];
// ... configure the view, including setting delegate...
[self presentViewController:storeDetailView animated:YES completion: nil];
to this:
StoreDetailView *storeDetailView = [[StoreDetailView alloc] initWithNibName:#"StoreDetailView" bundle:nil];
// ... configure the view, including setting delegate...
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:storeDetailView];
[self presentViewController:navController animated:YES completion: nil];
And then in the StoreDetailView, determined what the nav bar should look like based on whether the delegate was set:
if (self.delegate == nil) {
self.navigationItem.rightBarButtonItem = [self editButtonItem];
} else {
[self setEditing:TRUE];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(done:)];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:#selector(cancel:)];
}
I understand that I should set self.title in -initWithNibName:bundle:.
What about self.navigationItem.titleView?
Since self.navigationItem.titleView seems only to be used when self.view is loaded, I'm thinking I should, to save memory, set self.navigationItem.titleView in -viewDidLoad and nil it in -viewDidUnload, e.g.:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:
[UIImage imageNamed:#"logo.png"]];
}
- (void)viewDidUnload {
self.navigationItem.titleView = nil;
[super viewDidUnload];
}
What about self.navigationItem.backBarButtonItem?
It seems to work OK to set self.navigationItem.titleView in -viewDidLoad and nil it in -viewDidUnload.
You should set self.navigationItem.backBarButtonItem in -initWithNibName:bundle because if you push two view controllers without animation -viewDidLoad will not get called for the first view controller that's pushed. So, if that view controller sets self.navigationItem.backBarButtonItem in -viewDidLoad, it will actually not get set, and the back button on the second view controller will just default to the title of the first view controller as usual.