Click Event on UIImageView programmatically in ios - iphone

I am displaying a image from code here is the code
UIImageView *preArrowImage =[[UIImageView alloc]init ];
preArrowImage.image =[UIImage imageNamed:#"arrowprev.png"];
preArrowImage.frame = CGRectMake(20, 60, 10, 30);
[self.view addSubview:preArrowImage];
I want to handle the touch event on the preArrowImage programmatically.

SWIFT 5
let preArrowImage : UIImageView // also give it frame
let singleTap = UITapGestureRecognizer(target: self, action: #selector(tapDetected))
preArrowImage.isUserInteractionEnabled = true
preArrowImage.addGestureRecognizer(singleTap)
//Action
func tapDetected() {
print("Imageview Clicked")
}
Objective-c
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapDetected)];
singleTap.numberOfTapsRequired = 1;
[preArrowImage setUserInteractionEnabled:YES];
[preArrowImage addGestureRecognizer:singleTap];
-(void)tapDetected{
NSLog(#"single Tap on imageview");
}

Simply add a UITapGesture on the image but remember to make its UserInteraction Enabled.
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(singleTapGestureCaptured:)];
[preArrowImage addGestureRecognizer:singleTap];
[preArrowImage setMultipleTouchEnabled:YES];
[preArrowImage setUserInteractionEnabled:YES];

Now in Swift!
let singleTap = UITapGestureRecognizer(target: self, action: Selector("tapDetected"))
singleTap.numberOfTapsRequired = 1
preArrowImage.userInteractionEnabled = true
preArrowImage.addGestureRecognizer(singleTap)
//Action
func tapDetected() {
println("Single Tap on imageview")
}

Swift 3
On UIImageView enable UserInterAction
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
if let touch = touches.first {
if touch.view == self.imgVwPostPreview { //image View property
//Do your actions
//self.delegate?.didClickOnCellImageView(indexPath: self.cellIndexPath!)
}
}
}

Using story board with view in view controller.
After selecting one of the gesture recognizer drag it to your controller.
Now it will display in "Document outline" and header of view controller xib.
Drag gesture to UILable
create IBAction method in header file
Now drag it from handler to gesture icon in xib

Xamarin.iOS
UIImageView preArrowImage; // also give it frame
var singleTap = new UITapGestureRecognizer(TapDetected);
singleTap.ShouldReceiveTouch -= TapGesture_ShouldReceiveTouch;
singleTap.ShouldReceiveTouch += TapGesture_ShouldReceiveTouch;
preArrowImage.UserInteractionEnabled = true;
preArrowImage.AddGestureRecognizer(singleTap)
bool TapGesture_ShouldReceiveTouch(UIGestureRecognizer recognizer, UITouch touch)
{
return true;
}
void TapDetected(UITapGestureRecognizer tapGestureRecognizer)
{
print("Imageview Clicked")
}

If your UIImageView is an IBOutlet which you get from a Storyboard or Nib, you could add a UIGestureRecognizer to the image view, enable user interaction and connect a IBAction to the gesture recognizer.
For more info check my answer on this question: How to you make a UIImageView on the storyboard clickable (swift)

Here's another option: Cheat.
I mean, think laterally.
Set up your UIImage how you want, cropped, aspect fit, whatever.
Overlay this with a UIView (equal dimensions, position etc.)
Set the background to clearcolour, and the class to UIControl.
Point the touch up inside event to your handler, and voila.

Related

How to add multiple imageView with different shapes?

