How to show view controller from custom UIButton class? - objective-c

I have following view hierarchy: navigation controller, inside it I pushed another view controller which contains UITableView with custom UIButtons in the cells. I have another view controller (MyCustomViewController2), which I want to show above all this stuff with animation.
But I am confused with this hierarchy and I don't know how to overwrite - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event method in my custom UIButton class. The code that I've come so far is:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
MyCustomViewController2 *myVC = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"MyVC"];
[self.window addSubview: myVC.view];
}
But it's so bad! And I don't have any animations and I should delete it to remove. What can I try next?

It's highly recommended not to subclass a UIButton. So I wouldn't.
But since UIButton is a subclass of UIControl, you can use this method directly:
- (void)addTarget:(id)target
action:(SEL)action
forControlEvents:(UIControlEvents)controlEvents
Example in code could look like this:
[self.myButton addTarget:self action:#selector(showOtherVC) forControlEvents: UIControlEventTouchUpInside];
This will look at the target (self) for a particular method (showOtherVC) when the user lifts their finger from the button.
You could then have the current View Controller present the new one modally using this UIViewController method as example:
-(void)showOtherVC
{
MyCustomViewController2 *myVC = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"MyVC"];
[self presentViewController:myVC animated:true completion:nil];
}
Read more on UIControl here:
UIControl Documentation
And check out other UIViewController modal transition styles here:
UIViewController Documentation

Related

Resend touch event to another view in iOS

I've got an UIImageView on top of (and not an element of) a UIScrollView. Well, I've subclassed the UIImageView to check for touch events and have overridden the touchesBegan:withEvent: method. Inside that method I basically send the subclassed UIImageView behind the UIScrollView element. (I use [self.super bringSubviewToFront: scrollView] to achieve this).
What I want is to be able to send the current touch event of the UIImageView to the UIScrollView element, so that the user doesn't have to lift his finger and touch again to scroll. I already tried to call touchesBegan:withEvent: on the UIScrollView element, but it did nothing.
Does anyone have an idea of what should be done so I could simulate the touch of the UIImageView to the UIScrollView element?
Here is the overridden method in the subclassed UIImageView:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan: touches withEvent: event];
ViewController* controller = [Singleton getSingleton].controller;
[super bringSubviewToFront: controller.scrollView]; //Now scroll view is in the front
[controller.scrollView touchesBegan: touches withEvent: event]; //Does nothing
}
I need to override hitTest:withEvent: in the UIImageView subclass.
(UIView*) hitTest: (CGPoint)point withEvent: (UIEvent*)event
{
if([self pointInside:point withEvent:event]){
ViewController* controller = [Singleton getSingleton].controller;
[controller.view bringSubviewToFront: controller.scrollView];
return controller.scrollView;
}else{
return [super hitTest:point withEvent: event];
}
}
This way, when a touch on the UIImageView is issued, it would first call this method, which would bring the UIScrollView to front and then return the UIScrollView as the object which should handle the event.

Sublassing UIScrollView performSegue on ViewController

I have a custom UIScrollView class that implements the touchesEnded method. My project has a storyboard, and I am trying to call the performSeguewithIdentifier on the viewcontroller but when I call the method changePhoto it gives me the error below. I have verified that my segue is labeled correctly, it is not misspelled. It looks like when I am creating the new instance of the PhotoViewController it doesnt create any of the storyboard and/or the segues. What can I do to call to get the segue to work properly from my UIScrollView?
CustomUIScrollView.m
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
if ([[touch view] isKindOfClass:[UIImageView class]]) {
UIView *imageView = [touch view];
NSInteger tag = imageView.tag;
NSLog(#"Tag: %d", tag);
PhotoViewController *vc = [[PhotoViewController alloc]init];
[vc changePhoto:1];
}
}
PhotoViewController.m
-(void)changePhoto:(int)spaceId {
[self performSegueWithIdentifier: #"photoSegue" sender: self];
}
Error:
2012-10-09 10:28:38.765 Spaces[82945:11303] * Terminating app due to
uncaught exception 'NSInvalidArgumentException', reason: 'Receiver
() has no segue with identifier
'photoSegue''
Try initializing you view controller with this
PhotoViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"myIdentifier"]
Be sure to set the identifier of the according view controller in the interface builder.

Pushing a controller from a subview

In essence what is the correct way of pushing a new view controller from a sub view of a navigation controller.
The issue being subviews of the navigation view don't inherit the self.navigationController (its nil)
The reason is I need separate controllers for the navigation bar view & the main view but both need to push new controllers.
I am willing to change this model if someone can tell me the correct way of doing this.
Also:
AppDelegate *del = (AppDelegate *)[UIApplication sharedApplication].delegate
[del.navigationController pushViewController:vc animated:YES];
Does not work as the delegates controller is nil.
Create a following category on UIView.
#interface UIView (GetUIViewController)
- (UIViewController *)viewController;
#end
#implementation UIView (GetUIViewController)
- (UIViewController *)viewController;
{
id nextResponder = [self nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return nextResponder;
} else {
return nil;
}
}
#end
Now get the SuperView from the subView.
mySuperView = [mySubView superview];
Then call the method from category created.
mySuperViewController = [mySuperView viewController];
Now, using this viewController, you can access the navigationController.
I have not tried the above code and approach, but I hope it should work.
You may try two ways
1.Use the superViewController's navigationController to push your viewController
2.Embed your current viewController in a NavigationController so that the navigationController won't be nil

