click on the texture area - objective-c

I'd like to create a function that will be called by clicking on texture object. I've just figured out how the button action is processed. Could you tell me plz how it works. Maybe some special controls have to be correspond to texture object?
thanks in advance )))

I assume this is for iOS and by click you mean tap. The following code will add a gesture recognizer to any UIView:
myView.userInteractionEnabled = YES;
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
recognizer.numberOfTapsRequired = 1;
recognizer.numberOfTouchesRequired = 1;
[myView addGestureRecognizer:recognizer];
And implement your handler like this:
- (void)handleTap:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateEnded) {
//your code
}
}
The handleTap: method will be called every time your view receives a tap.

Related

Click Event on UIImageView programmatically

I'am displaying a collection of images from code,
This is my code:
UIImageView *addview;
myimgeview *addimageview =[[myimgeview alloc]initWithImage:[UIImage imageNamed:[NSString stringWithFormat:#"%#%d",PhotoName,i]] text:[textArray objectAtIndex:i]];
I want to handle the touch event on the addimageView programmatically.
I would suggest different approach. Add tap gesture recognizer to the imageview. That is probably easiest solutions. If you have addImageView method that adds image view to view then, go on like this,
- (void)addImageView
{
UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10,10,100,100);
imageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self selector:#selector(tapped:)];
[imageView addGestureRecognizer:tap];
}
- (void)tapped:(UITapGestureRecognizer*)tap
{
NSLog(#"%#", tap.view);
}
You have to subclass it and override -touchesBegan: method or you can add gesture recognizer to this object. Here is documentation about that:
Event Handling Guide for iOS
Good way is to subclass this class and add gesture recognizer inside the class.

UIPanGestureRecognizer beyondBounds

I have this code that retrieves the coordinates of an object when it is panned:
UITapGestureRecognizer *moveBuildingTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(moveobject:)];
Method moveobject contents:
CGPoint tapPoint=[recognizer locationOfTouch:0 inView:self.view];
I use this to change the frame of - move it -an imageview using these coordinates.
However, upon dragging the image around - triggering the uipangesturerecognizer action, I found that when I drag it to the absolute bottom, I get an error that -[UIPanGestureRecognizer locationOfTouch:inView:]: index (0) beyond bounds (0).
How can I solve this exception and prevent the user from dragging past this point?
Thanks
It's weird that your moveobject: method gets called even though the private touches array of the gesture recognizer seems to be empty.
Anyway, in general, if you don't handle multitouch gestures within gesture recognizer, I would suggest to use [recognizer locationInView:] rather then locationOfTouch:inView:.
Btw:
Your talking about a UIPanGestureRecognizer while in the code you're using a UITapGestureRecognizer.
The code I would recommend to handle dragging of a particular view looks like this:
//...
UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[someView addGestureRecognizer:panGR];
//...
- (void)handlePan:(UIPanGestureRecognizer *)gr
{
CGPoint translation = [gr translationInView:gr.view];
gr.view.frame = CGRectOffset(gr.view.frame, translation.x, translation.y);
[gr setTranslation:CGPointZero inView:gr.view];
}
You should check the numberOfTouches in your UIGestureRecognizer like:
if (recognizer.numberOfTouches) {
CGPoint tapPoint = [recognizer locationOfTouch:0 inView:self.view];
}

UIWebView without Copy/Paste when displaying PDF files

I have tried to disable Copy/Paste in UIWebView by using a category and overriding canPerformAction and returning NO for copy, cut and paste selectors.
It worked as expected when I loaded a webpage or all other document formats (e.g. docx, pptx, rtf, txt) but not when I loaded a PDF document into the UIWebView.
It seems like there is some different mechanism that handles PDF documents in UIWebView which handles/responds to Copy selector, and therefore I can not block it.
I also tried to disable user interaction for all the subviews of the UIWebView's UIScrollView, which worked fine for other document formats except PDF.
Can anyone help figuring out how to disable Copy in UIWebView for PDF documents as well?
OK, so I've been experiencing the same problem myself and seem to find a solution, even if it's partial.
What I do is use a UILongPressGestureRecognizer to disable long press gestures that can lead to copy/paste.
The code:
UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress:)]; // allocating the UILongPressGestureRecognizer
longPress.allowableMovement=100; // Making sure the allowable movement isn't too narrow
longPress.minimumPressDuration=0.3; // This is important - the duration must be long enough to allow taps but not longer than the period in which the scroll view opens the magnifying glass
longPress.delegate=self; // initialization stuff
longPress.delaysTouchesBegan=YES;
longPress.delaysTouchesEnded=YES;
longPress.cancelsTouchesInView=YES; // That's when we tell the gesture recognizer to block the gestures we want
[webView addGestureRecognizer:longPress]; // Add the gesture recognizer to the view and scroll view then release
[[webView scrollView] addGestureRecognizer:longPress];
[longPress release];
This solution worked for me:
METHOD 1 - Detect Custom Long Presses
A) Create a subclass of UILongPressGestureRecogniser.
B) Include the canBePreventedByGestureRecognizer: method in your subclass, like this:
Header:
#import <UIKit/UIKit.h>
#interface CustomLongPress : UILongPressGestureRecognizer
#end
Implementation:
#import "CustomLongPress.h"
#implementation CustomLongPress
- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer*)preventedGestureRecognizer {
return NO;
}
#end
That's the only code you need in the subclass.
C) Open up the view containing your uiwebview/pdf reader. Include your subclass: #import "CustomLongPress.h" and then add the custom UILongPressGestureRecogniser to your UIWebView, like this:
- (void)viewDidLoad
{
[super viewDidLoad];
//Load UIWebView etc
//Add your custom gesture recogniser
CustomLongPress * longPress = [[CustomLongPress alloc] initWithTarget:self action:#selector(longPressDetected)];
[pdfWebView addGestureRecognizer:longPress];
}
D) Detect the long press and switch your UIWebView's userInteraction Off then back On:
-(void)longPressDetected {
NSLog(#"long press detected");
[pdfWebView setUserInteractionEnabled:NO];
[pdfWebView setUserInteractionEnabled:YES];
}
Apparently the reason this works is because the UIWebView captures long presses with its own gesture recognisers, to the exclusion of any additional gesture recongisers you've added. But subclassing your gesture recognisers and preventing their exclusion by returning "NO" to the canBePreventedByGestureRecognizer: method overrides the default behaviour.
Once you can detect the long presses on PDFs, switching the userInteraction Off then On again prevents the UIWebView from actioning its default behaviour, i.e. launching a "Copy/Define" UIMenu or, if long pressing over a link, launching a pop-up actionsheet with "Copy" and "Open" actions.
METHOD 2 - Catch UIMenu NSNotification
Alternatively, if you just want to block the "Copy/Define" UIMenu, (but not affect long presses), you can add this line (listening for UIMenuControllerDidShowMenuNotification) to your ViewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(menuShown) name:UIMenuControllerDidShowMenuNotification object:nil];
and then add this method, using the same userInteraction Off/On method as above:
-(void)menuShown {
NSLog(#"menu shown");
[pdfWebView setUserInteractionEnabled:NO];
[pdfWebView setUserInteractionEnabled:YES];
}
First method taken from: https://devforums.apple.com/thread/72521?start=25&tstart=0, and second method from somewhere on Stack, sorry forgotten where. Please include if you know.
Great answer Zubaba. I’m using a webView to display colored and bolded text and I had the same problem. I put your solution into a method and call it just after I initialize the webView. I don’t seem to need the delegate.
self.textView = [[UIWebView alloc] initWithFrame:textFrame];
[self longPress:self.textView];
- (void)longPress:(UIView *)webView {
UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress)]; // allocating the UILongPressGestureRecognizer
longPress.allowableMovement=100; // Making sure the allowable movement isn't too narrow
longPress.minimumPressDuration=0.3; // This is important - the duration must be long enough to allow taps but not longer than the period in which the scroll view opens the magnifying glass
longPress.delaysTouchesBegan=YES;
longPress.delaysTouchesEnded=YES;
longPress.cancelsTouchesInView=YES; // That's when we tell the gesture recognizer to block the gestures we want
[webView addGestureRecognizer:longPress]; // Add the gesture recognizer to the view and scroll view then release
[webView addGestureRecognizer:longPress];
}
- (void)handleLongPress {
}
In case someone needs Zubaba's answer in Swift 3;
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
longPress.allowableMovement = 100
longPress.minimumPressDuration = 0.3
longPress.delegate = self
longPress.delaysTouchesBegan = true
longPress.delaysTouchesEnded = true
longPress.cancelsTouchesInView = true
yourWebView.addGestureRecognizer(longPress)
yourWebView.scrollView.addGestureRecognizer(longPress)
func handleLongPress() {
// Show some alert to inform user or do nothing.
}
Here is a modification to Zubaba's answer in Swift 3 that ended up working for me to eliminate warning. I changed assignment longPress.delegate = self to longPress.delegate = self as? UIGestureRecognizerDelegate.
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
longPress.allowableMovement = 100
longPress.minimumPressDuration = 0.3
longPress.delegate = self as? UIGestureRecognizerDelegate
longPress.delaysTouchesBegan = true
longPress.delaysTouchesEnded = true
longPress.cancelsTouchesInView = true
webView.addGestureRecognizer(longPress)
webView.scrollView.addGestureRecognizer(longPress)
webView.loadRequest(request)
The UILongPressGestureRecognizer is located in the UIPDFPageView. To get access to this view look at the view hierarchy in the Debug menu, currently you can access this view like so once you load the pdf to the web view:
let pdfPageView = myWebView.scrollview?.subviews[0]?.subviews[0]
Then to remove the Long Press use this method while passing in the pdfPageView:
func removeLongPressFromView(view: UIView){
if let gestures = view.gestureRecognizers{
for gesture in gestures{
if gesture is UILongPressGestureRecognzier{
view.removeGestureRecognizer(gesture)
}
}
}
}
Looking for a Xamarin.iOS solution.
var longPressGestureRecognizer = new CustomLongPressGestureRecognizer ((UILongPressGestureRecognizer obj) =>
{
Console.WriteLine ("CustomLongPressGestureRecognizer action");
});
webView.AddGestureRecognizer (longPressGestureRecognizer);
The approach given by Zubaba might look like this:
public class ZubabaLongPressGestureRecognizer : UIKit.UILongPressGestureRecognizer
{
public ZubabaLongPressGestureRecognizer (Action<UILongPressGestureRecognizer> action)
: base (action)
{
AllowableMovement = 100;
MinimumPressDuration = 0.3;
DelaysTouchesBegan = true;
DelaysTouchesEnded = true;
CancelsTouchesInView = true;
}
}
The open/copy/cancel menu still shows the first time a link is long held per PDF page. After that first long press, however, it properly does not show up for that page. That this is PDF page dependent does not give me confidence that there is a solution available.
Johnny Rockex's solutions might look like this:
public class RockexLongPressGestureRecognizer : UIKit.UILongPressGestureRecognizer
{
public RockexLongPressGestureRecognizer(UIKit.UIWebView webView, Action<UILongPressGestureRecognizer> action) :
base(UserInteractionAction(webView) + action)
{
}
private static Action<UILongPressGestureRecognizer> UserInteractionAction(UIKit.UIWebView webView)
{
return (UILongPressGestureRecognizer obj) =>
{
webView.UserInteractionEnabled = false;
webView.UserInteractionEnabled = true;
};
}
public override bool CanPreventGestureRecognizer(UIGestureRecognizer preventedGestureRecognizer)
{
return false;
}
}
and
notificationToken1 = UIMenuController.Notifications.ObserveMenuFrameDidChange (Callback);
notificationToken2 = NSNotificationCenter.DefaultCenter.AddObserver(UIMenuController.DidShowMenuNotification, Callback);
I was not able to get either to do anything. Helpfully someone else has done better with a Xamarin.iOS fix
1.ios11 iphone6 Object-C Without Copy/Paste/lookUp/share
2.
viewDidLoad{
.......
[self setupExclude];
}
- (void)setupExclude{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPG)];
longPress.minimumPressDuration = 0.2;
[self.webview addGestureRecognizer:longPress];
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:nil];
singleTapGesture.numberOfTapsRequired = 1;
singleTapGesture.numberOfTouchesRequired = 1;
[self.webview addGestureRecognizer:singleTapGesture];
UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(longPG)];
doubleTapGesture.numberOfTapsRequired = 2;
doubleTapGesture.numberOfTouchesRequired = 1;
[self.webview addGestureRecognizer:doubleTapGesture];
[singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender{
BOOL res = [super canPerformAction:action withSender:sender];
UIMenuController.sharedMenuController.menuVisible = NO;
self.webview.userInteractionEnabled = NO;
self.webview.userInteractionEnabled = YES;
return res;
}
- (void)longPG{
UIMenuController.sharedMenuController.menuVisible = NO;
self.webview.userInteractionEnabled = NO;
self.webview.userInteractionEnabled = YES;
}
3. Done!

How do I pass the user touch from one object to another object?

I am developing an application that allows the user at a certain point to drag and drop 10 images around. So there are 10 images, and if he/she drags one image onto another, these two are swapped.
A screenshot of how this looks like:
So when the user drags one photo I want it to reduce its opacity and give the user a draggable image on his finger which disappears again if he drops it outside of any image.
The way I have developed this is the following. I have set a UIPanGesture for these UIImageViews as:
for (UIImageView *imgView in editPhotosView.subviews) {
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(photoDragged:)];
[imgView addGestureRecognizer:panGesture];
imgView.userInteractionEnabled = YES;
[panGesture release];
}
Then my photoDragged: method:
- (void)photoDragged:(UIPanGestureRecognizer *)gesture {
UIView *view = gesture.view;
if ([view isKindOfClass:[UIImageView class]]) {
UIImageView *imgView = (UIImageView *)view;
if (gesture.state == UIGestureRecognizerStateBegan) {
imgView.alpha = 0.5;
UIImageView *newView = [[UIImageView alloc] initWithFrame:imgView.frame];
newView.image = imgView.image;
newView.tag = imgView.tag;
newView.backgroundColor = imgView.backgroundColor;
newView.gestureRecognizers = imgView.gestureRecognizers;
[editPhotosView addSubview:newView];
[newView release];
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [gesture translationInView:view.superview];
[view setCenter:CGPointMake(view.center.x + translation.x, view.center.y + translation.y)];
[gesture setTranslation:CGPointZero inView:view.superview];
}
else { ... }
}
}
Thus as you see I add a new UIImageView with 0.5 opacity on the same spot as the original image when the user starts dragging it. So the user is dragging the original image around. But what I want to do is to copy the original image when the user drags it and create a "draggable" image and pass that to the user to drag around.
But to do that I have to pass the user touch on to the newly created "draggable" UIImageView. While it's actually set to the original image (the one the user touches when he starts dragging).
So my question is: How do I pass the user's touch to another element?.
I hope that makes sense. Thanks for your help!
Well, you can pass the UIPanGestureRecognizer object to another object by creating a method in your other object which takes the gesture recognizer as a parameter.
- (void)myMethod:(UIPanGestureRecognizer *)gesture
{
// Do stuff
}
And call from your current gesture recognizer using....
[myOtherObject myMethod:gesture];
Not entirely sure I'm understanding your question here fully. :-/
Maybe:
[otherObject sendActionsForControlEvents:UIControlEventTouchUpInside];
Or any other UIControlEvent
In the end I decided to indeed drag the original image and leave a copy at the original place.
I solved the issue with the gesture recognizers I was having by re-creating them and assigning them to the "copy", just like PragmaOnce suggested.

