hi i am observing something weird in iOS custom views, where i have a button in side a custom view, i am looking for documentation or your answer to explain why i have this observation
i have a custom view (subclass of UIView), it contains a simple button, i bind the button touch event via addTarget:Action with UITouchUpInside, i just bind it selector to a simple method that NSLog a message
if you display the custom view and hits the button, you can see the message in the console, everything works as expected.
However, if you add a UIGestureReognizer to the custom view, and run it, when you click the button, it no longer prints the message, WHY???
i did more in depth investigation, and by looking at the hittest method IN THE CUSTOM VIEW (the view contains the button) (i override it, but i didn't do anything, i just call super again), the hittest is returning the correct view, i.e. when i click on the button, hittest method of the custom view is returning UIButton
i do not understand the event chain here, can someone pointing the documentation that explains this?
it seems to me when you add the regonizer to the container view of the button, for some reason, the button DOES NOT KNOW how to handle the event, so it asks its superview (custom view in thsi case) to handle it, but why?? i already bind the event handeler to the button via UITouchUPInside
please please help i want to understand this
Set your recognizer's cancelsTouchesInView property to NO.
Related
I am working on user interaction in different uiviews.
I know how to send user interaction on parent view or on it's specific UI components.
In my example, I am sending event to UIButton that is working properly even I tap on outside of UIButton bounds (Please take a look on attached code for more inside).
But I don't know that when I tapping on top view, UIButton TouchDown selector called but TouchUpInside not calling. Why is it happening?
Any suggestions? Any explanation is greatly appreciated!
Github code link: https://github.com/jackMac1811/iOSUIInteractionTest
All uibutton events carrying their own behavior which enables to execute its bunch of code according to it.
if you want to invoke both methods tapping on top view you should have to use touch up outside instead of touch up inside event
Here's a very useful link https://stackoverflow.com/a/11390048/4003548
Hope this helps ..
Lets say that i have an animation - an image is going from left side of the screen to the right. I would like to make it a little bit interactive - when user taps on a screen i want to change direction of image movement. Whats the best approach to implement it?
What I do in some cases is take the main view of the View Controller, in Storyboard, and change the class type of that UIView to UIControl.
In the code that is accessed as MyViewController.view, which you can write:
var viewAsControl = myViewController.view as UIControl
In Swift or some equivalent of that.
The UIControl subclass of UIView is the hierarchical layer (class) that adds the action/target facilities to a view. For example, UIButton is a UIControl, because it generates events (actions), and it is also a UIView so it can be added as a subview.
Then from the Connections Inspector, accessed via the far right Icon of the far right panel (that is, the panel to the right of the storyboard editor window), I'd select the Touch Up Inside event type or some other event and drag it to an #IBAction tagged function I'd add to the View Controller's source code, to receive the tap event. From that tap notification, you can cancel the current animation and add a new one, etc...
Alternatively, you can create an IBOutlet for the view if you've turned it into a UIControl in IB, and use the addTarget() method to assign an action handler for a specific event, e.g. to make it call a function in your code.
Either way the effect will be that any time the view is tapped, it will generate the event for you to respond to
In the XCode IDE, at the bottom of the view controller in the MainStoryboard editor, are two boxes: First Responder, and Exit.
I know what a firstResponder is programatically within the code, but in the storyboard editor, I can't seem to do anything useful by it.
Am I able to use the first responder in this area to somehow set the first responder of the view? I'd like the first textfield to be active on load and I have tried right+click and dragging to no avail. I know I can set it programatically in the viewDidLoad method, but is there some way of doing it here?
And what is the green Exit for?
There are no good answer for this question, so I am posting my answer:
From here:
Note: You probably won’t be using the First Responder very much. This is a proxy object that refers to whatever object has first responder status at any given time. It was also present in your nibs and you probably never had a need to use it then either. As an example, you can hook up the Touch Up Inside event from a button to First Responder’s cut: selector. If at some point a text field has input focus then you can press that button to make the text field, which is now the first responder, cut its text to the pasteboard.
Edit:
1) First Responder is very useful if you are using text fields with keyboard notifications. I use it to make keyboard disappear, make an outlet to variable currentFirstResponder of your class, and in viewWillDisappear:
[self.currentFirstResponder resignFirstResponder];
2) You can read about unwind segues ("Exit" box) here
I've never used it and probably never will but you can assign an object to be the first in line to receive the events from the UI.
I suppose you could be creating a UIView subclass and add it in to a UIViewController but you actually want some other object to receive and process the events other than the UIViewController you are adding it to.
I found this link which kind of explains it a bit better.
First Responder: The First Responder icon stands for the object that the user is currently interacting with. When a user works with an iOS application, multiple objects could potentially respond to the various gestures or keystrokes that the user creates. The first responder is the object currently in control and interacting with the user. A text field that the user is typing into, for example, would be the first responder until the user moves to another field or control.
Exit: The Exit icon serves a very specific purpose that will come into play only in multiscene applications. When you are creating an app that moves the user between a series of screens, the Exit icon provides a visual means of jumping back to a previous screen. If you have built five scenes that link from one to another and you want to quickly return to the first scene from the fifth, you’ll link from the fifth scene to the first scene’s Exit icon.
More here
You don't see this very often, where a deleted answer is actually correct, and the comment (likely influencing its deletion) on it is totally wrong! I'll try and improve on it.
Usually the IBAction you want to hook up to a button is in the view controller containing the button. However if the IBAction is in a different controller, e.g. a parent controller then drag from the button to the First Responder object and you are able to select the IBAction in the parent controller!
As the hidden answer states, how this is implemented is the action is sent to nil, which has the effect of the responder chain (i.e. view hierarchy) being searched for the action, as follows:
[UIApplication.sharedApplication sendAction:#selector(nextObject:) to:nil from:self forEvent:nil];
An example is a custom UITableViewCell. Add a UIButton to the cell but you want the action to go up to a View Controller that has an embed segue to a UITableViewController. Drag the touch up instead action to the First Responder and select the action in the container view controller. In the action to find the indexPath simply loop the visibleCells and check if the sender is isDescendantOfView:
- (IBAction)cellButtonTapped:(id)sender{
for(UITableViewCell *cell in self.tableViewController.tableView.visibleCells){
if([sender isDescendantOfView:cell]){
NSIndexPath *indexPath = [self.tableViewController.tableView indexPathForCell:cell];
NSLog(#"tapped %#", indexPath);
}
}
}
Another example could be a reload button: say your first view controller shows an downloaded item with an IBAction to reload it to get the latest data, then your child controller shows some detail, but you also want them to be able to reload the main item from within the detail, just add a button in the detail and drag its action to First Responder and select the reload IBAction in the parent controller. This allows you to hook up buttons to parent actions with no additional code like delegate methods!
For this to work the action needs to be in the responder chain hierarchy or it won't be found, you can read how the chain is built up in the docs. Also note if called from code the view needs to have appeared, viewWillAppear is too soon.
This is one of those "it was working a while ago" troubleshooting efforts.
I'm working on the document preview view controller, in which is a scroll view, which itself contains subclasses of UIView that represent each document. I'm modeling this pretty closely to how Keynote handles its document preview, except I build my scroll view horizontally and with paging. But the standard user experience is present: Long press on a document icon causes all document icons to start jiggling, nab bar has + button and Edit button, etc.
The issue at hand is that when you tap on the name of a document, I hide all the others, move the one being edited front and center, build a new text edit field, add it as a subview atop the real name label, and set it as first responder; but the
[editNameTextField setClearButtonMode:UITextFieldViewModeWhileEditing];
while correctly showing in the edit field is not taking any action when the user taps on the clear button.
I can't figure out what I may have done to cause this to not work -- it had been!
My first thought was that somehow my instance of this subclass is no longer the delegate for this text edit field. To try and confirm/deny that, I usurped a tap on the image view of the document preview to compare the delegate property to self, and it passes.
if (editNameTextField) {
NSLog(#"editNameTextField is still active");
if ([editNameTextField.delegate isEqual:self]) {
NSLog(#"we're still the delegate for the editNameTextField");
}
}
Editing the text within the edit field works fine. Pressing the Return/Done key correctly sends the delegate message textFieldShouldReturn:
While investigating this I implemented the delegate method textFieldShouldClear: just to write a log message if the method gets called (and return YES of course). It never gets called.
My next thought was that perhaps a subview had covered up the area where the clear button sits. So I implemented textFieldShouldBeginEditing: and used the opportunity to bring my the text field to the front. That didn't change anything either. I set a debugger breakpoint there to play a sound when it was called, and it got called, so I know my text edit field is frontmost.
I have only one troubleshooting strategy remaining: Go backwards through snap shots until it starts working again. Before doing that I thought I'd see if any of the more experienced folks out here have any suggestions of what to try next.
Where are you adding the textfield? As a subview of the scrollView? If you added the textfield and it is out of bounds of its parent view it won't receive any touches.
You can try and not call becomeFirstResponder and see if clicking it will show keyboard. Another possible error might be that the parent view of the UITextField has userInteractionEnabled = NO.
Without seeing more code I'm afraid I can not offer more solutions.
I'm trying to handle touch events with touchesBegan in an overlay to a parent UIView but also allow the touch input to pass through to sibling UIViews underneath. I expected there would be some straight-forward way to process a touch event and then say "now send it to the next responder as if this one didn't exist", but all I can find is the nextResponder method which appears to be giving back the parent to my overlay view. That parent is then not really passing it onto the next sibling of that overlay view so I'm stuck uncertain how to do what seems like a simple task that is usually accomplished with a touch callback that gets a True or False return value to tell it whether to keep processing down the widget hierarchy.
Am I missing something obvious?
Late answer, but I think you would be better off overriding hitTest:withEvent: instead of touchesBegan. It seems to me that touchesBegan is a pretty "high-level" method that is there to just do a simple thing, so you cannot alter at that level if the event if propagated further. The right place to do that is hitTest:withEvent:.
Also have a look at this S.O. answer for more details about this point.
I understand the desired behavior you're looking for Joey - I haven't found something in the API that supports this automatic messaging-up-the-chain behavior with sibling views.
What I originally wrote below was with respect to just informing a parent UIView about a touch. This still applies, but I believe you need to take it a step further and have the parent UIView use the hit testing technique that Sergio described on each of it's subviews that are siblings to the overlay, and have the parent UIView manually invoke a "do something" method on each of it's subviews that pass the hit test. Each of those sibling views can return a BOOL value on whether to abort informing other siblings or continue the chain.
If you find yourself using this pattern a lot, consider adding a category method on UIView that encapsulates the hit testing and asking views to perform a selector.
My Original Answer
With a little bit of manual work, you can wire this together yourself. I've had to do this, and it worked for me, because I had an oft-repeated use case (an overlay view on a button), where it made sense to create some custom classes. If your situation is similar, one of these techniques will suffice.
Option 1:
If the overlay doesn't need to do anything but look pretty, have it opt out of touch handling completely with userInteractionEnabled = NO. This will make it so that the touch event goes to it's parent UIView (the one it is an overlay to).
Option 2:
Have the overlay absorb the touch event (as it would by default), and then invoke a method on the parent UIView indicating that a touch or certain gesture was recognized, and here's what it is. This way, the UIView behind the overlay still gets to act on the touch recognition, even if someone else did the interception.
With Option 2, it's more a fit for simple UIControlEvent types, like UIControlEventTouchDown and UIControlEventTouchUpInside. In my case (a custom UIButton subclass with a custom overlay view on top of it), I'll wire touch down and touch up events on the button to two separate methods. These fire if a touch down or touch up inside event occurs on the button itself. But, they are also hooks I can invoke from the overlay view if I need to simulate that a button press occurred.
Depending on your needs, you could have a known protocol between the overlay and it's parent UIView or just have the overlay test the UIView informally, with a respondsToSelector: check before invoking performSelector: on it with the custom method you want called that would have fired automatically if the UIView wasn't covered by an overlay.