I have a UIViewController with a subclass of UIView on it called customSubView1. Then on customSubView1 I have another subclass of UIView called customSubView2.
I can capture all the events for touches on all the subviews when I put touchesBegan/touchesMoved/etc in the UIViewController class. But I want to be able to process them in my custom classes.
I keep reading that the UIViewController class needs to 'forward' the touch events to the subviews, but I haven't been able to find any example code to do this. Does anyone have any idea?
Many thanks,
Brett
Implement the touches... methods in your views.
Try to set for you UIView object userInteractionEnabled property:
myUIView.userInteractionEnabled = YES;
Related
I'm adding a subview to my primary iPad UIViewController, and within that subview I need to reference said view controller in order to play a video using that controller.
Can anyone help me out with the way that should be done, and possibly a code example?
Thank you in advance.
Regards,
Ed
EDIT (A LITTLE BIT MORE INFO):
This subview is a view from a uiviewcontroller class that is designed for the iPhone. It's a table that loads a video when a row is pressed. The movieplayer loads the video within the referenced viewcontroller (which is why I want to reference the iPad view controller from within the subview). The view is basically used within an iPad app in it's same form.
This sounds like an architecture problem. It's not up to your view to tell something to play a sound. That's a controller's job. It's up to your view to tell "someone" that it was touched, or slid, or whatever the user has done. "Someone" (who is watching) will then perform the correct response to that.
To do this, your view should generally take a target, and possibly and action. Look at how UIControl (for example, UIButton) informs other objects that it has been activated. The observer (controller) then reacts accordingly.
EDIT
The view should not load a video. A view controller should load the video and install it into the correct view. The only job the view has is to tell its view controller that it has been pressed. UITableView handles this automatically with the UITableViewDelegate method tableView:didSelectRowAtIndexPath:. If you're not using a UITableView, you should still follow this pattern. The view accepts has a delegate and tells the delegate (controller) that something was selected. Then the controller updates the views with the new data.
You probably already have the main view living in a property of your application delegate (it is commonly assigned via the application's .xib file, look in the app delegate's applicationDidFinishLaunching: method, where it adds the main view as a subview to the window, something like:
[window addSubview:primaryController.view];
[window makeKeyAndVisible];
).
So anywhere in your app where you need to access the main view, you can do:
[(MyAppDelegateType*)([UIApplication sharedApplication].delegate).primaryController somePrimaryControllerMethod];
Edit: while this will work, I agree with Rob Napier that this isn't the best way to do it, architecture-wise.
You could just use [yourSubview superview] to get the superview.
Can't you just add a property to the view like this:
#property (nonatomic, assign) UIViewController *parentViewController;
And on initialization of the view (from within the UIViewController), set the property:
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 460.0f)];
customView.parentViewController = self;
And from then on you should be able to call the parentViewController from within the view.
Edit: Rob Napier has a good point, it's probably a better idea to set a target and selector from within the view instead of a view controller. This way you should be able to hook up methods directly with the view. The properties would look like this:
#property (nonatomic assign) id target;
#property (nonatmic, assign) SEL selector;
And perhaps add a designated initializer to your view:
- (id)initWithFrame:(CGRect)aFrame target:(id)aTarget selector:(SEL)aSelector
{
self = [super initWithFrame:aFrame];
if (self)
{
self.target = aTarget;
self.selector = aSelector;
}
return self;
}
I have an inputAccessoryView for a UITextField set up, which is loaded from a XIB when needed (in the exact same way as Apple's KeyboardAccessory example).
I'm trying to get the buttons on it to click when pressed using the playInputClick function but I can't work out how. The Apple documentation says that I need to add add a delegate method to the view, but the view was created purely in interface builder so I don't see how I can do this.
Does anyone know how to make this work? There seems to be no example code of this method being used anywhere on the internet.
I've been having the same problem and finally figured it out.
If you implement the protocol in a UIViewController it does not work. It needs to be implemented in a UIView subclass.
So to get it working, I created a view in interface builder with a view controller. Then I added another class which is a subclass of UIView. This class implements the UIInputViewAudioFeedback protocol. Then in interface builder I went to the Identity inspector of my view and changed the class to my UIView subclass which implements the UIInputViewAudioFeedback protocol. And the keyboard click sound is now working.
My view controller is still calling the [[UIDevice currentDevice] playInputClick], all I did was move the code for the UIInputViewAudioFeedback protocol into a UIView Subclass and set my views class to my UIView sublass.
Assuming you are trying to do this in an inputAccessoryView:
In the .h file, indicate that you implement the UIInputViewAudioFeedback
#interface YourAcessoryView : UIView <UIInputViewAudioFeedback>
In the .m file, add this method to satisfy the protocol
- (BOOL)enableInputClicksWhenVisible {
return YES;
}
When a button is pressed, do something like:
- (void)buttonPressed:(UIButton*)sender
{
[[UIDevice currentDevice] playInputClick];
// do more stuff
}
You need to implement a view controller class, and in the interface builder assign the files'owner as this class. Connect the view controller's view to your view.
Once you have done that you can implement the delegate in the view controller.
Its pretty simple and straight forward. Basically your view will be a member of a view controller class.
So I just have a standard UIViewController with a UIWebView in it that displays a pdf. For the app functionality, I have need to be able to respond to the UIWebView's nested UIScrollView events like scrollViewWillBeginDragging, scrollViewDidScroll, etc.
The only way I can get access to the scrollView is to (it seems like a hack) go in and get it by the subviews array:
for (id subview in webView.subviews){
if ([[subview class] isSubclassOfClass: [UIScrollView class]]) {
UIScrollView * s = (UIScrollView*)subview;
s.delegate = self;
s.tag = 1;
scrollView = s;
}
}
But that seems to introduce more problems than it's worth, because I lose native UIScrollView stuff like zooming.
So to sum up what I'm needing:
What is the best way to set my UIViewController class as the delegate of the UIScrollView inside of the UIWebView? Is there something I need to do with subclassing my own UIWebView so that it handles events from it's built in UIWebView a certain way and then pass it along somehow to the UIViewController? How does that passing thing work anyway?
Please advise!
Thank you!
Have you checked there is only one UIScrollView subclass in the subviews? Bung in a log in your loop to see. If there is more than one, then you'll only pick up the last one using your code.
If there is just one UIScrollView subclass, you could try saving a reference to its delegate and then in your own delegate methods passing messages on after you have done your business.
So, in your loop, something like
originalDelegate = s.delegate
And then for the delegate methods, something like:
- (void) scrollViewDidScroll: (UIScrollView*) scrollView;
{
// do your stuff
[originalDelegate scrollViewDidScroll: scrollView];
}
You might need to check whether originalDelegate responds to the selector before calling it, i.e. if ([originalDelegate respondsToSelector: #selector(scrollViewDidScroll:)) etc. If it were me, I'd start by implementing all twelve delegate methods defined in the UIScrollView delegate protocol.
Not tested this, so will be interested to know if it can be made to work. Do note, the docs explicitly say that UIWebView "should not be subclassed"
hope someone can help me on this as been stuck for hours.
I am trying to make a kind of picture book.
I have a view which is my container and I add subviews to that by using addsubview.
On the subview, I have swipe gestures etc that I want to trigger off method in the parent view. I worked out how to trigger the delegate but I cant get the delegate to trigger the parent view. I have read over 10 different ways of doing it and none work.
I now very confused about what a super view is to. Just to confuse matters, the delegate has a tabcontroller and the parent view is tab button 1
I tried
[self.view.superview method]
[self.superview method]
On the delegate I tried
self.tabcontroller.parentviewcontroller, selectedview, super view.super
UPDATE :
The subview needs to be independant of the parent view as its a reusable view.
Also I have not set the parentview to superview as I just thought a superview is a view with subviews (please don't kill me). So maybe I just need to set the parentview to a superview?
The proper way of doing such things is to use protocol and delegate pattern.
Define a protocol like
#protocol subViewDelegate
-(void)somethingHappened:(id)sender;
#end
then implement that protocol in your superview :
#interface superView:UIViewController<subViewDelegate> {
...
}
...
#end
define a delegate property in your SubView like this
#interface subView : UIView {
id<subViewDelegate> delegate;
...
}
#propery (nonatomic, assign) id<subViewDelegate> delegate;
...
#end
the in your subview, call the delegate like this
[self.delegate somethingHappened :self];
It's a little hard to help you without any code given, but let's try:
Create a protocol: Name it however you like (I will call it "MyProtocol") and add to it the definition of the function you want to call in your superview, let's call it "respondToSwipe"
If your superview is a UIView, you have to create your own subclass of UIView and make your superview an instance of that class.
Let your (newly) created superview class implement the protocol of 1.) an implement the "respondToSwipe" method
Create an instance variable of the the type id in your subview, and name it however you like, e.g. "myDelegate".
Pass the superview created in 2/3.) to your "myDelegate" variable
Call [myDelegate respondToSwipe] whenever you like
For a custom view, you could subclass UIControl and use control events:
Define some control events. You're free to make up 4 control events (UIControlEventApplicationReserved = 0x0F000000)
Have whoever wants to receive events call addTarget:action:forControlEvents:
Have the control call [self sendActionsForControlEvents:events]
Alternatively, you could use a UIGestureRecognizer-style interface (addTarget:action:).
Alternatively just use UIGestureRecognizer (OS 3.2+)
Did your parent view set itself to be the superview of the subview when it added the subview? Otherwise the subview doesn't know who its superview is.
The more standard way of naming things to call the method handler the delegate instead of the superview, make it a property, and have the subview check for both the existence of the delegate being set and whether it can handle the method.
Here a very good example of how apply the delegation pattern on the iPhone. I downloaded the code an it works pretty good.
http://www.hivestudio.cat/index.php?option=com_content&view=article&id=57:technical-note-the-delegation-pattern-on-the-iphone&catid=35:technical-note-category&Itemid=76
I needed to see touch events on my window, so I subclassed UIWindow to make my a MYWindow class. I am overriding the sentEvent function to receive the touch events on the window and all of that is working just fine. I did the following to achieve this:
self.window = [[MYWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window makeKeyAndVisible];
The issue I am facing is that when I do this, the viewControllers that are on this window won't rotate anymore. If I were to use a regular UIWindow, they all work just like I intended them to work. Obviously, something is wrong with my setup.
I was looking through UIWindow header file and there is a method called
- (void)becomeKeyWindow; //override point for subclass, Do Not call directly
Am I suppose to implement this in my custom UIWindow class just like I had to implement the sendEvent: method? Please point me in the right direction with this.
Thanks in advance guys.
I figured out what the issue was. I was creating a delegate on the custom window and called it "delegate" which was causing the issue. Naming it to "aDelegate" solved the issue. thanks for all your help.
I'm only guessing here, but I'd presume it is because you made MYWindow the key window now (even if it is transparent) and shouldAutorotateToInterfaceOrientation: is part of UIViewController class, not UIWindow.
So even though you can see your other views, they are not getting their rotation events called on them anymore.
Instead of making it a UIWindow subclass, why not implement those touch events on a UIView that gets added to your view that needs the touch events handled? You can make it transparent as well, and just keep the rotation handled at the UIViewController level?