UIWebView's inside UIScrollView consuming touch events

I have multiple custom UIWebView's as subviews within a UIScrollView. I have implemented the touch methods within the UIViewController for the UIWebView's, but these methods are not being called.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { }
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { }
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { }
I have read elsewhere and it says that the UIScrollView will consume the events. This makes sense, but should the UIWebView's not receive the touch event before the UIScrollView itself? What is probably happening, is that the UIWebBrowserView (a subview of the UIWebView) is consuming the events. This class can't be subclassed however, and so I can't send the events up the superview chain to the UIScrollView. I have read on the Apple iOS Documentation that one shouldn't ever place a UIWebView inside a UIScrollView, but I need to do so for my purposes.
So my question is: how can my custom UIWebView's (that are subviews of a UIScrollView) receive touch events?
My controllers and views are set up as follows:
HomeViewController.h /.m /.xib
HomeView.h /.m (is a UIScrollView)
DashboardViewController.h /.m /.xib (custom UIWebView's)
HomeViewController is loaded from a Nib file that contains a custom view: HomeView.
DashboardViewController is loaded from a Nib file. It contains a few labels, sliders, etc. (note that there is no custom view here - I am not subclassing UIView here).
HomeViewController dynamically loads several DashboardViewControllers and adds its view to the HomeView.
DashboardViewController *dashboardViewController = [[DashboardViewController alloc] initWithNibName:#"DashboardViewController" bundle:[NSBundle mainBundle];
[homeView addSubview:dashboardViewController.view];
You are probably looking for canCancelContentTouches.
scrollView.canCancelContentTouches = NO;
The User Interaction is standard enabled.
Did you connect your views delegate to the File's Owner?
And did you bind your View to the Controller? And make sure you connect the corresponding actions to the correct methods. (CTRL Click on the View and drag the plus sign onto the corresponding method in your Class.h file.
#interface MyScrollView : UIViewController {
UIView *myFirstView;
UIView *mySecondView;
UIView *myThirdView;
}
#property (nonatomic, retain) IBOutlet UIView *myView;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
#end
Have you set userInteractionEnabled = YES for all the subviews?
UIViewController gets the touches* messages before the UIView. The UIViewController doesn't pass the message along to the UIView (by calling [super touchesBegan]) by default. So you need to move or forward the touches* functions if your UIView has a controller.
Personally I put the various touches* methods on the view subclass, not the view controller subclass.
You can try this, when you get the touches methods being called for UIScrollView in turn you can call the same touches method in subview from scrollview.

How can I use a single UIToolbar with multiple different UIViewControllers?

I have a simple app with two basic screens, a UIMapView and a UITableView. I'd like to have a toolbar at the bottom with a couple of buttons and a UISegmentedControl with two segments: "Map" and "Table". (The layout is similar to the Google Maps app that ships with the iPhone.) How can I keep the same toolbar while presenting either the UIMapView (with a UIMapViewController) or the UITableView (with a UITableViewController) when the user switches back and forth on the segmented control? Of course, I can just create an identical toolbar for each of the two different views and display them separately, but is there a better way?
Write a UIViewController that manages your 2 VC's and transitions between the MKMapView and UITableView in response to the segmented control.
First set up the nib for this new VC in Interface Builder: add an UISegementedControl and a simple UIView (contentView).
The interface file contains references to the UI Elements and to the 2 VC's + an action to respond to the segmented control:
//
// MapAndTableViewController.h
//
#import <UIKit/UIKit.h>
#import "MyMapViewController.h"
#import "MyTableViewController.h"
#interface MapAndTableViewController : UIViewController {
IBOutlet UISegmentedControl* segmentedControl;
IBOutlet UIView* contentView;
UIViewController* firstVC;
UIViewController* secondVC;
}
-(IBAction) valueChanged:(UISegmentedControl*) sender;
#end
Implementation:
//
// MapAndTableViewController.m
//
#import "MapAndTableViewController.h"
#implementation MapAndTableViewController
-(IBAction) valueChanged:(UISegmentedControl*) sender {
if (sender.selectedSegmentIndex == 0) {
[UIView transitionFromView:[contentView.subviews lastObject] toView:firstVC.view duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
}
if (sender.selectedSegmentIndex == 1) {
[UIView transitionFromView:[contentView.subviews lastObject] toView:secondVC.view duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
}
}
-(void)awakeFromNib {
firstVC = [[MyMapViewController alloc] initWithNibName:#"MyMapViewController" bundle:nil];
secondVC = [[MyTableViewController alloc] initWithNibName:#"MyTableViewController" bundle:nil];
}
- (void)viewDidLoad {
[super viewDidLoad];
[contentView addSubview:firstVC.view];
}
- (void)dealloc {
[firstVC release];
[secondVC release];
[super dealloc];
}
#end
In the valueChanged method you replace the current view and animate the transition.
Note that the views firstVC.view and secondVC.view are created on first access of the view-property of each VC.
you could use a single view controller, and add all of the views(UIMapView, UITableView, etc) to your view and simply show/hide the correct views upon clicking the segmented control
with such a simple app without many views, you shouldn't have a messy/clustered view controller file and can easily show/hide these 2 views.
perhaps use an animation between switching between views so it looks nice