In my app I have a UIScrollView and within that I have programmatically created UIImageViews. This is based off the Tab-bar template in Xcode. What I'm trying to do is to change Tabs when a particular image is pressed, and whilst I'm able to register the event as a touch, changing the tabIndex does nothing.
Typing in the following code:
UITabBarController *tab=self.tabBarController;
if (tab){
NSLog(#"Good");
}
else {
NSLog(#"No Good");
Always results in No Good being Logged. The code I've written can be seen here, where my UIScrollView is of type ScrollViewer:
#implementation scrollViewer
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event {
if (!self.dragging) {
NSLog(#"Recieved Touch!");
UITouch *aTouch = [touches anyObject];
MainView *theInstance = [[MainView alloc] init];
CGPoint point = [aTouch locationInView:self];
theInstance.ycoord = point.y;
[theInstance touchHand];
}
[super touchesEnded: touches withEvent: event];
}
#end
Any help is appreciated thanks!
You never gave us any sample output for:
NSLog(#"Y Coord= %d",adjustedy);
But I will assume the value should cause the switch.
So first you need to log self.tabbarcontroller to be sure it is not nil. If it is ok the system may not want to let you switch inside touch events (it's bad form anyway). I would do the setting as this:
dispatch_async(dispatch_get_main_queue(), ^{ do code here } );
So it runs later.
Related
I have been trying to figure this out for some time now. I am using auto layout with a scrollview. Inside the scrollview is a UIView, another UIView and then the subview within that. I can't get touch to work with the subview. I have tried touchesbegan along with uitapgesture with no success. Any suggestions? Here is the layout hierarchy.
ScrollView
---- UIView
------- UIView
------ UIView - Need to detect touch here.
EDIT:
Sorry guys, was at work. Here is some code. I have subclassed my scrollview to receive touches as it seems to be working best so far getting down to the subview level of other view subviews.
The following code detects that the view is that class without the pointInside. I am currently having issues receiving the right point for the view.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UIView *relatedVidsView = [[[touches allObjects] firstObject] view];
for (int i = 0; i < relatedVidsView.subviews.count; i++) {
UIView *view = [relatedVidsView.subviews objectAtIndex:i];
CGPoint touchPoint = [[[touches allObjects] firstObject] locationInView:self];
if ([view isKindOfClass:[RecipeVideoView class]] && [view pointInside:touchPoint withEvent:nil]) {
NSLog(#"TEST");
}
}
[super touchesBegan:touches withEvent:event]; }
EDIT 2:
So I was able to figure this out. I subclassed the view that was containing the subviews that i needed touch events for. What I did was passed the events from the parent to the subviews using touchesBegan. I will post my code later, thanks for the help anyway.
I have an app in which the user can interact with many objects. These are several UIButtons, a few UILabels and a lot of UIImageViews.
The focus of all interaction centers around touching the UIImageView objects. With a touch I can move the images around, tell them to do this or that. However, my current obstacle is in knowing how to properly have the app distinguish touches that occur when I touch a UIButton.
Why? Well the logic within the Touches Began event is meant to be only for the UIImageViews, however the moment that I touch a button, or any other object, the app is interpreting the touch as if it occurred for a UIImageView object.
So, my approach boils down to: is there a good way of identifying if a touch occurred for a UIButton, UIImageView, UILabel object? That way I can filter out the irrelevant touches in my app from the relevant ones.
EDIT:
The code below outlines how I capture the touch event, however I do not know how to know from the touch event whether it is a button or a view that I touched.
touch = [touches anyObject];
touchLocation = [touch locationInView:[self view]];
To know whether UIButton is pressed follow this:
-(void) touchBegan : (NSSet *) touches withEvent : (UIEvent *) even
{
UITouch *touch = [touched anyObject];
UIView *touchedView = [touch view];
if([touchedView isMemberofClass : [UIButton class])
{
//do something when button is touched
}
}
Call hitTest:withEvent: on the view with the touch event to get the view that is actually being touched. Then you can use isKindOfClass: to check what type of view it is and respond accordingly.
you can use this method :-
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if ([self pointInside:point withEvent:event]) {
//You clicked inside the object
}
return [super hitTest:point withEvent:event]
}
and wain has already given you the explanation for it..
https://stackoverflow.com/a/18051856/1865424
I've read the apple docs on the responder chain and needed to know: How is it do i NSLog the object that was tapped on?
Let's say I have a very complex view controller with multiple views object, when ever i tap on an object (UIButton or what ever..) is there a way to know that particular object that was tapped on?
The docs gave a good overview, but wasnt to clear in methods to overwrite.
EDIT:
The situation is in testing different apps i have not written. I need a quick way to determine the Object that was tapped on (as many apps have custom controls/object that looks like one thing, but is really another). I was hoping there was some way to intercept the touch event right as it was being sent to UIAppication, and then NSLog it.
You can override -[UIApplication sendAction:to:from:forEvent] to do what you want:
#implementation MyApplicationSubclass
- (BOOL)sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event
{
NSLog(#"Sending action %# from sender %# to target %# for event %#", NSStringFromSelector(action), sender, target, event);
return [super sendAction:action to:target from:sender forEvent:event];
}
#end
Put that in a custom subclass of UIApplication. Then, in main.m, change the call to UIApplicationMain() so that your custom subclass is used:
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, NSStringFromClass([MyApplicationSubclass class]), NSStringFromClass([AppDelegate class]));
}
}
Note that this only works for UIControl subclasses, which send their actions to their targets using this mechanism. If you want to see all touch events going through an app, override -[UIApplication sendEvent:] instead. In that case, it will be up to you to figure out which object is going to receive the touch. You can do that with by calling -hitTest: on your main view/window, though keep in mind that that figures out which view the touch lands on, not necessarily which view handles it (views can forward events to other objects, for example). Something like this:
#implementation MyApplicationSubclass
- (void)sendEvent:(UIEvent *)event
{
UIWindow *window = [self keyWindow];
NSSet *touches = [event touchesForWindow:window];
for (UITouch *touch in touches) {
UIView *touchedView = [window hitTest:[touch locationInView:window] withEvent:event];
NSLog(#"Touch %# received in view %# for event %#", touch, touchedView, event);
}
[super sendEvent:event];
}
#end
For a button the action method normally has that param,
- (void)action:(id)sender {
Here sender represents the button. You can use it as,
UIButton *button = (UIButton *)sender;
button.hidden = YES;//use the properties of button now
You can also check with the UITouch delegate methods.
For eg:-
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *myTouch = [[event allTouches] anyObject];// or UITouch *touch = [touches anyObject];
CGPoint point = [myTouch locationInView:myViewToCheck];
if (CGRectContainsPoint(myViewToCheck.bounds, point) ) {
//Touch detected on myViewToCheck.
}
Have you had a chance to look at https://github.com/domesticcatsoftware/DCIntrospect.
Straight from github: Introspect is small set of tools for iOS that aid in debugging user interfaces built with UIKit.
It has a logging component that may be of use?
I'm trying to implement a uitableview that its rows can be dragged to right and left (and show something behind them).
The code works fine, I've implemented it using the following methods:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
My problem is that the rows also contain UIButtons that when clicking them- should be clicked but when dragging - should drag the entire cell.
I've found this solution. Basically to bubble up the events when clicking on the UIButtons:
[super touchesBegan:touches withEvent:event];
[self.nextResponder touchesBegan:touches withEvent:event];
But, it seems taht the event touchesMoved only bubbles once.
I've seen all sort of questions in this area. Example. But I don't see any solution or responses.
Any help, suggestion or creative workaround would be appreciated!
Instead of implementing touchesBegan, etc. why not use a UIPanGestureRecognizer? I tested this with just a simple rectangular view which was mostly covered by a UIButton. The view was dragged, no matter where I touched, and the button method fired if I clicked over the button.
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)];
[self.theView addGestureRecognizer:panGesture]; //theView is IBOutlet for small view containing a button
}
-(void)viewDidAppear:(BOOL)animated {
self.currentViewFrame = self.theView.frame;
}
- (IBAction)handlePanGesture:(UIPanGestureRecognizer *)sender {
CGPoint translate = [sender translationInView:self.view];
CGRect newFrame = self.currentViewFrame;
newFrame.origin.x += translate.x;
newFrame.origin.y += translate.y;
sender.view.frame = newFrame;
if (sender.state == UIGestureRecognizerStateEnded)
self.currentViewFrame = newFrame;
}
-(IBAction)doClick:(id)sender {
NSLog(#"click");
}
Just check to see which one was touched using the tag system.
I have made a UISlider work just like the "slide to unlock" slider.
What I need to do is determine the point at which lifting your finger off is classed as touchUpOUTSIDE and not touchUpINSIDE. This is the point where you slide your finger past the end of the slider too far.
I guess it's the same as with a UIButton, you can press the button then slide your finger off the button and depending how far you go, it can still be classed as touchUpInside.
If possible, i'd like to mark the target area with a circle.
Once i've managed to find where this point is, is it possible to change it? So I could have a bigger target area?
I really don't know where to start with this. Thanks
According to the docs, the UIControlEventTouchUpOutside event is triggered when the finger is outside the bounds of the control. If you're trying to change that area, the slider will scale with it. Why not just tie the action for UIControlEventTouchUpOutside to the same as UIControlEventTouchUpInside?
It's taken me a few hours, but i've managed to sort this.
I've done a lot of testing overriding touchesMoved, touchesEnded and sendAction:action:target:event and it seems that any touch within 70px of the frame classes as a touch INSIDE. So for a UISlider that's 292x52 any touch from x:-70 to x:362 or y:-70 to 122 will count as an inside touch, even though it's outside the frame.
I have come up with this code that will override a custom class to allow a bigger area of 100px around the frame to count as an inside touch:
#import "UICustomSlider.h"
#implementation UICustomSlider {
BOOL callTouchInside;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
callTouchInside = NO;
[super touchesMoved:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint touchLocation = [[touches anyObject] locationInView:self];
if (touchLocation.x > -100 && touchLocation.x < self.bounds.size.width +100 && touchLocation.y > -100 && touchLocation.y < self.bounds.size.height +100) callTouchInside = YES;
[super touchesEnded:touches withEvent:event];
}
-(void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
if (action == #selector(sliderTouchOutside)) { // This is the selector used for UIControlEventTouchUpOutside
if (callTouchInside == YES) {
NSLog(#"Overriding an outside touch to be an inside touch");
[self sendAction:#selector(UnLockIt) to:target forEvent:event]; // This is the selector used for UIControlEventTouchUpInside
} else {
[super sendAction:action to:target forEvent:event];
}
} else {
[super sendAction:action to:target forEvent:event];
}
}
With a little bit more tweaking I should be able to use it for the opposite also. (Using a closer touch as an outside touch).