Simple question that I'm currently having trouble as to how to get started.
I have a ViewController with multiple images/icons, and I would like to have new view controllers for each image/icon that is selected. I have a segue (show) from my initial view controller to my new one...but now how do I code it so when I click on a specific image it'll segue to the corresponding VC I want?
Thanks in advance!
If all of your Images/Links goes to one kind of ViewController, you can link it with segue in your interface builder and so you use just -performSegueWithIdentifier and seed it with your data or model corresponding to your ViewController.
For getting touch events and handling it, you can use delegate method which each subviews call their delegate when touch event catches. Assume below code for better imagination:
//Added in initiation of SubView
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGestureUpdated)];
//This function will be fired when gesture recognized
- (void)tapGestureUpdated {
if ([self.tapDelegate respondsToSelector:#selector(onItemTapped:)]) {
[self.tapDelegate onItemTapped:self.item];
}
}
But if you're showing to different types of ViewControllers, for each kind you should add segue and ViewControllers.
Superview will call next ViewController like this:
[self performSegueWithIdentifier:#"showNextViewControllerSegue" sender:self];
So there is such event chain for handling it:
UITapGestureRecognizer -> Your SubView -> Call it's delegate
(SuperView) -> Call next segue according to subView's type.
Related
I have a UITableViewController and custom UITableViewCell, when I tap some button on the cell, controller should push to another controller and present another view.
I can think of two solutions for the code architect for this.
First one is that I create a protocol method to react to the tapping event in my cell and set controller as delegate, so once there is tapping on my cell, controller would react to push to another view.
But I could also do something in my cell class like this, instead of creating delegate, I keep asking the nextResponder if it's the right controller with for loop and once I get it I use it to push to the next one:
#implementation MyCustomTableViewCell
//...
//...
//...
-(void)tappedOnSomeView
{
id obj = nil;
for (obj = self; obj; obj = [obj nextResponder]) {
if ([obj isKindOfClass:[MyTableViewController class]])
{
UIViewController *uiVC = (UIViewController *)obj;
MyNextViewController *nextVC = [[MyNextViewController alloc] init];
[uiVC.navigationController pushViewController:nextVC animated:YES];
return;
}
}
}
//...
//...
//...
#end
So is this not very MVC? Or is it ugly code? Should I just create delegate to handle all the gesture events on my cell in its tableview controller? Or is there another better way to do this?
Thanks.
You should go with the first approach. The benefit of this is that you can also pass back some data from the cell back to the view controller.
The other option is you could do something like this
In cellForRowAtIndexPath:, add target to the view to handle the tap event.
This way you can directly catch the response for the tap inside the view controller. However I would emphasise on the 1st approach of delegate.
Having user actions on custom table view cells is a common practice and per MVC, "view" should not take decisions like what to show, how to show, when to show. View should only know what things it needs to draw on what conditions. In your case, per MVC, first approach makes sense. Add your ViewController as delegate to cell in cellForRowAtIndexPath: and handle the pushing of new view controller in your controller!
I have created a custom uiscrollview and want an action on the uiscrollview to trigger a segue. I realise that all controls that trigger a segue have a "triggered segues" section in their connections inspector.
- (IBAction)startSegue....
In the .h file fits under the "Received Actions" section, is there a way to programmatically create a connection under the "triggered segues" connection to enable a custom class control to act as a connection for a segue?
Thanks
D
No, you can't do what you're suggesting. You need to make segue from the view controller, and call performSegueWithIdentifier:sender: inside one of the scroll view's delegate methods.
You can trigger a segue programatically if that is what you're asking. Create a method which is called after from action (a button or motion on the UIScrollView) and call a method similar to this: (Taken from Creating a segue programmatically)
-(void) someMethod{
MyNewViewController *myNewVC = [[MyNewViewController alloc] init];
// do any setup you need for myNewVC
[self presentModalViewController:myNewVC animated:YES];
}
I have a problem with a GLKViewController (subclass) used in a Storyboard: it refuses to handle tap events.
I added a Tap Gesture Recognizer to the GLKViewController (subclass) in the Storyboard and linked it to a 'tapDetected' method:
- (IBAction) tapDetected:(id)sender {
NSLog(#"tap\n");
[self.navigationController setNavigationBarHidden: ![self.navigationController isNavigationBarHidden]];
} // tapDetected
This exactly how I manage - successfully - the taps in other view controllers in the same Storyboard. Only the GLKViewController does not receive the event: neither the log nor the navigation bar are affected by the tap.
I searched for any specific need of GLKViewControllers but could not find any. Do you have any idea and/or suggestion?
It looks like if I add a touchesBegan method everything works ok. So the question is: what's the point of the gesture recognizer in the Storyboard?
I am a novice ios programmer and this is my first project.I am going to develop this project specifically for ipad. In this project i need to draw several circles & display data on it by parsing xml element.I have done the the circle drawing part by subclassing UIView class and overriding drawRect method. I load the UIView subclass in a UIViewcontroller via loadview method .Now what i need to do is
Touch a circle and switch to another UIViewcontroller.
I am not sure how to switch to another UIViewcontroller because all of drawing and touch detecting code is in my UIView subclass.
A help will be appreciated
You need to use a delegate method to tell the parent view controller that there was a touch, so it can present another view controller.
At the top of your UIView subclass header, add this:
#protocol MyCustomViewDelegate <NSObject>
- (void)customViewCircleTapped;
#end
Then, in your declaration of the view (the existing declaration you have of your custom view subclass):
#interface MyCustomView : UIView
...
#property (weak) id<MyCustomViewDelegate> delegate;
After that, in your view controller, you need to set view.delegate = self, so the view can reference the view controller.
Then, in your view controller header, change your declaration to look like this:
#interface MyViewController : UIViewController <MyCustomViewDelegate>
then implement customViewCircleTapped in the view controller implementation:
- (void)customViewCircleTapped {
... // Open a view controller or something
}
Once you have done that, in the touch detection code in your view, you can add:
[self.delegate customViewCircleTapped];
What this does is gives your custom view the ability to tell its parent view controller that something has happened, by calling this method (you can change it and add arguments if you need to pass data), and then the view controller can open another view controller or perform some action based on this.
View detects touches → Process touches → call customViewCircleTapped delegate method on view controller → view controller opens another view controller
Provide the subclassed view with a delegate notifying the view is touched and in the delegate call in the mainVC do the push job
The way I like to do this is to send a message up the responder chain. This completely decouples the view from it's enclosing views and view controllers. So, when your circle view is tapped, it emits a "circle view was tapped message" to the responder chain--the first object on the responder chain that responds to that message (which don't know/care which one) will have it invoked. It's simple to implement.
You attach a UITapGestureRecognizer to your view with -tapped: as the action.
On your view, your tap action might look like this.
-(IBAction)tapped:(UIGestureRecognizer*)g
{
[ self sendAction:#selector( circleViewTapped: ) withObject:self ] ;
}
The -sendAction: method on UIResponder is added via a category, like this:
#implementation UIResponder (ActionSending)
-(void)sendAction:(SEL)action withObect:(id)object
{
UIResponder * target = self ;
while( self && ![ target respondsToSelector:action ] )
{
target = [ target nextResponder ] ;
}
[ target performSelector:action withObject:object ] ;
}
#end
Your view controller or any parent view or parent view controller that responds to circleViewTapped: will have that method invoked when your circle view is tapped.
A note about organizing your views:
I would make a circle view UIView subclass. Instantiate your subclass for each circle to be displayed. To each of those attach a UITapGestureRecognizer. The target of your gesture recognizer is the -tapped: method, above.
The current answers are overcomplicating the solution. You don't need gesture recognisers or delegate protocols.
Make your circle drawing view a subclass of UIControl instead of UIView. Then attach your view controller as a target to the UIControlEventTouchUpInside event:
[circleView addTarget:self action:#selector(circleTapped:) forControlEvents:UIControlEventTouchUpInside];
This will call the circleTapped: method on your view controller, with the tapped view as the sender.
I connected first view controller with the second one using StoryBoard Push Segue and Interface Builder.
The button is named GO on top/right.
I have three textfield that must be filled before going to second controller.
I display an alert when one of them is empty.
The problem is that my code after displaying correct alertView goes to SecondController instead of remaining on mainController.
if ([segue.identifier isEqualToString:#"DataDisplay"])
{
if (![self verifySelection]) {
return;
} else {
RowViewController *rowViewController = segue.destinationViewController;
// rowViewController.delegate = self;
}
}
1) You have a segue wired directly from your Go button to your Sensor Data view controller. You don't want this, because anytime someone touches Go, the segue is going to happen ... no stopping it. So, first step is to remove the segue you have going from Go to your second view controller.
2) Instead, wire the segue from the File's Owner icon below the view controller to the second view controller. Give it a name like DataDisplay.
3) In the IBAction for your Go button
if ([self verifySelection) {
[self performSegueWithIdentifier:#"DataDisplay" sender:self]
}
An easy fix would be to create the segue manually, rather than letting the interface builder manage it. So you would ctrl-drag from your main view controller to your second one, selecting push as the type of segue and assigning it an identifier through the identifier inspector, then you connect an IBAction to your Go button and in the method you perform the checks on the text fields before programmatically firing the segue with:
[self performSegueWithIdentifier:#"whateverIdentifierYouGaveYourSegue" sender:self];
Heads up: to create a manual segue from a viewcontroller to another one, you need to either zoom out in your storyboard or ctrl-drag from the yellow circle underneath the view!
Edit: Your IBAction connected to the button method should be something like the following:
- (IBAction)download:(id)sender {
if(text boxes are ok)
[self performSegueWithIdentifier:#"segueIdentifier" sender:self];
else
[self showWarning];
}
Make sure that you assigned the ID segueIdentifier to the segue you created in your storyboard.
Your problem is you are defining the "performSegueWithIdentifier" after displaying the alert.
I think the code you are doing is like this :
//AlertView Allocation
[alert show];
Perform Segue
If this is how you are doing, then you are doing it wrong.
You have to use the structure of If-Else Statements and put up the Perform Segue in the condition where all the textfields are filled.