ios How to know which UIView when I click from xib? - ios

Because some reasons, I create a UIView xib file to reuse.
How to know which UIView when I click from xib?
I create a xib file extends UIView(named with XibView).
Then I drag two UIView(leftView,rightView) in the storyboard, and set the custom class "XibView" in XCode inspector window.
When I compile the code, It will get correct result that show two UIViews.
XibView.m file part code below:
-(id) initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if( self )
{
UIView *containerView = [[[UINib nibWithNibName:#"XibView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0];
CGRect newFrame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
containerView.frame = newFrame;
[self addSubview:containerView];
}
return self;
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"tap in xib");
......
}
But How can I know which UIView is my click?
// I add some detail description.
In the xib custom class, I will using post the notify to the uiviewcontroller and take some data when user click in xib(touchesBegan is in the xib custom class).
I have two uiview in the storyboard , these uiview will using the xib file.
so I want to know when I click which uiview, I can know which one user click.
// ------------- answer . please refer #Boyi Li answer. ---------
In the XibView.m had add the
[super touchesBegan:touches withEvent:event];
to override the touches begain method.
and add the XibView header file in the viewcontroller.
It can drag the refer to the viewcontroller.

As your said, you could assign different tag for these two views. Inside XibView, you could check self.tag == <tag>.
If you want to get specific view in parent view or controller, use viewWithTag: method. It will check hierarchy whose tag property matches the value in the tag parameter.
Update:
Create IBOutlet Reference in ViewController for left and right Views. In your controller, you have:
#property (nonatomic, weak) IBOutlet XibView *leftView;
#property (nonatomic, weak) IBOutlet XibView *rightView;`
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint leftTouchPoint = [touch locationInView:leftView];
CGPoint rightTouchPoint = [touch locationInView:rightView];
if ([self.leftView pointInside:leftTouchPoint withEvent:event]) {
// Do something for leftView
} else if ([self.rightView pointInside:rightTouchPoint withEvent:event]) {
// Do something for Right View
}
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
if ([touch.view isKindOfClass: UIView.class])
{
UIView *view=touch.view;
if (view==YourView1)
{
//start editing
}else
if (view==YourView2)
{
//start editing
}
}
else
{
}
}

Related

Subclass of UIButton connecting to IBAction of parent View Controller using Interface Builder?

Disclaimer: I'm new to Interface Builder, so it's possible I'm using it entirely incorrectly.
I've created a NavigationViewController, attached two ViewControllers to it my Storyboard, and added (dragged) a UIButton onto the first VC.
I then created a navigation segue on the UIButton that navigates between the two VCs. This works as expected.
Where things fall apart, is when I try to use a custom subclass of UIButton that I created: the segue is never fired.
In my UIButton's property inspector, I set the Custom Class to my custom class name.
I added a couple of user defined runtime attributes to the UIButton.
The UIButton header looks like this:
#import <UIKit/UIKit.h>
#interface RSButton : UIButton
#property (nonatomic) CGFloat touchAlpha;
#end
And the UIButton implementation looks like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.adjustsImageWhenHighlighted = NO;
self.highlighted = YES;
[UIView transitionWithView:self
duration:0.04
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
if (self.touchAlpha && self.touchAlpha < 1.0)
{
self.alpha = self.touchAlpha;
}
} completion:nil];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
self.highlighted = NO;
[self resetToDefaultState];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
self.highlighted = NO;
[self resetToDefaultState];
}
- (void)resetToDefaultState
{
[UIView transitionWithView:self
duration:0.12
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
if (self.alpha < 1)
{
self.alpha = 1.0;
}
} completion:nil];
}
Interestingly enough, I can see what the problem is: I've overridden the touchesBegan/touchesEnded events, and now the segue stuff isn't firing.
What I can't figure out is how to trigger the attached segue action programmatically?
performSegueWithIdentifier isn't available to run on [self], and
[self performSelector:#selector(performSegueWithIdentifier:#"segueId" sender:self)]; would suggest I need to know what the segueId is before I fire it.
In your method, call:
[super touchesBegan:touches withEvent:event]
So:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event]
//Your code
}
or:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//Your code
[super touchesBegan:touches withEvent:event]
}
Do the same in touchEnded etc.
IMPORTANT:
Remember to call the method on the superclass using super when you override important and standard methods like those.
It's the same that you do when you call init on your subclass:
- (id)init {
self = [super init];
if(self) {
//your code
}
return self;
}