Currently I am working for a collage app. I want to draw collage frame and after that I need to add images from Camera roll or camera . I need the following type view
I have added 5 UIImageViews. To draw in shape I have added UIBezierPath like for imageview1
UIBezierPath *path1 = [[UIBezierPath alloc] init];
[path1 moveToPoint:CGPointMake(0, 0)];
[path1 addLineToPoint:CGPointMake(150, 0)];
[path1 addLineToPoint:CGPointMake(0, 150)];
[path1 addLineToPoint:CGPointMake(0, 0)];
UIImageView *imgView1 = [[UIImageView alloc] initWithFrame:CGRectMake(140, 0, 160, 300)];
imgView1 . tag = 2;
imgView1 . userInteractionEnabled = YES;
imgView1. backgroundColor = [UIColor greenColor];
CGPathRef borderPathRef2 = [path1 CGPath];
CAShapeLayer *borderShapeLayer2 = [[CAShapeLayer alloc] init];
[borderShapeLayer2 setPath:borderPathRef2];
[[imgView1 layer] setMask:borderShapeLayer2];
imgView1.layer.masksToBounds = YES;
[borderShapeLayer2 release];
[view1 addSubview:imgView1];
Like this I have done for all 5. But after adding imageView5 touch is not detected on all other 4 views because its frame overlaping on the other four imageview.
So I am not getting how to design this . I need to add images to all imageviews by touch action.
Please help me. If someone have any idea about this then please share.
Thanks In Advance.
Following is the example 2 which is from Insta Collage app.
I have solved this problem by overriding hitTest:withEvent: method of UIView.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
//Getting View which is touched.
UIView *testView = [super hitTest:point withEvent:event];
//If this is self. and point hitted on Masked Layer(Which is hiding our UIView).
if(testView == self && testView.layer.mask != nil && CGPathContainsPoint([(CAShapedLayer*)testView.layer.mask path], NULL, point, false))
{
//Then return nil. So Touch thinks that UIView has not clicked. and touch is passed to UIView which is behind this view. And again hitTest is performed on inner UIViews.
testView = nil;
}
//Return nil. So touch thinks that UIView is not clicked.
//Return self. So touch thinks self is clicked. and no more hitTest is performed.
return testView;
}
Now i implemented a control called IQIrregularView for this problem.
Check this.
https://github.com/hackiftekhar/IQIrregularView
You should try random shaped buttons
you can find your solution in this custom class found on GitHub
It worked for me and hope will work for you also....
Happy Coding...........
I was also trying to detect a touch on a UIView within the path of a UIBezierPath, using Swift
My scenario is:
- UIView (Container)
- UIImageView (masked with Triangle UIBezierPath)
- UIImageView (masked with Triangle UIBezierPath)
In my container, I override hitTest and return the subview that was touched like so:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for subview in subviews.reversed() {
// ** convert the touch to the subview space:
let convertedPoint = subview.convert(point, from: self)
// ** if the subview layer mask exists I know that it has a UIBezierPath:
if let layerShape = subview.layer.mask as? CAShapeLayer {
if let shapePath = layerShape.path, shapePath.contains(convertedPoint) {
// ** return the subview with the path that contains the converted point
return subview
}
}
}
return nil
}
Hope that helps!

UIButton touch up inside event not working in ios5 while it's ok in ios6

I had a project which contains lots of UIButton, using xcode 4.5 and storyboard and ARC. after testing in ios6, everything goes fine. But in ios5, UIButton touch up inside event not working, the action not called. I tried to use touch down and it works. However, I have a lot of UIButton, I cannot change that one by one. what's more, the touch down event does give a good experience.
I used code below in many of my view controllers:
in viewDidLoad:
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:tap];
-(void)dismissKeyboard {
[aTextField resignFirstResponder];
}
this might be the reason. I will check it out later. But it works in ios6.
Do you know what's wrong ? Thanks very much!
I had the same problem to you.
That's because the touch event in iOS5 is prevented when the gestureRecognizer you registered captures the event.
There are two solutions.
One:
1) Add a new view inside your view. It should have the same level to the buttons. The priority of the buttons should be higher than the new view.
2) change the call of 'addGestureRecognizer' to the new view.
[self.newView addGestureRecognizer:tap];
Two:
1) Implement the code below. You should return NO when the point is in the buttons.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
CGPoint point = [gestureRecognizer locationInView:self.view];
NSLog(#"x = %.0f, y = %.0f", point.x, point.y);
if (CGRectContainsPoint(self.testBtn.layer.frame, point))
return NO;
return YES;
}
ps.
you should import QuartzCore.h to access layer's attributes.
#import <QuartzCore/QuartzCore.h>
Just use the property "cancelsTouchesInView" (NO) on UITapGestureRecognizer tap
Example:
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTap)];
gesture.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:gesture];
Override your button parent view pointinside method, setting a new custom frame size that contains your button.

