iOS UIBarButtonItem action falls through to button behind it - ios

I am creating a custom UIView to act as a "pop up" window with a toolbar. The action for each UIBarButtonItem in the toolbar is triggered as expected on iPhone simulator but is not triggered on iPad or iPad simulator. Instead, the action for the button behind the pop up view is triggered. I am targeting iOS 8. What might be the problem?
This is where the custom view is being created. This is inside a subclass of UIView. It contains the selectors:
-(void)setupButtons: (SandBoxViewController*)ctrl :(UIImage*)img1 :(UIImage*)img2 :(UIImage*)img3 :(CGRect) rect
{
controller=ctrl;
CGRect frame = CGRectMake(rect.origin.x-12, rect.origin.y, rect.size.width*4, TOOLBARH);
toolbar = [[UIToolbar alloc]initWithFrame:frame];
[toolbar setBarStyle:UIBarStyleBlackTranslucent];
UIBarButtonItem *customItem1 = [[UIBarButtonItem alloc]
initWithImage:img1 style:UIBarButtonItemStylePlain
target:self action:#selector(setToOne:)];
UIBarButtonItem *customItem2 = [[UIBarButtonItem alloc]
initWithImage:img2 style:UIBarButtonItemStylePlain
target:self action:#selector(setToTwo:)];
UIBarButtonItem *customItem3 = [[UIBarButtonItem alloc]
initWithImage:img3 style:UIBarButtonItemStylePlain
target:self action:#selector(setToThree:)];
NSMutableArray *toolbarItems = [NSMutableArray arrayWithObjects:customItem1, customItem2,customItem3,nil];
[toolbar setItems:toolbarItems];
[self addSubview:toolbar];
}
This creates the custom view and calls the above function. It is in the ViewController:
-(void)setupLongPressButtonView: (CGRect)frame
{
self.buttonView =[[LinkButtonView alloc]initWithFrame:frame];
UIImage *image1=[[UIImage imageNamed:#"connect1.png"]imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIImage *image2=[[UIImage imageNamed:#"connect2.png"]imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIImage *image3=[[UIImage imageNamed:#"connect3.png"]imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
//retrieves the button that I want the popup to be placed above
UIBarButtonItem *button=[self.toolbarItems objectAtIndex:self.linkButtonIndex];
CGRect buttonFrame=button.customView.frame;
[self.buttonView setupButtons:self :image1 :image2 :image3 :buttonFrame];
[self.view addSubview:self.buttonView];
CGRect superv=self.view.frame;
CGRect subv=self.buttonView.frame;
NSLog(#"superview bounds: %f,%f,%f,%f. subview bounds: %f,%f,%f,%f",superv.origin.x,superv.origin.y,superv.size.width,superv.size.height,subv.origin.x,subv.origin.y,subv.size.width,subv.size.height);
}
Clicking with long press on the button marked Link causes the view to pop up above it. See that the pop up view buttons show up OK but clicking on them causes the button's action behind them to be triggered (as if its ignoring the view).
Note: I also tried to create a custom view with a UIButton for the UIBarButtonItem but it didn't make any difference.
Update: Here are the bounds of the superview and the view containing the buttons in question:
superview bounds: 0.000000,0.000000,768.000000,1024.000000
subview bounds: 0.000000,912.000000,224.000000,56.000000
So the sub is within the constraints of the super

as if its ignoring the view
Exactly right. It is ignoring it. The pop-up view is outside the bounds of its superview. A view outside the bounds of its superview is, by default, untouchable. Taps on it just fall through as if it weren't there. This is totally normal, expected behavior.

Related

make custom view stick to top of the keyboard while rotating device

My app is iPad only and the root view is embedded in a navigation controller which has two sub views:
1) a UITextView (not UITextField) which covers the whole area except for the navigation bar.
2) another UIView which serves as a tool bar. It covers the UITextView and initially stays at the bottom of root view.
Now I can make the "tool bar" goes up and down in sync with the virtual keyboard.
But there is one problem: if I rotates the device while the keyboard is showing, the "tool bar" no longer sticks to the top of virtual keyboard, instead it stays in the middle of the screen while rotating and falls down to meet the keyboard after rotation, which is quite ugly.
Currently I make the tool bar view goes up and down by dynamically adding and removing constraints on it and I am not sure whether this is a problem because I am only testing it using simulator.
Can anyone give me some advice on it?
Examples of such UITextView with tool bar at the bottom apps may be
Document Pro or Pages.
I will recommend using inputAccessoryView property of the UITextView.
UIToolbar* toolbar = [[UIToolbar alloc] init];
toolbar.barStyle = UIBarStyleDefault;
toolbar.items = [NSArray arrayWithObjects:
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:nil action:nil],
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:nil action:nil],
[[UIBarButtonItem alloc]initWithTitle:#"Cancel" style:UIBarButtonItemStylePlain target:self action:nil],
[[UIBarButtonItem alloc]initWithTitle:#"Done" style:UIBarButtonItemStyleDone target:self action:nil],
nil];
[toolbar sizeToFit];
self.textfiled.inputAccessoryView = toolbar;
Simply with the above you will a toolbar sticking on top of the keyboard and it will have a nice and smooth animation during rotation. I haven't looked at the other app that you quotes, but I believe that's what they use in Pages.
As an alternative, you can manually layout your toolbar without applying constraints. For that, you can:
Add method to handle keyboard appearing.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
In this method you need to calculate current keyboard height.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
self.keyboardHeight = MIN(kbSize.height, kbSize.width);
[self layoutElements];
}
This method also called when you rotate your device.
Then in your custom layoutElements method you use pre-calculated keyboard height to position your toolbar.
You can use the size of your root view or screen size ([[UIScreen mainScreen] bounds].size), but pay attention that in iOS7 and iOS8+ there are different values for width and height depending on orientation.
To ensure that your custom layout method (layoutElements) is called you can also put it inside viewWillLayoutSubviews method.
- (void) viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[self layoutElements];
}

UIBarButtonItem with custom view flexibly sized

Is it somehow possible to get a UIBarButtonItem with a custom view containing a UISlider to automatically resize similarly to a UIBarButtonItem using the system style UIBarButtonSystemItemFlexibleSpace?
For example (for the sake of brevity I've left out setting frames and adding the toolbar as a subview to an existing view)
UISlider *slider = [UISlider new];
slider.minimumValue = 0;
slider.maximumValue = 1;
slider.value = 0.5
UIBarButtonItem *sliderItem = [[UIBarButtonItem alloc] initWithCustomView:slider];
UIBarButtonItem *exampleItem = [[UIBarButtonItem alloc] initWithTitle:#"Example" style:UIBarButtonItemStylePlain target:nil action:nil];
UIToolbar *toolbar = [UIToolbar new];
toolbar.items = #[sliderItem, exampleItem];
The idea being the toolbar is the full width of the screen, exampleItem can be a variable width due to localisation of it's title, and slideItem resizes similarly to UIBarButtonSystemItemFlexibleSpace to take up the remaining space in the toolbar.
But the results are instead the slider stays taking up what ever frame you've given it (or the default/minimum frame if left out) and the next button just sits up against it.
I've tried the method mentioned in the following answer make a UIBarButtonItem with customView behave like Flexible-Space item by changing the auto resizing mask of the slider, but it appears not to work (the question is about using a UITextField not a UISlider which is probably why.)
I've also tried using another UIView containing the UISlider as the custom view to intercept any sizeThatFits calls, but it in fact never gets called as I suspected.

iOS 7.1 update hide navigation bar left button by default

I have following code to create UINavigationBar and set the navigation item with a back button at the right side.
UINavigationBar *navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 60)];
navBar.delegate = self;
UIBarButtonItem *back = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleDone target:self action:#selector(backButtonTapped)];
UINavigationItem *backItem = [[UINavigationItem alloc] init];
[backItem setTitle:#"What's New"];
[backItem setLeftBarButtonItem:back];
backItem.leftBarButtonItem.enabled = YES;
[navBar pushNavigationItem:backItem animated:NO];
[self.view addSubview:navBar];
This worked perfectly until i update my xCode 5 to iOS 7.1 update recently.
But now when the UIView presented the navigation button is not visible. But when i touch the location of the button (where it used to be before the update), it shows me the button and click even is firing.
My question is how to set the button visible at the moment view is present to user ?
Thank you.
Try to initialize your UIBarButtonItem with initWithCustomView. Also, try to set buttonType of the UIButton to UIButtonTypeSystem.
Thanks to all,
finally what happen is as follows.
The above behaviour is not what i assume it is. When a new view is loaded the button is disappear by default and only if i move my finger here & there on the location button used to be it appears. Further when i keep the view still, after it loaded, about 15 seconds, button appears.
So i suspect this is with "viewDidLoad" method. (Code related to this UINavigationBar is in viewDidLoad method.
When I move above code to "viewDidAppear" method it start to work again.

How to add UINavigationBar with two UIBarButtonItem programmatically?

I added code to the viewDidLoad method of ViewController. I have two UIBarButtonItem, one of them is on the left called Left and the other one is at the right called Right. I can get the left button to work but I cannot even see the right button. What am I doing wrong?
UINavigationBar *navbar = [[UINavigationBar alloc]initWithFrame:CGRectMake(0, 0, 420, 44)];
[navbar setBackgroundImage:[UIImage imageNamed:#"bg_fade.png"] forBarMetrics:UIBarMetricsDefault];
UINavigationItem *item = [[UINavigationItem alloc]initWithTitle:#"Tray Tracker"];
UIBarButtonItem *leftButton = [[UIBarButtonItem alloc]initWithTitle:#"Left" style:UIBarButtonItemStylePlain target:self action:
#selector(leftAction:)];
UIBarButtonItem *rightButton = [[UIBarButtonItem alloc]initWithTitle:#"Right" style:UIBarButtonItemStylePlain target:self action:
#selector(saveAction:)];
item.leftBarButtonItem = leftButton;
item.rightBarButtonItem = rightButton;
navbar.items = #[item];
[self.view addSubview:navbar];
I do have the IBActions for leftAction and saveAction
If you simply want a bar with two buttons, you should use a UIToolbar instead. It's much easier to handle for this simple case. The only change you would need is to create a UIBarButtonItem flexibleSpace (it's a system item), and call [toolbar setItems:#[leftButton, flexibleSpace, rightButton]];
If you need actual navigation functionality such as a back button, take a look at this answer. You need to use UINavigationBar pushNavigationItem.
using UINavigationBar without UINavigationController
In your example, you are misusing the items property. The documentation for UINavigationBar states,
The bottom item is at index 0, the back item is at index n-2,
and the top item is at index n-1, where n is the number of items in the array.
In order to do accomplish what you're trying to do with a UINavigationBar, I think you need multiple navigationItems.
It would be MUCH easier to either use a UIToolbar or use the UINavigationBar in conjunction with a UINavigationController.
Edit
Now that you posted the image, I think the reason you can't see the right bar button is because your frame width is too large. The iphone screen is 320 px wide, so your frame for the navigationBar should be CGRectMake(0, 0, 320, 44)

Position Toolbar on top of the screen with correct shadow in the bottom

I have navigation controller based app and one viewcontroller presents modally graph in a landscape mode. I then add Toolbar with Done button to dismiss the graph vc and return to navigation and portrait mode.
I can't figure out how to position the Toolbar on top of the graph viewcontroller with correct shadow on the bottom of the toolbar. So far I have this code to add the toolbar to the bottom position, which has default shadow on the top of the toolbar. Is it allowed to have toolbar on top of the screen? For the reason of forced orientation rotation I cannot use navigation controller with the graph vc. Platform is iOS7 and iPhone only. Thanks.
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(self.view.bounds.origin.x, self.view.bounds.size.width - 44.0, self.view.bounds.size.height, 44.0)];
UIBarButtonItem *flexibleSpaceButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *doneButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(done)];
toolbar.items = [NSArray arrayWithObjects:flexibleSpaceButtonItem, doneButtonItem, nil];
[self.view addSubview:toolbar];
I think your frame is looks a bit strange. You are calculating the y position from the view width and the width from the view height.
Maybe you have to specify that the toolbar is on top using the UIBarPositioning protocol.
UIImage *shadow = [toolbar shadowImageForToolbarPosition: UIBarPositionAny];
[toolbar setShadowImage:shadow forToolbarPosition:UIBarPositionTopAttached];
Next Edit:
This is what the documentation has to say about the iOS 7 UIToolbar:
UIBarPositionTop
Specifies that the bar is at the top of its containing view.
The system uses this as a hint to draw directional decoration accordingly. For example, any shadow would be drawn below the bar.
Instances of UIToolbar do not appear with this position on iPhone, but they can on iPad.
Available in iOS 7.0 and later.
Declared in UIBarCommon.h.
Maybe toolbars are not meant to be used on top. However, you can simply add a shadow with addSubview:
Try to implement the method
- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar
from of UIToolbarDelegate protocol.

Resources