In My view there is reset button .i need userinteraction disabled except for that button .how can i do that can any one share the code.thanks in advance?
btn1 is your button, self.view - your view
for (UIView *view in self.view.subviews)
view.userInteractionEnabled=NO;
btn1.userInteractionEnabled=YES;
For all the elements there is a property userinteractionenabled. Set it to false
yourelement.userInteractionEnabled = NO;
Also place your UIButton on top of your view hierachy.
Other option is to place a tranparent UIButton on your entire view and your UIButton on top of this view. This way only your UIButton is touch enalbed. Other touches would be taken in by the transparent button which does nothing.
Sligthly better approach, define a "magic value" that will help you :
#define kDontDisableUserInteraction 3928473
then set this value as the tag of your button you don't want disabled:
[resetButton setTag:kDontDisableUserInteraction];
you can now create a function in your superview's class :
- (void)setInterfaceEnabled:(BOOL)newEnabled {
for (UIView *subview in self.subviews) {
if (subView.tag != kDontDisableUserInteraction)
continue;
subView.userInteractionEnabled = newEnabled;
}
}
That allows you to create other non-disableable buttons simply by giving them the right tag (which can be whatever int value you want, not only 3928473, depends on your #define).
Related
I have two views. viewController and myCustomAlert. i have added myCustomAlert as a subview in viewController.
this is the function which is called when myCustomAlert is added as a subview.
-(void)ratingAction:(id)sender
{
if ([sender isKindOfClass:[UIButton class]])
{
[[NSBundle mainBundle] loadNibNamed:#"myCustomAlert" owner:self options:nil
];
Custom_view.frame = CGRectMake(10, 100, 287, 165);
[self.view addSubview:Custom_view];
self.view.userInteractionEnabled = false;
Custom_view.userInteractionEnabled = true;
}
}
i want parent view to be disabled so i disable the userinteraction but it disable the subview too. how to do that?? i want my subview to work like an alert.
One approach you can try is set subviews frame in a way that it completely hides its super view controller i.e. 0,0,320,460 and make sure that the content on subviews are properly placed at position where you want, and then make the background color of subview transparent.
myCustomAlert.backgroundColor = [UIColor clearColor];
when you add a subview to a specific view, the subview and parent become one view collectively, i.e. the parent view. so disabling interaction will disable both.
for you is to present modelview controller with animated property as false and disable parent view interaction. tell me if you achieve your goal
I'm using an UIPageViewController in my application and I wanted to have a few UIButtons inside it, sort of like a menu. The problem I have is that when I put an UIButton (or any other interactive element) near the edges of the screen and tap it, instead of the UIButton action being applied, what happens is that the page changes (because the tap on the edge of the screen changes the page on the UIPageViewController). I'd like to know if there's a way to make it so that the UIButton has higher priority than the UIPageViewController so that when I tap the button, it applies the appropriate action instead of changing the page.
I came here with the same problem. Split’s link has the answer.
Make your root view controller the delegate of each of the UIPageViewController’s gesture recognizers, then prevent touches from being delivered if they occur inside any UIControl:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return ([touch.view isKindOfClass:[UIControl class]] == NO);
}
UIPageViewController has two UIGestureRecognizers. You can access them via gestureRecognizers property. Determine which one is UITapGestureRecognizer and then use this. Hope this helps.
For people that just want to copy/paste code, here is mine :
// I don't want the tap on borders to change the page
-(void) desactivatePageChangerGesture {
for (UIGestureRecognizer* gestureRecognizer in self.pageViewController.gestureRecognizers) {
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
gestureRecognizer.enabled = NO;
}
}
}
Just call this function after the UIPageViewController creation.
I had this same problem, and was unsure how to handle the UIGestureRecognizer delegate methods. This short example assumes you are using the "Page Based Application" project type in Xcode 4. Here is what I did:
In RootViewController.h, I made sure to announce that RootViewController would handle the UIGestureRecognizerDelegate protocol:
#interface RootViewController : UIViewController <UIPageViewControllerDelegate, UIGestureRecognizerDelegate>
In RootViewController.m, I assigned RootViewController as the delegate for the UITapGestureRecognizer. This is done at the end of the viewDidLoad method. I did this by iterating over each gestureRecognizer to see which one was the UITapGestureRecognizer.
NSEnumerator *gestureLoop = [self.view.gestureRecognizers objectEnumerator];
id gestureRecognizer;
while (gestureRecognizer = [gestureLoop nextObject]) {
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[(UITapGestureRecognizer *)gestureRecognizer setDelegate:self];
}
}
Finally, I added the gestureRecognizer:shouldReceiveTouch method to the bottom of RootViewController.m (This is copied directly from Split's link):
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass:[UIControl class]]) {
// we touched a button, slider, or other UIControl
return NO; // ignore the touch
}
return YES; // handle the touch
}
Comment out these line from your code
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
or use UIGestureRecognizer as told by Split
Hope this will help you
OLD ANSWER: If your UIPageViewController has a transitionStyle of UIPageViewControllerTransitionStyleScroll and you are in iOS 6.0+, then you can't use the gestureRecognizer:shouldReceiveTouch: method, because there is no way to set the delegate to self on the gestureRecognizers since pageViewController.gestureRecognizers will return nil. See UIPageViewController returns no Gesture Recognizers in iOS 6 for more information about that.
If you simply want to make sure your UIPageViewController passes along button touch events to a UIButton, you can use
for (UIScrollView *view in _pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
view.delaysContentTouches = NO;
}
}
if you have a transitionStyle of UIPageViewControllerTransitionStyleScroll and you are in iOS 6.0+.
See this answer about why delaysContentTouches = NO is needed for some cases of a UIButton in a UIScrollView
UPDATE: After doing a little more research it appears that if your issue is that the UIButton click seems to only be called sometimes, then that is actually probably the desired behavior inside a UIScrollView. A UIScrollView uses the delaysContentTouches property to automatically determine if the user was trying to scroll or trying to press a button inside the scroll view. I would assume it is best to not alter this behavior to default to NO since doing so will result in an inability to scroll if the user's finger is over a button.
None of the solutions here where you intercept the UIPageViewController's tap gesture recognizers worked for me. I'm targeting iOS 8 and 9.
What worked is to override the functions touchesBegan, touchesCancelled, touchesMoved, and touchesEnded in my custom button which is a subclass of UIControl. Then I just manually send the .TouchUpInside control event if the touch began and ended within the frame of my custom button.
I didn't have to do anything special for the containing page view controller, or the view controller that contains the page view controller.
Swift 5 answer here should do the job.
pageViewController.view.subviews.compactMap({ $0 as? UIScrollView }).first?.delaysContentTouches = false
I have a view with multiple subviews. When a user taps a subview, the subview expands in size to cover most of the screen, but some of the other subviews are still visible underneath.
I want my app to ignore touches on the other subviews when one of the subviews is "expanded" like this. Is there a simple way to achieve this? I can write code to handle this, but I was hoping there's a simpler built-in way.
Hope this help...
[[yourSuperView subviews]
makeObjectsPerformSelector:#selector(setUserInteractionEnabled:)
withObject:[NSNumber numberWithBool:FALSE]];
which will disable userInteraction of a view's immediate subviews..Then give userInteraction to the only view you wanted
yourTouchableView.setUserInteraction = TRUE;
EDIT:
It seems in iOS disabling userInteraction on a parent view doesn't disable userInteraction on its childs.. So the code above (I mean the one with makeObjectsPerformSelector:)will only work to disable userInteraction of a parent's immediate subviews..
See user madewulf's answer which recursively get all subviews and disable user interaction of all of them. Or if you need to disable userInteraction of this view in many places in the project, You can categorize UIView to add that feature.. Something like this will do..
#interface UIView (UserInteractionFeatures)
-(void)setRecursiveUserInteraction:(BOOL)value;
#end
#implementation UIView(UserInteractionFeatures)
-(void)setRecursiveUserInteraction:(BOOL)value{
self.userInteractionEnabled = value;
for (UIView *view in [self subviews]) {
[view setRecursiveUserInteraction:value];
}
}
#end
Now you can call
[yourSuperView setRecursiveUserInteraction:NO];
Also user #lxt's suggestion of adding an invisible view on top of all view's is one other way of doing it..
There are a couple of ways of doing this. You could iterate through all your other subviews and set userInteractionEnabled = NO, but this is less than ideal if you have lots of other views (you would, after all, have to subsequently renable them all).
The way I do this is to create an invisible UIView that's the size of the entire screen that 'blocks' all the touches from going to the other views. Sometimes this is literally invisible, other times I may set it to black with an alpha value of 0.3 or so.
When you expand your main subview to fill the screen you can add this 'blocking' UIView behind it (using insertSubview: belowSubview:). When you minimize your expanded subview you can remove the invisible UIView from your hierarchy.
So not quite built-in, but I think the simplest approach. Not sure if that was what you were thinking of already, hopefully it was of some help.
Beware of the code given as solution here by Krishnabhadra:
[[yourSuperView subviews]makeObjectsPerformSelector:#selector(setUserInteractionEnabled:) withObject:[NSNumber numberWithBool:FALSE]];
This will not work in all cases because [yourSuperView subviews] only gives the direct subviews of the superview. To make it work, you will have to iterate recursively on all subviews:
-(void) disableRecursivelyAllSubviews:(UIView *) theView
{
theView.userInteractionEnabled = NO;
for(UIView* subview in [theView subviews])
{
[self disableRecursivelyAllSubviews:subview];
}
}
-(void) disableAllSubviewsOf:(UIView *) theView
{
for(UIView* subview in [theView subviews])
{
[self disableRecursivelyAllSubviews:subview];
}
}
Now a call to disableAllSubviewsOf will do what you wanted to do.
If you have a deep stack of views, the solution by lxt is probably better.
I would do this by putting a custom transparent button with the same frame as the superView. And then on top of that button I would put view that should accept user touches.
Button will swallow all touches and views behind it wouldn't receive any touch events, but view on top of the button will receive touches normally.
Something like this:
- (void)disableTouchesOnView:(UIView *)view {
UIButton *ghostButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, view.frame.size.width, view.frame.size.height)];
[ghostButton setBackgroundColor:[UIColor clearColor]];
ghostButton.tag = 42; // Any random number. Use #define to avoid putting numbers in code.
[view addSubview:ghostButton];
}
And a method for enabling the parentView.
- (void)enableTouchesOnView:(UIView *)view {
[[view viewWithTag:42] removeFromSuperview];
}
So, to disable all views in the parentViev behind yourView, I would do this:
YourView *yourView = [[YourView alloc] initWithCustomInitializer];
// It is important to disable touches on the parent view before adding the top most view.
[self disableTouchesOnView:parentView];
[parentView addSubview:yourView];
Just parentView.UserInteractionEnabled = NO will do the work.
Parent view will disable user interaction on all the view's subviews. But enable it does not enable all subviews(by default UIImageView is not interactable). So an easy way is find the parent view and use the code above, and there is no need to iterate all subviews to perform a selector.
Add a TapGestureRecognizer to your "background view" (the translucent one which "grays out" your normal interface) and set it to "Cancels Touches In View", without adding an action.
let captureTaps = UITapGestureRecognizer()
captureTaps.cancelsTouchesInView = true
dimmedOverlay?.addGestureRecognizer(captureTaps)
I will give my 2 cents to this problem.
Iteratively run userInteractionEnabled = false it's one way.
Another way will be add a UIView like following.
EZEventEater.h
#import <UIKit/UIKit.h>
#interface EZEventEater : UIView
#end
EZEventEater.m
#import "EZEventEater.h"
#implementation EZEventEater
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor clearColor];
self.userInteractionEnabled = false;
}
return self;
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//EZDEBUG(#"eater touched");
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
}
In your code you add the EZEventEater view to cover all the views that your may block the touch event.
Whenever you want to block the touch event to those views, simply call
eater.userInteractionEnabled = YES;
Hope this helpful.
In Swift 5, I achieved this behaviour by placing a view right on top(the highlighted one) and setting:
myView.isUserInteractionEnabled = true
This does not let the touches go through it, thus ignoring the taps.
For my app, I think it will be sufficient to disable navigation to other tabs of the app (for a limited duration, while I'm doing some processing):
self.tabBarController.view.userInteractionEnabled = NO;
Also, I disabled the current view controller--
self.view.userInteractionEnabled = NO;
(And, by the way, the recursive solutions proposed here had odd effects in my app. The disable seems to work fine, but the re-enable has odd effects-- some of the UI was not renabled).
Simple solution. Add a dummy gesture that does nothing. Make it reusable by adding it to an extension like this:
extension UIView {
func addNullGesture() {
let gesture = UITapGestureRecognizer(target: self,
action: #selector(nullGesture))
addGestureRecognizer(gesture)
}
#objc private func nullGesture() {}
}
setUserInteractionEnabled = NO on the view you want to disable
I had the same problem, but the above solutions did not help.
I then noticed that calling
super.touchesBegan(...) was the problem.
After removing this the event was only handled by the top-most view.
I hope this is of help to anybody.
How can I position a button on my view dynamically?
Try this code
for(UIView * view in self.view.subviews)
{
if([view isKindOfClass:[UIButton class]])
{
// view is button
}
}
You can also use tags
Set the tag property of the UIButton (e.g. in interface builder, or programatically). Then you can walk the children of the owning view:
for (UIView *view in myView.subviews) {
if (view.tag == 101 /*or whatever you set it to*/) {
// things...
}
}
Ok, now we understand the question better...
You can place a button/UIComponent at runtime by settings its frame. This will place it at a given coordinate within the view containing the button.
myButton.frame = CGRectMake(newX, newY, myButton.frame.width, myButton.frame.height);
If you wish to do a linear style animation (no curves), look up CATransitions, see iPhone UIView Animation Best Practice. If you need to support pre-iOS4 devices, you can't use CATransitions, and have to do it the old way using UIView animations.
To do more complex animations, you might have to mess around with timers, which can slightly juddery effects if you're trying to do complicated animation paths.
Get a array of all the subviews of a view using:
[myView subviews]
iterate through that array and check for your butotn by some unique characteristic. (You can set the button's tag attribute to some unique value and check for that value)
I have a UITableViewController, when there is no data to populate the UITableView, I want to add a button, which uses an image. So, rather than the user seeing a tableview with no records, they will see an image that says, "No records have been added, Tap to add one", then they click and we create a new one.
I assumed I would just hide the UITableView, then create the button, but I never see the button. Here I am using:
if ([[fetchedResultsController sections] count] == 0) {
self.tableView.hidden = YES;
// Create button w/ image
UIButton * btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = CGRectMake(0, 0, 100, 50);
[btn setImage:[UIImage imageNamed:#"no-rides.png"] forState:UIControlStateNormal];
[self.view addSubview:btn];
}
Ideas on why I would never see the button? When I show this view, it seems to have a transparent background for a second, then changes white...
I am not sure if it works but you can try this:
If your view controller is not a UITableViewController and it contains a UITableView
[self.tableView removeFromSuperview]; then [self.view addSubview];
If your view controller is a UITableViewController, you may need to consider to set the first row to contain the image and text. Then, you can handle the event: tableView:didSelectRowAtIndexPath: and if user click on the first cell, you trigger the method handle the button event
In a UITableViewController, self.view could be self.tableView in which case hiding the table would also hide the button. Try using a custom UIViewController and creating either the table or the button as a subview of self.view instead.
Alternately, when there is no data, you can create a single custom cell containing your button and use that instead of normal cells.
I believe you can add the button to the table footer, as the table footer is shown even when there are no cells. (Note, this is different to a section footer.)
By default the tableFooterView property of the UITableView is nil. So just create your button, and then do:
self.tableView.tableFooterview = btn;
For all future travelers, I simply set .userInteraction = true (Swift) and it worked like a charm. All in all:
tableView.backgroundView = constructMyViewWithButtons()
tableView.backgroundView!.userInteraction = true