UIGestureRecognizer for part of a UIView

I'm using UIGestureRecognizer in my iOS application and I'm having some issues.
I only want the gestures to work in a certain area of the view, so I made a new UIView with a specific frame and added it to the root view. The gestures are working fine with this, but the only issue now is that I can't click the stuff that is under/behind that new view (the objects that are on the root view). If I set userInteractionEnabled to NO, it breaks the gestures so that is not an option.
What can I do to fix that?
Thanks.
Don't create a new view for your gesture recognizer. The recognizer implements a locationInView: method. Set it up for the view that contains the sensitive region. On the handleGesture, hit-test the region you care about like this:
0) Do all this on the view that contains the region you care about. Don't add a special view just for the gesture recognizer.
1) Setup mySensitiveRect
#property (assign, nonatomic) CGRect mySensitiveRect;
#synthesize mySensitiveRect=_mySensitiveRect;
self.mySensitiveRect = CGRectMake(0.0, 240.0, 320.0, 240.0);
2) Create your gestureRecognizer:
gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
[self.view addGestureRecognizer:gr];
// if not using ARC, you should [gr release];
// mySensitiveRect coords are in the coordinate system of self.view
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.view];
if (CGRectContainsPoint(mySensitiveRect, p)) {
NSLog(#"got a tap in the region i care about");
} else {
NSLog(#"got a tap, but not where i need it");
}
}
The sensitive rect should be initialized in myView's coordinate system, the same view to which you attach the recognizer.
Yo can also do:
gestureRecognizer.delegate = self
somewhere. generally on viewDidLoad(). then you implement the method:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
let view = self.getTheViewDontWannaConsider() /* or whateva */
let point = touch.location(in:view)
if point.y >= 50 /* or whateva calc. you want */ {
return false
}
return true
}

How to cancel UIGestureRecognizer if subview's button pressed

