I set a UIButton's exclusiveTouch to YES, and it works fine (other views will not receive touch-events if I touch the button). The exception is that when I touch the UIBarButtonItem on the navigation bar, its action is triggered. It doesn't happen every time, but if you tap a bit higher on the UIBarButtonItemor just tap on the status bar close to the UIBarButtonItem, it happened.
Can anyone explain why this happens?
UIBarButtonItem is not a subclass of UIView. So it doesn't have exclusiveTouch property and won't be affected by other views' exclusiveTouch.
Try this in viewDidLoad
//Set exclusive touch on each navigationItem
for(UIView *navigationItem in self.navigationController.navigationBar.subviews)
{
[navigationItem setExclusiveTouch:YES];
}
Hope this helps.
Related
In a UINavigation bar, there is a right custom share UIBarButtonItem and a back button in the left UIBarButtonItem. When simultaneously tapping on both buttons, the app produces a black view, possibly because both buttons are attempting to display a new view simultaneously - the share button presents a UIActivityViewController and the back button a VC from the prior screen.
In looking through similar questions here, I've tried the following solutions but neither prevented a black view from appearing on a simultaneous button touch:
Inserting exclusiveTouch into ViewDidLoad in the following 2 ways
a)
for(UIView *temp in self.navigationController.navigationBar.subviews)
{ [temp setExclusiveTouch:YES]; }
b) [self.navigationController.navigationBar setExclusiveTouch:YES];
Applying self.navigationController.navigationBar.userInteractionEnabled = NO; after a touch.
Are there other solutions?
Is this related to multi-threading?
In each touch event handler, add the following line:
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
When the handler has completed, execute the following:
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
It's up to you to figure out what to consider the end of the handler. If you're pushing or popping view controllers, you might add that second line to the viewWillAppear of the relevant view controllers. If you're displaying a modal view controller, you can use the completion handler of -[UIViewController presentViewController:animated:completion:].
Pretty much simple you can use make use of ExclusiveTouch property in this case
[self.navigationController.navigationBar setExclusiveTouch:YES];
This is a Boolean value that indicates whether the receiver handles touch events exclusively.
Setting this property to YES causes the receiver to block the delivery of touch events to other views in the same window. The default value of this property is NO.
If you want only one button to respond to touches at a time, you need to set exclusiveTouch for that button, rather than for the parent view.
Put this just after you add your bar button items.
for(UIView *temp in self.navigationController.navigationBar.subviews)
{
[temp setExclusiveTouch:YES];
}
or you can set exclusiveTouch individually for each UIBarButton while creating them.
Pressing the button quickly and not holding for a short time, will not highlight the button.
Different from a UIButton on a common UIView.
Like the head photo in official Twitter client got same issue.
Instagram client seems solved this, all buttons works fine.
Find same question here:
Why doesn't UIButton showsTouchWhenHighlighted work when the button is on a UITableViewCell?
But I still don't know how to fix it.
Well... a UITableView is a subclass of UIScrollView and the UIScrollView class is known to eat touches for it's own purpose.
When it realizes the touch was not meant for it, it passes it to it's immediate subview.
This feature is the delaysContentTouches property (which by default is YES).
Which is why, the UIButton shows it's highlighted state only after a extended touch because the touch event was with the UITableView for a short while until it determined whether the touch was meant for scrolling or swiping the cell and on realizing the touch was for neither, it immediately passes the touch event to the subView directly below it.
In case of a quick-tap, the button's highlighted state is bypassed due to this delay and the target selector method is called directly.
To show the highlighted state of the button in a UITableView (just as it would on a UIView) do:
For iOS7+:
In -viewDidLoad or anywhere appropriate do:
[yourTableViewObject setDelaysContentTouches:NO];
Also... The cell.subviews has a class UITableViewCellScrollView which apparently is another scrollView and we need to disable the delaysContentTouches property of this class as well.
So... in the -cellForRowAtIndexPath: method (just before return cell;) do:
NSArray *test = cell.subviews;
for (UIView *currentView in cell.subviews) {
if ([NSStringFromClass([currentView class]) isEqualToString:#"UITableViewCellScrollView"]) {
UIScrollView *svTemp = (UIScrollView *) currentView;
[svTemp setDelaysContentTouches:NO];
break;
}
}
For iOS 6-:
In iOS6, the cell.subviews has a UITableViewCellContentView class which is not a scrollView subclass and so all it takes is setting one parameter for the tableView alone.
So, in -viewDidLoad or anywhere appropriate, this is all that you need:
[yourTableViewObject setDelaysContentTouches:NO];
PS: By doing this, it will mess up with the scrolling of the tableView so use your better judgement.
I want to developer app with a custom navigation bar like in the following images:
I think that i need to subclass UINavigationBar and add button to centre of nav bar, but i don't really know how to make navigation bar look like on image. Can you please give me advice what should i do, links to any kind of documentation would be awesome!
Similar questions about navBar that doesn't helped me:
ios back button in the bar
Use custom Navigation Bar in iOS
Custom Navigation Bar in iOS 5
rogcar
EDIT:
My idea is next: make custom navigation bar height little bigger than default size, and add background image with arrow in it and with some transparency on the edges.
If you want a button (you probably do want) you can achieve it completely by subclassing UINavigationBar. You should remember that height of UINavigationBar is read-only property.
Style but not tappable:
So let's assume we subclass the navigation bar and add button there. You could do this and it will be going look great. For example:
- (void)drawRect:(CGRect)rect
{
self.backgroundColor = [UIColor lightGrayColor];
UIButton *myButton = [[UIButton alloc] initWithFrame:CGRectMake(self.frame.size.width/2-50, 0 , 100, 100)];
[myButton setBackgroundColor:[UIColor lightGrayColor]];
[myButton setTitle:#"Normal" forState:UIControlStateNormal];
[myButton setTitle:#"Highlighted" forState:UIControlStateHighlighted];
[self addSubview:myButton];
[self sendSubviewToBack:myButton];
}
But you will facing a problem that your button is non tapeable below UINvaigationBar. (I post an image on the bottom of the answer)
So there is clearly not a path you want to follow. Don't even try that.
Style but not tappable 2:
You may override this method in your navigation bar subclass
- (CGSize) sizeThatFits:(CGSize)size {
return CGSizeMake(custom_width, custom_height);
}
And then mask it using UIBezierPath for example
The right (tappable) way:
You have to create a view stick to your UINavigationBar. What i will do here (if you want it to every screen) is:
Make a Category of UIViewController which can draw (for example - this is easiest way) UIButton.
Style this 'UIButton' whatever you want (if you want
Pin action to 'UIButton': [btn addTarget:self action:#selector(menuShow:) forControlEvents:UIControlEventTouchUpInside];
menuShow: method should be declare in your category
You can call drawing button every time you want to redraw view controller.
As you can see there there will be two separates View: UINavigationBar and UIButton. This is allow you to set content under this little button and make it tapable.
So why just don't hide navigation bar, and use different view? Because iOS7 ;) When Apple change it in iOS7 for example then you have to rebuild your pseudo NavigationBar, with only additional view, you don't need to do anything.
You do not need to subclass UINavigationBar. Create UIView add to it UIImageView as background with image in the shape you need, add button.
Subclass UINavigationController hide UINavigationBar, add custom navigation bar.
First Hide navigation bar using -
self.navigationController.navigationBarHidden = YES;
Then create UIView with required height,height of navigationBar is 44px.Then create background image view, object of required UIButton and add all objects on created UIView as a subview.It will look like navigationBar.Thank you.
You can add your custom shaped view as titleView on the navigation bar.
Just make sure that clipsToBounds is set to NO, so it doesn't get clipped.
There are a lot of talks about customization of UIBarButtonItem with custom view as an UIButton. It's quite clear. What I didn't find so far though, and what surprises me, is that there is no mentioning for custom UIBarButtonItems which handle interface rotations - its a common behavior when you rotate an iPhone, bar buttons get squeezed vertically. However, if you customize UIBarButtonItem in ordinary way (by calling initWithCustomView: method), it will stay non-squeezed after rotating into landscape orientation. Are there any workarounds for that?
Well, I've found solution with handling UIApplicationDidChangeStatusBarOrientationNotification in custom UIBarButtonItem class pretty decent.
If you can add the UIBarButtonItem from IB (you don't have to create it programatically) you can set the springs and struts to get your default behaviour. This solution worked for me.
I think you should rebuild the UIBarButtonItem (Your Custom UIButton) with new orientation image and text, or reset the frame of custom view.
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
UIButton *rightItemButton = (UIButton *)self.navigationItem.rightBarButtonItem.customView;
//resize the frame
if (isLandscape) {
} else if (isPortrait) {
....
}
}
It may seem like duplicate, but I can't actually find any good answer to my concrete situation.
I have some button with background image (1pt-wide, streched), icon-like image inside along with the text label. It's height is 33pt, I need to make it's hittest area 44pt-high.
I saw two solutions, but neither of them works for me.
First solution is to enlarge frame and adjust image so it would have some padding. But that is not acceptable for me, because I have both background image and image inside the button.
Second solution is to subclass UIButton (which is absolutely acceptable) and override - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event. I did that, when I put some breakpoints in this method and tried to tap in desired area nothing happened (breakpoints worked when tapped inside the button frame).
Are there any other solutions? What could be possibly wrong with the second solution?
If you create the view programmatically, you can instantiate a UIView that is 44px high, add a tap gesture recognizer to it, and add your UIButton as a subview inside this view:
CGRect bigFrame = CGRectMake(0,0,100,44);
UIView *big = [[UIView alloc] initWithFrame:bigFrame];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doPress)];
[big addGestureRecognizer:tapRecognizer];
UIButton *yourButton = ...
[big addSubview:yourButton];
[self.view addSubview:big];
Then implement the selector as:
- (void)doPress {
[yourButton sendActionsForControlEvents:UIControlEventTouchUpInside];
}
In that way, the outer view taps will be interpreted as button presses for your button.