Dismiss a view when a user taps anywhere outside the view

I have a very complex app with lots of stacked views which contain lots of buttons ,drawing areas and other custom touch handling . I am displaying a draggable helper view which can be on top of all the other views . I need to dismiss this view if the user taps anywhere outside the helper view . I have tried using multiple UIWindows and adding Gesture recognizers to the UIWindow .
A easy is to add a transparent button which bounds is equal the superview's bounds. And the superview insert the transparent button below your helper view.
The transparent button add a click event which can dismiss the helper view and the transparent button it self.
For example :
UIButton *transparencyButton = [[UIButton alloc] initWithFrame:superview.bounds];
transparencyButton.backgroundColor = [UIColor clearColor];
[superview insertSubview:transparencyButton belowSubview:helperView];
[transparencyButton addTarget:self action:#selector(dismissHelper:) forControlEvents:UIControlEventTouchUpInside];
and the dismissHelper: method can do this :
- (void)dismissHelper:(UIButton *)sender
{
[helperView dismiss];
sender.hidden = YES;
// or [sender removeFromSuperview]
}
You can check the view being touched by going through the touches and looking at the .view property of the touch. It reflects the view from where the view actually originates.
Assuming that you keep a reference to your view (either via an IBOutlet or otherwise) to your view called "myView" the below works.
In your .m file. The touchesBegan: function is triggered every time the user touches a finger down. We loop through the touches to see if the originating view of the touch is NOT equal to "myView". This comparison could also be done via checking the class, tag or any other property you use to identify your view. Examples given below.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"touches began");
UITouch *touch = [touches anyObject];
if(touch.view!=myView){
myView.hidden = YES;
}
}
Or in case of using a tag:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"touches began");
UITouch *touch = [touches anyObject];
if(touch.view.tag!=23){
myView.hidden = YES;
}
}
create an hittestintercept view. this can make the view below handle events like before.
#protocol HitTestInterceptViewDelegate <NSObject>
- (BOOL)interceptHitTest:(CGPoint)point withEvent:(UIEvent *)event;
#end
#interface HitTestInterceptView : UIView
#property(nonatomic, weak) id<HitTestInterceptViewDelegate> hitTestInterceptDelegate;
#end
HtiTestInterceptView.m
#import "HitTestInterceptView.h"
#implementation HitTestInterceptView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if ([_hitTestInterceptDelegate interceptHitTest:point withEvent:event] == NO) {
return [super hitTest:point withEvent:event];
}
return nil;
}
#end
then use it in the toast view
- (void)dismissIfTouchOutsideInView:(UIView *)view {
if (_interceptView != nil) return;
_interceptView = [[HitTestInterceptView alloc] initWithFrame:view.bounds];
_interceptView.backgroundColor = [UIColor clearColor];
_interceptView.hitTestInterceptDelegate = self;
[view insertSubview:_interceptView belowSubview:self];
}
- (BOOL)interceptHitTest:(CGPoint)point withEvent:(UIEvent *)event {
dispatch_async(dispatch_get_main_queue(), ^{
// [self dismiss];
});
return YES;
}

Using touchesBegan & touchesEnded as Swipe