I am struggling to get the behaviour I would like from the gesture recognisers, specifically cancelling certain gestures if others have fired.
I have a scrollView set to paging and multiple subviews in each page. I have added a touch gesture recogniser to scroll to the next or prev page if the user taps to the right or left of the page.
// Add a gesture recogniser turn pages on a single tap at the edge of a page
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGestureHandler:)];
tapGesture.cancelsTouchesInView = NO;
[self addGestureRecognizer:tapGesture];
[tapGesture release];
and my gesture handler:
- (void) tapGestureHandler:(UIGestureRecognizer *) gestureRecognizer {
const CGFloat kTapMargin = 180;
// Get the position of the point tapped in the window co-ordinate system
CGPoint tapPoint = [gestureRecognizer locationInView:nil];
// If the tap point is to the left of the page then go back a page
if (tapPoint.x > (self.frame.size.width - kTapMargin)) [self scrollRectToVisible:pageViewRightFrame animated:YES];
// If the tap point is to the right of the page then go forward a page
else if (tapPoint.x < kTapMargin) [self scrollRectToVisible:pageViewLeftFrame animated:YES];
}
All works well, except where I have a subview on the page that has buttons in it. I want to be able to ignore the tap to turn the page if the user touches a button on the subView and I can't figure out how to do this.
Cheers
Dave
The solution that worked the best for me in the end was to use the hitTest to determine if there were any buttons underneath the location of the tap gesture. If there are then just ignore the rest of the gesture code.
Seems to work well. Would like to know if there are any gotchas with what I have done.
- (void) tapGestureHandler:(UIGestureRecognizer *) gestureRecognizer {
const CGFloat kTapMargin = 180;
// Get the position of the point tapped in the window co-ordinate system
CGPoint tapPoint = [gestureRecognizer locationInView:nil];
// If there are no buttons beneath this tap then move to the next page if near the page edge
UIView *viewAtBottomOfHeirachy = [self.window hitTest:tapPoint withEvent:nil];
if (![viewAtBottomOfHeirachy isKindOfClass:[UIButton class]]) {
// If the tap point is to the left of the page then go back a page
if (tapPoint.x > (self.bounds.size.width - kTapMargin)) [self scrollRectToVisible:pageViewRightFrame animated:YES];
// If the tap point is to the right of the page then go forward a page
else if (tapPoint.x < kTapMargin) [self scrollRectToVisible:pageViewLeftFrame animated:YES];
}
}
Apple documentation shows the answer:
- (void)viewDidLoad {
[super viewDidLoad];
// Add the delegate to the tap gesture recognizer
self.tapGestureRecognizer.delegate = self;
}
// Implement the UIGestureRecognizerDelegate method
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch: (UITouch *)touch {
// Determine if the touch is inside the custom subview
if ([touch view] == self.customSubview){
// If it is, prevent all of the delegate's gesture recognizers
// from receiving the touch
return NO;
}
return YES;
}
Of course in this case customSubview would be subview on the page that has buttons in it (or even the buttons on it)
Swift 3
Set UITapGestureRecognizer
let tap = UITapGestureRecognizer(target: self, action: #selector(Class.didTap))
tap.delegate = self
UITapGestureRecognizer delegate method:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return touch.view != buttonThatShouldCancelTapGesture
}
You can subclass UIView and overwrite the -touchesBegan selector, or you can play with the opaque property of the subviews to make them "invisible" to the touches (if view.opaque = NO, the view ignores touch events).

UIBarButtonItem has a too large click area

