UIBarButtonItem with custom view flexibly sized - ios

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.

Related

iOS UIBarButtonItem action falls through to button behind it

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.

How do I fit a UISearchBar in a UIBarButtonItem?

I'm using a UISearchController in iOS 8 and when I make its UISearchBar my table's header view everything is fine. However now I need it in my UINavigationBar's left bar button (can't be the title view because of the vertical centering when I enlarge the nav bar). When I wrap the UISearchBar in a UIBarButtonItem it is larger than the width of the screen.
I think it is related to my view controller initialized from a storyboard with size classes enabled which means my frames are not set till viewDidLayoutSubviews. However I had the same issue with my segmented control in a toolbar and I just called sizeToFit on the toolbar in viewDidLayoutSubviews and that fixed the toolbar. When I do the same for the search bar it still draws partly off screen. Apple does this in some of their iPad apps but how?
Note: I can hack it to get it all on screen but I have to wrap it in another view but then the color is off and it just seems wrong and I think the animation is off.
You need to create a UIBarButtonItem with UIBarButtonSystemItemFixedSpace with -10 width which will remove 10 px padding.
UIBarButtonItem *Spacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
[Spacer setWidth:-10];
UISearchBar *searchBar= [[UISearchBar alloc] initWithFrame:CGRectMake(0, 5, self.view.frame.size.width-10, 39)];
UIBarButtonItem *searchBarItem = [[UIBarButtonItem alloc] initWithCustomView:searchBar];
self.navigationItem.leftBarButtonItems = [NSArray arrayWithObjects:Spacer,searchBarItem,nil];

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.

UISlider does not appear in the UIToolbar

I have a fairly basic video player I'm creating. There is a UIToolbar (toolBar) that contains several buttons and a UISlider. The slider will appear just fine when the toolBar is not a referencing outlet of the View Controller. Once I link up the toolBar with the view controller, the slider disappears, but all other buttons remain on the toolbar.
I am using a storyboard for the UI, and adding buttons and the slider to the toolbar when the view loads with...
UIBarButtonItem *scrubberItem = [[UIBarButtonItem alloc] initWithCustomView:movieTimeControl];
UIBarButtonItem *flexItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
toolBar.items = [NSArray arrayWithObjects:flexItem, currentPositionLabel, scrubberItem, timeLeftLabel, nil];
movieTimeControl is a UISlider property.
I need the toolBar to be an outlet of the controller so that I can dismiss it on specific gestures.
This is how it looks before I link up the toolBar with the controller...
And after linking it with the controller...
So the space for the slider remains, but the slider is not there.
Any thoughts on why the UISlider is disappearing?

Resources