detect long press on UINavigationItem's back button

I want to add functionality to my back buttons through my UINavigationController-based app where long-pressing the back button will pop to root. However, I can't figure out where to attach the gesture recognizer. Do I subclass UINavigationBar and try and detect if the long press is in the left button region?
I've heard of people adding similar functionality before. Anyone have any ideas?
I know this question is old, but I came up with a solution. Instead of trying to add the gesture recognizer to the button itself (which would be ideal), I added it to the self.navigationController.navigationBar and then in the action method, use the locationInView to see if I'm over the back button. I wasn't entirely sure about how to identify the back button precisely, so I'm clumsily just grabbing the the first subview with an x coordinate less than some arbitrary value, but it seems promising. If someone has a better way to identify the frame of the back button, let me know.
- (void)longPress:(UILongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
// set a default rectangle in case we don't find the back button for some reason
CGRect rect = CGRectMake(0, 0, 100, 40);
// iterate through the subviews looking for something that looks like it might be the right location to be the back button
for (UIView *subview in self.navigationController.navigationBar.subviews)
{
if (subview.frame.origin.x < 30)
{
rect = subview.frame;
break;
}
}
// ok, let's get the point of the long press
CGPoint longPressPoint = [sender locationInView:self.navigationController.navigationBar];
// if the long press point in the rectangle then do whatever
if (CGRectContainsPoint(rect, longPressPoint))
[self doWhatever];
}
}
- (void)addLongPressGesture
{
if (NSClassFromString(#"UILongPressGestureRecognizer"))
{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[self.navigationController.navigationBar addGestureRecognizer:longPress];
[longPress release];
}
}
I believe UIGestureRecognizers can only be added to UIViews and subclasses of UIViews.
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html
The back button is a UIBarButtonItem that descends from NSObject. Therefore, you won't be able to attach a gesture recognizer to a standard back button using
UILongPressGestureRecognizer *longPressGesture =
[[[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(longPress:)] autorelease];
[self.navigationItem.backBarButtonItem addGestureRecognizer:longPressGesture];
You can however add a custom view to a UIBarButtonItem. A custom view could just as easily be a UIView, UIButton, UILabel, etc.
Example:
UIView *myTransparentGestureView = [[UIView alloc] initWithFrame:CGRectMake(0,0,40,30)];
[myTransparentGestureView addGestureRecognizer:longPressGesture];
[self.navigationItem.backBarButtonItem setCustomView:myTransparentGestureView];
// Or you could set it like this
// self.navigationItem.backBarButtonItem.customView = myTransparentGestureView;
[myTransparentGestureView release];
You have to be careful however, since setting properties on backBarButtonItem applies to the next view that you push. So if you have view A that pushes to view B and you want the gesture to be recognized when you tap back in view B. You must set it up in view A.
I followed a slightly different path, figured I'd share it. The above answers are fine, but really, if the long press is in the leading 1/3 of the nav bar, that's good enough for me:
- (void)longPress:(UILongPressGestureRecognizer *)gr
{
NSLog(#"longPress:");
UINavigationBar *navBar = [self navigationBar];
CGFloat height = navBar.bounds.size.height;
CGPoint pt = [gr locationOfTouch:0 inView:navBar];
//NSLog(#"PT=%# height=%f", NSStringFromCGPoint(pt), height);
if(CGRectContainsPoint(CGRectMake(0,0,100,height), pt)) {
[self popToViewController:self.viewControllers[0] animated:YES];
}
}
Here's my solution:
In appDelegate (the "owner" of the nav bar in my app), In applicationDidFinishLaunchingWithOptions:
Get the nav bar view and add the gesture recognizer to the whole view:
// Get the nav bar view
UINavigationBar *myNavBar = nil;
for (UIView *view in [self.window.rootViewController.view subviews]) {
if ([view isKindOfClass:[UINavigationBar class]]) {
NSLog(#"Found Nav Bar!!!");
myNavBar = (UINavigationBar *)view;
}
}
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(backButtonLongPress:)];
[myNavBar addGestureRecognizer:longPress];
NSLog(#"Gesture Recognizer Added.");
Then in appDelegate, in -(void) backButtonLongPress:(id) sender
Check to see if the gesture occurs within the frame of the back button:
if ([sender state] == UIGestureRecognizerStateBegan) {
// Get the nav bar view
UINavigationBar *myNavBar = nil;
for (UIView *view in [self.window.rootViewController.view subviews]) {
if ([view isKindOfClass:[UINavigationBar class]]) {
NSLog(#"Found Nav Bar!!!");
myNavBar = (UINavigationBar *)view;
}
}
// Get the back button view
UIView *backButtonView = nil;
for (UIView *view in [myNavBar subviews]) {
if ([[[view class] description] isEqualToString:#"UINavigationItemButtonView"]) {
backButtonView = view;
NSLog(#"Found It: %#", backButtonView);
NSLog(#"Back Button View Frame: %f, %f; %f, %f", backButtonView.frame.origin.x, backButtonView.frame.origin.y, backButtonView.frame.size.width, backButtonView.frame.size.height);
}
}
CGPoint longPressPoint = [sender locationInView:myNavBar];
NSLog(#"Touch is in back button: %#", CGRectContainsPoint(backButtonView.frame, longPressPoint) ? #"YES" : #"NO");
if (CGRectContainsPoint(backButtonView.frame, longPressPoint)) {
// Place your action here
}
// Do nothing if outside the back button frame
}