I know some have already asked the question but so far, the answers are not really specific.
Apparently apple have made the click area of the NavigationBar larger than it really is, but I don't think it's supposed to be that large.
In my app, there is a TableView right underneath the NavBar and you can click all the way down to half of the first cell to trigger the event of the rightBarButtonItem.
the button is instanced like this:
UIBarButtonItem *editButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:#selector(editTable)];
[[self navigationItem] setRightBarButtonItem:editButton];
"self" is the root ViewController of a NavigationController.
As you could imagine, it's a problem since the cells are selectable to push another ViewController.
I managed to go around the problem by making the cells' height bigger but I'd rather have them at the regular size.
I'm sure I'm not the only one with this case of scenario.
Thanks in advance.
UIButton *menuButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[menuButton addTarget:self action:#selector(revealMenu:) forControlEvents:UIControlEventTouchUpInside];
[menuButton setImage:[UIImage imageNamed:#"menuIcon"] forState:UIControlStateNormal];
UIView *menuButtonContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[menuButtonContainer addSubview:menuButton];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:menuButtonContainer];
This perfectly worked for me....
You can't do much about the touch area. It's all based on how much finger area is picked up by the display. You can calculate size of touch area (warning: undocumented api) but intercepting touch events and calling a method to correct for fat finger syndrome seems a bit over-the-top.
Basic process is:
You touch screen
Change of capacitance is measured in a unique, individualized portion of the screen and sent to hardware/software to interpret (just as an aside, Apple's patent states as many as 15 touches can be tracked at once.. 10 fingers, two palms, and 3 others in case you ever make an iPhone "Twister" game)
Software interprets gradients in capacitance on screen and identifies touch boundaries
Software calculates centroid of touch based on the average capacitance magnitude for each pixel or point in the region
Software sends coordinates of touch boundary locations into application
Your app interprets as a touch event, it gets sent along the responder chain, and (hopefully) the user see's the touch on the screen
The other answer here from iosDeveloper about putting a container view around the button can help reduce the size of the clickable area on the left and right of the button, but doesn't help with the issue I had where you would activate the button clicking below the navigation bar.
After hours of searching several different SO threads and trying various things, I finally found my answer to this issue here: https://stackoverflow.com/a/10597699/1289240
Since i use a transparent toolbar and tint the UIBarButtonItem, I couldn't use a custom view as I would need to set tint, width/height, rounded corners to make it similar to UIBarButtonItem look
I therefore use a invisible button which I add to my items to resolve this issue. and events are trappped there
in #interface UIBarButtonItem (ImageButton)
+ (UIBarButtonItem*) invisibleButtonForTransparentBar {
static UIBarButtonItem* fakeBtn = nil;
if (!fakeBtn) {
fakeBtn = [[UIBarButtonItem alloc] initWithCustomView:[[UIView alloc] init]];
}
return fakeBtn;
}
in View Controller:
[self.viewToolbar setItems: #[flexSpace,
[UIBarButtonItem fakeLastButtonForTransparentBar],
self.btnTrackUser,
self.btnMapConfig,
[UIBarButtonItem fakeLastButtonForTransparentBar],
flexSpace] ];
We are unable to reduce the default uibarbuttonitem size. The idea is to make a uibutton, add to a uiview , then to uibarbuttonitem's custom view.
The size of the uiview containing uibutton will be the click area of the uibarbutton.
Here is my code:
self.navigationController?.navigationBarHidden = false
let navigationBar = UINavigationBar(frame: CGRectMake(0, 0, self.view.frame.size.width, 64)) // Offset by 20 pixels vertically to take the status bar into account
navigationBar.backgroundColor = UIColor.blueColor()
navigationBar.delegate = self;
// Create a navigation item with a title
let navigationItem = UINavigationItem()
//menu button
let menubutton: UIButton = UIButton(frame: CGRectMake(0, 0, 30, 30))
menubutton.setImage(UIImage(named: "menu"), forState: UIControlState.Normal)
menubutton.addTarget(self, action: "btn_clicked", forControlEvents: UIControlEvents.TouchUpInside)
//menu button custom view
let leftView = UIView(frame: CGRectMake(0,0,30,30))
leftView.addSubview(menubutton)
//left uibarbutton
let leftItem:UIBarButtonItem = UIBarButtonItem(customView: leftView)
navigationItem.leftBarButtonItem = leftItem
//searchButton
let searchbutton: UIButton = UIButton()
searchbutton.setImage(UIImage(named: "search1x"), forState: UIControlState.Normal)
searchbutton.frame = CGRectMake(0, 0, 30, 30)
searchbutton.addTarget(self, action: "btn_clicked", forControlEvents: UIControlEvents.TouchUpInside)
//menu button custom view
let rightView = UIView(frame: CGRectMake(0,0,30,30))
rightView.addSubview(searchbutton)
//right uibarbutton
let rightItem:UIBarButtonItem = UIBarButtonItem(customView: rightView)
navigationItem.rightBarButtonItem = rightItem
// Assign the navigation item to the navigation bar
navigationBar.items = [navigationItem]
// Make the navigation bar a subview of the current view controller
self.view.addSubview(navigationBar)
Note:
Ex:- menu button is a UIButton and added to leftView. The leftItem is a UIBarButtonItem and is added to leftBarButtonItem of the navigationBar. The leftView is added as the custom view of leftItem. So, the click area of leftBarButtonItem of the navigationBar is the frame of leftView. i.e., frame: CGRectMake(0,0,30,30).
All I can offer is to create UIBarButtonItem with custom view
let view = CustomView()
let itemButton = UIBarButtonItem(customView: view)
Then override this view's touchesEnded
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
if touches.count == 1,
let touch = touches.first {
let touchPoint = touch.location(in: self)
if bounds.contains(touchPoint) {
someAction()
}
}
}
Don't forget to make it user interaction enabled somewhere in the init isUserInteractionEnabled = true.