iOS: setting Exclusive Touch to all buttons in a view - ios

My app has many buttons in a Window and I want to set Exclusive Touch all of them together. Do you have any suggestion about this? Thanks

There is a way to set exclusive touch to all buttons in your app, may be helpful.
#import </usr/include/objc/objc-class.h>
static IMP gOringinalWillMoveToSuperview = nil;
static id newMoveToSuperviewPlusSettingExclusiveTouch(id self,SEL selector,...)
{
va_list arg_list;
va_start( arg_list,selector);
gOringinalWillMoveToSuperview(self,selector,arg_list);
[self setExclusiveTouch:YES];
return nil;
}
-(void)addSettingExclusiveTouchToAllUIViewMethodWillMoveToSuperview
{
gOringinalWillMoveToSuperview = class_getMethodImplementation([UIButton class], #selector(willMoveToSuperview:));
class_replaceMethod([UIButton class], #selector(willMoveToSuperview:), &newMoveToSuperviewPlusSettingExclusiveTouch, "v#:");
}
if you don't understand this, you can refer to this and this.

Are you just looking for an easy way to set them all at once?
If you have all the buttons in an array (e.g. they're all connected to the same IBOutletCollection) you can use key value coding to set the exclusiveTouch property of the array:
[buttonArray setValue:[NSNumber numberWithBool:YES] forKey:#"exclusiveTouch"];
NSArray will then invoke the same method on every item in the array.

-(void)setExclusiveTouchForButtons:(UIView *)myView
{
for (UIView * v in [myView subviews]) {
if([v isKindOfClass:[UIButton class]])
[((UIButton *)v) setExclusiveTouch:YES];
else if ([v isKindOfClass:[UIView class]]){
[self setExclusiveTouchForButtons:v];
}
}
}
then call this function at viewDidAppear

If these buttons are all in the same view, you can loop through the view's subviews, test for whether the particular subview is a button (or test for a tag if you have one set) and set exclusiveTouch on each.

I just found an answer for this:
#pragma mark Set Buttons Exclusive Touch Yes
-(void)setExclusiveTouchForButtons:(UIView *)myView
{
for (UIView * button in [myView subviews]) {
if([button isKindOfClass:[UIButton class]])
[((UIButton *)button) setExclusiveTouch:YES];
}
}
Source

If you want to set exclusiveTouch for ALL UIButtons in your whole application method swizzling will be the perfect solution for you.
This answer explains the way very well : https://stackoverflow.com/a/24534814/976246 , and it works perfectly for me.
Also go through this article to know how this (http://nshipster.com/method-swizzling/) tecknique can be used for various purposes.

If you are adding buttons pragmatically, then send a message to the button [button setExclusiveTouch:YES]; for each buttons before adding to its super view. Else if you are using xib, you have to send the same message to the button in viewDidLoad or in loadView.

Here is some code in swift that will set exclusive touch to all buttons in your viewcontroller's view
for button in self.view.subviews {
if(button.isKindOfClass(UIButton)){
(button as! UIButton).exclusiveTouch = true
}
}

Even though the question was asked many years ago, here is a simple way to set isExclusiveTouch for all subviews that are buttons. This example shows code for subviews of self.view. Change self.view to your view of interest.
Seems like cleaner code for anyone with questions on this in the present.
for subview in self.view.subviews
{
if subview is UIButton {
subview.isExclusiveTouch = true
}
}

Related

UIButton not working in UITableview

Have UITableviewcell --> inside which i have 4 different UIView and 3 of the views has UIButtons. I want to make UIButton clickable. For the first time the buttons are clickable but when i go to next screen and come back, the buttons don't work. Please let me know how to implement this? Thanks!
PHMyTripView *tripsView=[[PHMyTripView alloc] initWithFrame:CGRectMake(10, 5, self.frame.size.width-20, cell.frame.size.height)];
tripsView.onBaggageClick = ^{
[weakSelf handleBaggagePurchaseTapped];
};
if ([data count]>0) {
[tripsView fillTripData:[data firstObject]];
[tripsView showAncillaries:self.predictiveManager.upcomingCDAirBookingInfo];
}
[cell bringSubviewToFront:tripsView.bagsButton];
[cell.viewPlaceHolder addSubview:tripsView];
This happens because the cells are reusable and if you go to another screen the button may kinda mess round into different cell in UI or functionally.
The way to solve this is to add tag. You can define cell.tag = indexPath.row * 10 or so.
Then when draw the button, you check the condition, if cell.tag == ?, then add your button.
Thank you everyone for your response but they din't work in my case.
Here is the answer. Since the UITableview was reloading when it comes back to the screen.The frames were mis placed and Hence the button was not able to click.
Used this one line code which worked fine.
self.tripsView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
You should turn off delaysContentTouches on each UITableView subview, which is an instance of UIScrollView class.
Subclass UITableView and override initWithFrame method:
.h file
#interface NoDelaysOnTouchTableView : UITableView
#end
And .m file
#import "NoDelaysOnTouchTableView.h"
#implementation NoDelaysOnTouchTableView
- (id) initWithFrame: (CGRect) frame
{
self = [super initWithFrame: frame];
if (self) {
for (id view in self.subviews) {
if ([NSStringFromClass([view class]) isEqualToString: #"UITableViewWrapperView"]) {
if ([view isKindOfClass: [UIScrollView class]]) {
// turn OFF delaysContentTouches in the hidden subview
UIScrollView* scroll = (UIScrollView*)view;
scroll.delaysContentTouches = NO;
}
break;
}
}
}
return self;
}
#end
Next - use this subclass (create an instance of NoDelaysOnTouchTableView) to be able to press UIButton immediately.
I think that the causes would be multiples.
You can try:
check the constraints for each button in your cell
in cellForRow you can do addTarget: for each button with relative #selector and in this method check the object (datasource) associated to button.
do not use addSubView in your cell, instead use a Xib (or define your cell in a storyboard within tableView) so you can set the constraints.
I Hope, i've helped you.
Not specifically about this issue, but for some people it may be the case.Try to add subviews in contentView of the cell.
Instead of addSubview(textField) use contentView.addSubview(textField)

UIButton doesn't work in UIScrollview

I have UIScrollview and view. In view I have UIButton which doesn't work, if I have UIButton in UIScrollview works. I have User Interaction Enabled on both.
for button I have this method:
- (IBAction)search
{
NSLog(#"SEARCH");
}
Whatever I give to the view doesn't work.
I solve the problem. View was not properly configured (Constraints in autolayout)
Try this
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
return ![view isKindOfClass:[UIButton class]];
}
hope it helps.
When an UIButton doesn't send the message to the IBActionreceiver is because the button view is not contained in the superview.frame
Try it setting a backgroundColor to the superview
Take a look there: UIButton selector doesn't work in nested UIViews

Scroll to top with status bar tap

I've looked through various SO questions on the topic and I have not found a solution. I have a UIViewController with a UITableView and a UICollectionView. I want the UICollectionView to scroll to the top, when the user taps it.
The documents say if you have more than one UiScrollView subclass - you need to set them to no and the UiScrollView you want to scroll to the top, to yes.
So I wrote this bit of code to go through all my views:
for (UIScrollView *view in self.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
view.scrollsToTop = NO;
}
}
self.collectionView.scrollsToTop = YES;
This way I am sure any subclass of UiScrollView has it's scrollsToTop property set to no.
However tapping on the status bar does not do anything.
Can someone tell me what I am missing here?
Thank you
It seems that you are only iterating through the subviews of your main view. Your UITableView may be nested inside another view. Try doing the following;
//in view did load
[self setScrollToTopFalse:self.view];
self.collectionView.scrollsToTop = YES;
-(void)setScrollToTopFalse:(UIView *)v
{
for (UIView * v1 in [v subviews]) {
if ([[v1 class]isSubclassOfClass:[UIScrollView class]]) {
((UIScrollView *)v1).scrollsToTop = NO;
}
[self setScrollToTopFalse:v1];
}
}

Looping Through UIScrollView and Setting UImageView

I am trying to implement lazy loading of images in a UIScrollView but I am having some problems trying to set the images.
The user scrolls and I use the following to detect what the next view tag should be. I then want to be able to loop through the corresponding UIView and set its UIImageView image.
The following is not working and I was hoping someone could help me to correct it.
for (UIView *subview in self._scrolViewForEffects.subviews){
for (UIImageView *view in subview.subviews){
if([view isKindOfClass:[UIImageView class]]){
if(view.tag == currentPage){
if(view.image == nil){
[view setImage:[UIImage imageNamed:#"Theme1.png"]];
}
}
}
}
}
It is not enough information to say what problem you can have but I suggest you to execute in debugger window a command
po [self._scrolViewForEffects recursiveDescription]
to check your view hierarchy at runtime.
Turns out that it wasn't to do with the code above. It turns out that I wasn't setting the tag on the UIImageView when I was creating it.
Thanks for the help!

UIButton not working correctly in iOS 7

I have a problem with a UIButton that works perfectly well in iOS6, but fails to respond to touch events in iOS7 up to a certain point.
To clarify please see below image:
The button that fails is the "Discard All" button that is in the UIView. (Please note, this button is only disabled temporarily and that is NOT the issue. I just don't have a screenshot of the newest test where the button is enabled")
This button ignores all touches, unless one first presses the "Discard" or "Retry" buttons in the UITableViewCell. (This does cause a reload of the view controller, which triggers the lifecycle methods like ViewDidLoad to be called again.)
After either the "Discard" or "Retry" buttons in the table view cell have been pressed, the "Discard All" button starts functioning correctly.
The view and the "Discard All" button are build on the Controller's XIB file and not in code. This only fails on iOS7, and starts working as soon as the taleview cell buttons are touched.
Anyone have any ideas?
Thanks!
I found the solution last night.
Okay, so what happens is that I put the above table view and UIView elements onto a target frame.
I'm not 100% sure, but it seems that in iOS6 the buttons respond to events irrespective of where they are placed.
For some reason in iOS7 when the button sits outside of the frame it is supposed to be in, it ignores touch events, even though it does get displayed.
I solved the problem by positioning the view's frame in the correct place so it overlays the button.
If I can find any documentation around this, I will post here.
Thanks!
I have just faced to this problem. According to #Guntis Treulands advice, I decided to check what happens if I override hitTest:withEvent: method in my custom header view. This method ignores view objects that are hidden, that have disabled user interactions, or have an alpha level less than 0.01. This method does not take the view’s content into account when determining a hit. Thus, a view can still be returned even if the specified point is in a transparent portion of that view’s content and now, after it has been overridden, receives touches outside the bounds. It did the trick for me. Hope it helps you, guys.
swift
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard !isHidden, alpha > 0 else {
return super.hitTest(point, with: event)
}
for subview in subviews {
let subpoint = subview.convert(point, from: self)
let result = subview.hitTest(subpoint, with: event)
if result != nil {
return result
}
}
return super.hitTest(point, with: event)
}
Objective-C
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (!self.clipsToBounds && !self.hidden && self.alpha > 0) {
for (UIView *subview in self.subviews.reverseObjectEnumerator) {
CGPoint subPoint = [subview convertPoint:point fromView:self];
UIView *result = [subview hitTest:subPoint withEvent:event];
if (result != nil) {
return result;
}
}
}
// use this to pass the 'touch' onward in case no subviews trigger the touch
return [super hitTest:point withEvent:event];
}
Just as an alternative answer - Actually for me, this has never worked (if uibutton is outside it's parent view, then it will not receive touch.) - But in some cases, it is required to have the button outside it's boundaries.
For that reason, there is a hittest function, which can be overridden - to simply check parent view all subviews to find uibutton that is placed outside parent views boundaries. (You would then check each subview, if it is uibutton type, and if touched coordinates are inside uibuttons frame.
By default hittest function skips checking views, that are outside boundaries.
Hittest example: https://stackoverflow.com/questions/17246488/best-way-to-perform-the-hit-test-objective-c .
Not sure if this is the same case, but it might be related:
When the UIButton is in a view with a UIGestureRecognizer which did NOT set delaysTouchesEnded to NO (default is YES), the button won't get TouchUpInside events anymore in iOS 7. It DID get the TouchUpInside events in iOS 6.
If possible, set the delaysTouchesEnded property of the UIGestureRecognizer to NO to solve the problem.
This is weird but I was developing a new app for iOS 7 and setting [cell.contentView setUserInteractionEnabled: YES]; actually worked for me.
I had this same issue - buttons did work on iOS6 and didn't on iOS7, problem was with Container UIView to which one I was adding UIButtons, I add constrains to container UIView and it started working. Code that I had to add in my case:
[superView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V: [_controllersView]-|" options:0 metrics:nil views:views]];
[superView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[_controllersView(70)]" options:0 metrics:nil views:views]];
[superView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_controllersView]|" options:0 metrics:nil views:views]];
I also got same problem but after adding below code button actions working properly cell.contentView.userInteractionEnabled = NO;
If it's using auto layout, then try adding a log statement in -viewDidAppear:
NSLog(#"Height of UIView: %f", self.<name of UIView>.frame.size.height);
if the result is 0 (or not large enough), then set the height of UIView large than the height of 'Discard All' button.
Check that the button in inside parent frame
Okay, so seems like buttons programatically created in iOS7 won't call their target.
I don't know why, but I've found the following solution:
In the InterFace Builder (or nib) add a button and customize and HIDE it. ( theButtonInTheNib);
Make it IBOutlet property: #property (strong) IBOutlet UIButton *theButtonInTheNib
Make the target IBAction connection: (IBAction)theTargetAction:(id)sender;
Here comes the trick. We will make a copy/or copies of this button:
NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:theButtonInTheNib];
UIButton *theCopyButton = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData];
[theCopyButton setHidden:NO];
[theCopyButton setFrame:CGRectMake(x, y, width, height)];
[theCopyButton addTarget:self action:#selector(theTargetAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:theCopyButton];
I have added again the target action, but any other action will work.

Resources