I'm trying to see if a user swiped all the way from one view, label, etc. to another.
Here is my relevant CodeViewController.h :
#property (strong, nonatomic) IBOutlet UITextField *textField;
#property (strong, nonatomic) IBOutlet UILabel *swipeBegin;
#property (strong, nonatomic) IBOutlet UILabel *swipeEnd;
TextField : To display if the whole thing was successful.
SwipeBegin : The Label from which the swipe begins.
SwipeEnd : The Label where the swipe is supposed to end.ViewController.m :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
if ([touch view] == swipeBegin) {
gestureStartPointTouched = YES;
}
If the swipe began at "swipeBegin" a bool value is set to YES.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
if ([touch view] == swipeEnd) {
if (gestureStartPointTouched == YES) {
[TextField setText:#"Success"];
}
}
If I start swiping at "swipeBegin" the touchesBegan method is called -> works fine
The troubling part is the touchesEnded method.
if([touch view] == swipeEnd)
is false no matter what i do about it.
Does anybody have an advice ?
Thank You !SolutionEditing the touchesEnded method :
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
if ([self.view hitTest:[[touches anyObject] locationInView:self.view] withEvent:event] == SwipeEnd) {
if (gestureStartPointTouched == YES) {
[TextField setText:#"Success"];
}
}
The view of the touch never changes, if it starts in swipeBegin that will always be it's view. If you want to find out where it ended use hitTest:withEvent: with the touch's end location, that will return the view you are looking for.

UIButton is not receiving actions behind UIScrollView

I put UIButton inside UITableViewCell in UITableView that is behind UIScrollView. I subclassed UIScrollView to forward touches to UITableView.
So method from UITableViewDelegate didSelectRow is calling properly. The problem is that UIButton inside table cell is not receiving TouchUpInside actions.
How can I solve this problem without deleting ScrollView over TableView?
EDIT:
I resolved this issue by detecting which view will receive touch. Here's the code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView *hitView = [self hitTest:[(UITouch*)[[touches allObjects] objectAtIndex:0] locationInView:self] withEvent:event];
if ([hitView isKindOfClass:[UIButton class]]) {
[(UIButton*)hitView sendActionsForControlEvents:UIControlEventTouchUpInside];
}
[super touchesBegan:touches withEvent:event];
}
If you want to enable actions for bouth objects - UIScrollView and UIButton you should to implement custom hit test mechanism for ScrollView.
In your UIScrollView subclass override - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event method to make views behind ScrollView available for getting events.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
return __touchesEnabled;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
_touchesEnabled = NO;
UIWindow *window = [UIApplication sharedApplication].delegate.window;
for(UITouch *touch in touches) {
CGPoint point = [touch locationInView:self];
point = [window convertPoint:point fromView:self];
UIView *view = [window hitTest:point withEvent:event];
[view touchesBegan:touches withEvent:event];
}
_touchesEnabled = YES;
}
It works for me
Since you have added your scroll view over the UIButton, all the touch actions will not be passed to the button.
[yourScrollView setUserInteractionEnabled:NO];
This may solve your problem.

Forwarding touch events only to the views being touched

I have a UIWindow with a text field, a button, and a table.
I would like to be able to track all the touches on the screen and then forward them to the element being touched.
I have read about overriding sendEvent in the Apple documentation but I still do not understand:
How to use hitTest to retrieve the element being touched
How to forward touches
This is what I have so far.
- (void) sendEvent:(UIEvent *)event
{
for (UITouch *touch in [event allTouches])
{
/* Get coordinates of touch */
CGPoint point = [touch locationInView:self];
/* Get subview being touched */
/* something like this???
UIView *receiver = [self hitTest:point withEvent:event];
*/
/* Forward touch event to right view */
/* how??? */
}
[super sendEvent:(UIEvent *)event];
}
Thank you.
I am not sure this is the best solution but I am following what has been posted here.
Basically I have a subclass of UIView covering the entire space. Such class contains a reference to ALL the elements that can be touched. (I wish there was a way to avoid that)
This is the code in the header
#interface SubclassUIView : UIView {
UITextField *text;
UITableView *table;
UIButton *button;
UIToolbar *toolbar;
}
And this is the implementation:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint tableHit = [table convertPoint:point fromView:self];
CGPoint buttonHit = [button convertPoint:point fromView:self];
CGPoint toolbarHit = [toolbar convertPoint:point fromView:self];
CGPoint messageHit = [text convertPoint:point fromView:self];
if ([table pointInside:tViewHit withEvent:event]) return table;
else if ([button pointInside:buttonHit withEvent:event]) return button;
else if ([toolbar pointInside:toolbarHit withEvent:event]) return toolbar;
else if ([text pointInside:messageHit withEvent:event]) return text;
return [super hitTest:point withEvent:event];
}

Resources