Lets say I have a UIViewController with two buttons, both going (push) to another UIViewController that has two UIWebViews (showing two different PDF files), how can I make sure that only the one I choose via the button is showed?
You need to pass some information to the UIViewController which has the UIWebViews, saying which button was pressed. Then, based on that information, decide which of the UIWebViews to display.
As you are using storyboards, I suggest you look into prepareForSegue. It will allow you to set a property on the destination view controller with something like the following. You should add this to the UIViewController which contains the buttons.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"buttonOne"]) {
ExampleViewController *destViewController = segue.destinationViewController;
destViewController.buttonClicked = #"One";
} else if ([segue.identifier isEqualToString:#"buttonTwo"]) {
ExampleViewController *destViewController = segue.destinationViewController;
destViewController.buttonClicked = #"Two";
}
}
You can then use the buttonClicked property in the destination view controller to decide which you should display. If you have two separate UIWebViews, you could choose to hide one using webViewOne.hidden = YES; and show the other using webViewTwo.hidden = NO;.
However, it would probably be neater to only have a single UIWebView. You could then use prepareForSeque to pass in the URL of the PDF you would like it to display, rather than just sending the name of the button clicked.
Assuming you webView is in a view controller called SecondViewController and your buttons are in the view controller called FirstViewController
1) Create an object in your SecondViewController.h
#interface SecondViewController : UIViewController
#property (nonatomic, strong) NSString *whichButtonClicked;
#end
2) Import SecondViewController in your FirstViewController
#import "SecondViewController.h"
3) In you button IBAction method in FirstViewController.m . use this code
- (IBAction) firstButtonClicked
{
SecondViewController *secondViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"secondView"];
secondViewController. whichButtonClicked = #"first"
[self.navigationController pushViewController:secondViewController animated:YES];
}
- (IBAction) secondButtonClicked
{
SecondViewController *secondViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"secondView"];
secondViewController. whichButtonClicked = #"second"
[self.navigationController pushViewController:secondViewController animated:YES];
}
PS Don't forget. In you Storyboard. Set Storyboard ID for SecondViewController as secondView
4) In your SecondViewController.m use this code to check which button
if ([self.whichButtonClicked isEqualToString:#"first"])
{
///display first web view here
}
else
{
//display second web view here
}
Hope this helps
Related
I am developing e-commerce application very similar like Flipkart.
Now I can visit my application without login. I mean initially I can skip login. But when I am going to purchase any item user should be be prompted to login.
Now client's requirement is there should be login button at every page of the application so, user should be navigated to login page from every page and after successfully login he should return to perticular page from when he/she went to login page.
Any idea how can I achieve this kind of functionality?
Step 1 : Create Base class
BaseViewController.h
#import <UIKit/UIKit.h>
#interface BaseViewController : UIViewController
-(void)takeMeToLogin;
#end
BaseViewController.m
#import "BaseViewController.h"
#interface BaseViewController () {
UIView *myTabBar;
UIButton *loginButton;
}
#end
#implementation BaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
loginButton = [UIButton buttonWithType:UIButtonTypeCustom];
loginButton.tag = 5566778897;
[loginButton addTarget:self action:#selector(takeMeToLogin) forControlEvents:UIControlEventTouchUpInside];
[loginButton setBackgroundImage:[UIImage imageNamed:#"login.png"] forState:UIControlStateNormal];
loginButton.frame = CGRectMake(x,y, width, height);
[self.view addSubview:backButton];
}
-(void) takeMeToLogin {
// code here to go to Login screen
}
Step 2: Use BaseViewController as your base class.
Now whenever you create any class, you will have as below.
#interface YourViewController : BaseViewController
By default you will have #interface YourViewController : UIViewController
Let me know if you need anything else
Edit 1
Regarding your comment, Fahim's solution is also working but it has one limitation that I have to create login button on navigation bar. I can't put login button anywhere in the screen., I will say, you can add it anywhere you want. Below is how.
In YourViewController.m have below.
UIButton *buttonThatYouWantToMove = (UIButton *)[self.view viewWithTag:5566778897];
[self.view addSubview:buttonThatYouWantToMove]; // if this don't work use insertSubview:aboveSubview:
buttonThatYouWantToMove.frame = CGRectMake(x,y,width,height); // this is very important
Done!!!
Let me know if you need further explanation.
I'd create MYBaseViewController and inherit from it in all other controller. I'd create a function which adds login button to navbar right item and a function which handles the login. This way, you'll keep everything in one place.
You can do the same in UIViewController category and call the methods in proper controllers, but personally I think that if that's solution for every single UIViewController it's less pretty.
If someone has better idea, I'd gladly hear about it.
Fahim's solution is also working but it has one limitation that I have to create login button on navigation bar. I can't put login button anywhere in the screen.
I approached differently.
I have created loginViewController.
Create protocol in loginViewController file.
Put button in every viewcontroller to open loginviewcontroller file
Modally open loginview controller
Most important thing is in my Stroryboard file I took loginviewcontroller file embedded in NavigationController and my NavigationController is modally attached with every ViewController.
In LoginViewController.h
#class LoginViewController;
#protocol LoginViewControllerDelegate <NSObject>
- (void)LoginViewControllerViewDidCancel:(LoginViewController *)controller;
- (void)LoginViewControllerViewDidDone:(LoginViewController *)controller;
#end
#interface LoginViewController : UIViewController
#property (nonatomic, weak)id <LoginViewControllerDelegate> delegate;
#end
In LoginViewController.m
- (IBAction)didCancel:(UIBarButtonItem *)sender {
[self.delegate LoginViewControllerViewDidCancel:self];
}
- (IBAction)didDone:(id)sender {
[self.delegate LoginViewControllerViewDidDone:self];
}
Now same code for FirstViewController, SecondViewController etc.
FirstViewController.m
#import "LoginViewController.h"
#interface FirstViewController ()<LoginViewControllerDelegate>
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"LoginView"]) {
UINavigationController *navigationController = segue.destinationViewController;
LoginViewController *loginViewController = [navigationController viewControllers][0];
loginViewController.delegate = self;
}
if ([segue.identifier isEqualToString:#"Thirdpage"]) {
}
}
#pragma loginViewController delegate
-(void) LoginViewControllerViewDidCancel:(LoginViewController *)controller{
//Your Logic
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void) LoginViewControllerViewDidDone:(LoginViewController *)controller{
//Your Logic
[self dismissViewControllerAnimated:YES completion:nil];
}
It works for me like a charm..
I have this storyboard:
When I press the "Insegnante" button in the first view controller (wich is called newCourseViewController) it show me a table view with a list of teacher. When I press on a teacher (and the method tableView:canEditRowAtIndexPath: is called) I want that the UITableViewController "pass" the object pressed to the first view controller.
This is my code for the first view controller newCourseViewController.h
#import <UIKit/UIKit.h>
#import "Teacher.h"
#interface newCourseViewController : UIViewController
#property (nonatomic , strong) Teacher *teacher;
#end
And this is my code for the first view controller newCourseViewController.m (only important code)
#import "newCourseViewController.h"
#import "Courses.h"
#import "Teacher.h"
#import "addTeacherToCourseViewController.h"
#interface newCourseViewController ()
#property (weak, nonatomic) IBOutlet UITextField *textField;
#end
#implementation newCourseViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)setTeacher:(Teacher *)teacher
{
self.teacher = teacher;
NSLog(#"Maestro settato!");
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"addTeacherToCourse"]) {
[segue.destinationViewController setPreviousViewController:self];
}
}
Now the code for the second view controller addTeacherToCourseViewController-h
#interface addTeacherToCourseViewController : UITableViewController
#property (nonatomic , weak) id previousViewController;
#end
and the addTeacherToCourseViewController.m (only the important method)
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Teacher *teacher = [self.teachers getTeacherInPosition:indexPath.row];
[self.previousViewController setTeacher:teacher];
[self.navigationController popViewControllerAnimated:YES];
}
In the first view controller in the prepareForSegue method I set myself to the previousViewController in the second view. Then I "pass" the teacher selected and than I dismiss the second view controller.
When the application execute the [self.navigationController popViewControllerAnimated:YES]; Xcode crash and the simulator crash.
I can't figure out what is the problem. Can you help me?
To send values to parent controller you have to use protocols. I will provide proper steps you should take in order to have your desired functionality working.
1.
Create a protocol for your AddTeacherToCourseController.
In your AddTeacherToCourseController.h add the following right below the imports:
#protocol AddTeacherToCourseControllerProtocol <NSObject>
- (void)yourDelegateMethod:(Teacher *)insegnante;
#end
And below interface tag add:
#property (strong, nonatomic) id <AddTeacherToCourseControllerProtocol> delegate;
2.
In AddTeacherToCourseController.m:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// I would use the existing array you are using to display the teachers in order to select the correct one you want to send back like this:
// Teacher *teacher = [self.teachers getTeacherInPosition:indexPath.row];
[self.delegate yourDelegateMethod:[yourTeacherArray objectAtIndex:indexPath.row]];
}
[this method will call your delegate method through the protocol and will pass your selected professor to the parent controller]
3.
In your parent controller, your newCourseViewController.h right after interface line add:
<AddTeacherToCourseControllerProtocol>
4.
If you do not have an Insegnante button action, create one in interface builder [dragging and naming]. Then add the following to this action:
// assuming your storyboard is named MainStoryboard. here you create your segue programmatically:
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
addTeacherToCourseViewController *addTeacherController = (addTeacherToCourseViewController *)[storyBoard instantiateViewControllerWithIdentifier:#"addTeacherToCourseViewController"];
addTeacherController.delegate = self;
[self.navigationController pushViewController:addTeacherController animated:YES];
5.
In Interface Builder:
Remove your segue from Insegnante button.
Edit the Storyboard Id of 'addTeacherToCourseViewController' to 'addTeacherToCourseViewController'
6.
In newCourseViewController.h write your delegate method:
- (void)yourDelegateMethod:(Teacher *)insegnante{
// Do whatever you want with your Insegnante
// and be sure to pop the second controller from the view stack:
[self.navigationController popViewControllerAnimated:YES];
}
Let me know if you have questions and if my answer helped anyone.
In order to give you an exact answer please tell me what object you are using to display your list of professors in your second controller, the tableViewController. I am guessing that is an array of Teacher instances. Is that correct? [class Teacher]
I am using a single view application template in xcode. I created the first view controller, and then added another with a new .m and .h and xib.
I can click a button IBAction, and get to my second view, however the code I am using for the "back" button wont take me back to my first view, everything crashes. I have included my code which seems to follow the tutorial I was using. Additionally I just control clicked my button and dragged the line to my IBAction in the .h to hook in the secondViewController buttons, which is what I did on the first view controller and it seems to work there.
If anyone can help that would be great!
//from my first view controller .h which works
-(IBAction) buttonPressedPayTable: (id) sender;
//from my first view controller.m which also works and gets me to the second view
-(IBAction) buttonPressedPayTable: (id) sender
{
SecondViewController *payTableView = [[SecondViewController alloc]
initWithNibName:#"SecondViewController" bundle:nil];
[self.view addSubview:payTableView.view];
}
//from my second view controller .h that will not get me back to the first view without crashing
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController
{
}
-(IBAction) back: (id)sender;
#end
//from my second view controller .m which doesn't seem to work
#import "SecondViewController.h"
#implementation SecondViewController
-(IBAction) back: (id)sender
{
[self.view removeFromSuperview];
}
#end
use UINavigation controller
[self.navigationController popViewControllerAnimated:YES];
You might be better off using modal views. So instead of addSubView use:
[payTableView setModalPresentationStyle:UIModalPresentationFullScreen];
[payTableView setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentModalViewController:payTableView animated:YES];
Then on the seconViewController back method:
[self dismissModalViewControllerAnimated:YES];
You can change the ModalTransitionStyle to the few that apple gives you :D
Hope this helps
You can use a navigation method for switching from one view controller to the other.
See the apple docs about view controllers
Don't add your second ViewController as a subview of the view.
Use a UINavigationController to add the new UIViewController to the view stack using [self.navigationController pushViewController:payTableView animated:YES];
Then you can either use the nav controllers back button, or use your own button with the code [self.navigationController popViewControllerAnimated:YES];
Alternately, you can use Storyboards and use a simple segue.
[payTableView.view removeFromSuperview];
i think you are removing the whole view .that is why app is crashing
You need to use delegate here:
in .h of first view declare a member variable :
#interface FirstViewControllerClassName: UIViewController
{
SecondViewController *payTableView;
}
#property (nonatomic, retain) SecondViewController *payTableView;
in .m :
#synthesize payTableView;
-(IBAction) buttonPressedPayTable: (id) sender
{
if (!payTableView)
payTableView = [[SecondViewController alloc]
initWithNibName:#"SecondViewController" bundle:nil];
payTableView.delegate = self;
payTableView.isFinishedSelector = #selector(removeView);
[self.view addSubview:payTableView.view];
}
- (void)removeView
{
[payTableView.view removeFromSuperview];
[payTableView release];
payTableView = nil;
}
//Just declare a member variable delegate in secondviewcontroller like:
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController
{
id delegate;
SEL isFinishedSelector;
}
#property (nonatomic, assign) id delegate;
#property (nonatomic, assign) SEL isFinishedSelector;
-(IBAction) back: (id)sender;
#end
#import "SecondViewController.h"
#implementation SecondViewController
#synthesize delegate;
#synthesize isFinishedSelector;
-(IBAction) back: (id)sender
{
if ([self.delegate respondsToSelector:isFinishedSelector])
[self.delegate performSelector:isFinishedSelector];
}
#end
I trying to pass the data from TableViewCell to the another ViewController.But No data Displaying in the another ViewController.here is my Code
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
PeripheralManager *objSelected=[device objectAtIndex:indexPath.row];
[self prepareForSegue:#"TableDetails" sender:objSelectedDevice];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"TableDetails"])
{
DetailViewController *detail=segue.destinationViewController;
detail.dataArray=device;
}
}
Error Message
nested push animation can result in corrupted navigation bar
2012-10-24 12:01:39.805 [3182:707] nested push animation can result in corrupted navigation bar
2012-10-24 12:01:40.164 [3182:707] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
2012-10-24 12:01:40.167 [3182:707] Finishing up a get navigation transition in an unexpected state. Navigation Bar subview tree might corrupted.
You do not need this:
[self.navigationController pushViewController:mdc animated:YES];
That is what Segue will do automatically
Also, you are having 3 lines that will load view controller - see below for comments:
NSInteger row=[indexPath row];
NSString *value=[device objectAtIndex:row];
MeBleDetailViewController *mdc=[self.storyboard instantiateViewControllerWithIdentifier:#"MeBleDetailViewController"];
mdc.deviceName=value;
[self presentModalViewController:mdc animated:YES]; // Load ViewController
[UIView commitAnimations];
[self performSegueWithIdentifier:#"TableDetails" sender:[device objectAtIndex:indexPath.row]]; // Load ViewController
[self.navigationController pushViewController:mdc animated:YES]; // Load ViewController
That is why you are getting that error: nested push animation can result in corrupted navigation bar
Also, If you have configured the segue from table cell to another view controller then you don't need anything in didSelectRowAtIndexPath method.
Edit:
Whatever data you want the pushed view controller to have - put it in prepareforSegue method instead of didSelectRowAtIndexPath
If you create a segue from table cell to view controller then you don't need to execute the following as this method is to execute the segue programmatically.
[self performSegueWithIdentifier:#"TableDetails" sender:[device objectAtIndex:indexPath.row]];
Remove your extra code Only do this-
In DetailViewController.h
#property(nonatomic, retain)NSMutableArray *dataArray;
In DetailViewController.m
#synthesize dataArray = _dataArray;
Now In TableViewController.m Just write this -
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"TableDetails"])
{
DetailViewController *detailViewObject = segue.destinationViewController;
detailViewObject.dataArray = anyArray;
}
}
Here I'm passing NSMutableArray.
OK. Let's say you have two viewcontrollers FirstViewController and SecondViewController.
In FirstViewController you have a tableview and of course tableviewcell. In SecondViewControlleryou need to display data.
So in SecondViewController.h you need to set a propery of some variable, in this case it is of id type #property (strong, nonatomic) id secDetailItem;. Synthesize it in SecondViewController.m and add a setter method like this
-(void)setDetdetailItem:(id)newSecdetailItem{
if (secDetailItem != newSecdetailItem) {
secDetailItem = newSecdetailItem;
// Update the view.
[self configureView];//This method is needed to update view if there are some changes in that view.
}
}
So then in FirstViewController.h import SecondViewController.h and add property #property (strong, nonatomic) SecondViewController *secondViewController; then
synthesize. In FirstViewController.m file in this delegate method do following:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
self.secondViewController.secDetailItem=//set your data should be passed.
//Also if you need to push viewcontroller add pushviewcontroller:SecondViewController, or use IB to connect tableview cell and SecondViewController together with push method.
}
In this case you will not need to use perform segue. The Setter method will work as soon as you set to the secDetailItem something.
Also if you need to update your view in SecondViewController add this method to it.
- (void)configureView
{
if (self.secDetailItem) {
self.textLabel.text=self.secDetailItem;//Data passed from FirstViewController
}
}
This is all you need to do. Sorry if it is complicated. Ask any question.
It might have something to do with this line:
[UIView commitAnimations];
You can delete it if you don't need it.
I have the following simple view controller class set up
#protocol ThermoFluidsSelectorViewControllerDelegate;
#interface ThermoFluidsSelectorViewController : UIViewController <UITextFieldDelegate>
#property (weak, nonatomic) id <ThermoFluidsSelectorViewControllerDelegate> delegate;
// user hits done button
- (IBAction)done:(id)sender;
#end
#protocol ThermoFluidsSelectorViewControllerDelegate <NSObject>
-(void) didFinishSelection:(ThermoFluidsSelectorViewController *)controller fluidID: (NSString *)fluidID;
#end
the 'didFinishSeletion: fluidID:' method is defined in the master view controller and should dismiss the selector view controller when called. When the done button is pressed the following method is called:
- (IBAction)done:(id)sender
{
[[self delegate] didFinishSelection:self fluidID:nil];
}
the 'done:' method gets called (checked with an alert) but 'didFinishSelection...' is not getting called so the view will not revert back to the main screen. Any ideas?
It sounds like you have not assigned your delegate in your master view controller.
You should have something like this in your master view controller which sets up the delegate:
ThermoFluidsSelectorViewController *view = [[ThermoFluidsSelectorViewController alloc] init];
view.delegate = self;
here you can see I create the view, then set the delegate of the view back to myself.
If you are not creating the Thermo... view controller programatically, but have used a storyboard, then you can set the delegate in the prepareForSegue: method of your master view controller:
// Do some customisation of our new view when a table item has been selected
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure we're referring to the correct segue
if ([[segue identifier] isEqualToString:#"MySegueID"]) {
// Get reference to the destination view controller
ThermoFluidsSelectorViewController *cont = [segue destinationViewController];
// set the delegate
cont.delegate = self;
Hope this helps.