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

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];
}

Related

Gap between nested container view controllers

Have a UITabbarController and two UINavigationViewController nested in. UINavigationViewControllers have toolbars both, but toolbars are not laying at the bottom, but 44px upper. Why?
Toolbars added programmatically:
UIBarButtonItem *update = [[UIBarButtonItem alloc] initWithImage:[PfbUtility imageFromConfigIfExist:#"reload"] style:UIBarButtonItemStylePlain target:self action:#selector(eah)];
self.toolbarItems = [NSArray arrayWithObjects: update, nil];
self.navigationController.toolbarHidden = NO;
Attach the bottom side of the splitview to the bottom edge of the screen/view (below the tabBar), not to the top of the tabBar. The application understands that it is below a tabBar and will automatically offset/inset content based on this. This is not a 'nasty fix', it is the intended way to do it. When doing this, you also have the option of using translucent navigation and tabBars.

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];

Strange behavior with UIToolbar in iOS app

In my current project I have an app based on a navigation controller. Some of the view in the app require a toolbar at the bottom of the view. I am creating the toolbar programmatically with the following (excerpted from viewDidLoad in a table view controller):
UIBarButtonItem *reloadButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(refreshList)];
[reloadButton setTitle:#"Refresh"];
[reloadButton setTintColor:[UIColor whiteColor]];
self.navigationController.toolbar.barStyle = UIBarStyleBlackTranslucent;
[self.navigationController.toolbar sizeToFit];
CGFloat toolbarHeight = [self.navigationController.toolbar frame].size.height;
[self.navigationController.toolbar setFrame:CGRectMake(CGRectGetMinX(self.view.bounds),
438,
CGRectGetWidth(self.view.bounds),
toolbarHeight)];
NSArray *toolbarItems = [[NSArray alloc] initWithObjects:reloadButton, nil];
[self setToolbarItems:toolbarItems];
[self.navigationController setToolbarHidden:NO];
This has worked well in most views. However, I am now incorporating it into another view using identical code. In that controller, the toolbar appears about an inch above the bottom of the view--it doesn't snap to the bottom of the view as it does in other view controllers. Once I have navigated to the problem view, the other toolbars in other view begin exhibiting the same behavior. This is only happening in the simulator, not on my physical device. However, I currently have only an iPhone 4 as my physical device. Is this a bug in the simulator or an indication of an issue somewhere? THanks!
You are setting the y position to a hardcoded value of 438. This will result in it not appearing at the bottom of the screen for the taller iPhone 5's). You should calculate the y position as well perhaps with:
CGRectGetMaxY(self.view.bounds) - toolbarHeight
this would result in:
[self.navigationController.toolbar setFrame:CGRectMake(CGRectGetMinX(self.view.bounds),
CGRectGetMaxY(self.view.bounds) - toolbarHeight,
CGRectGetWidth(self.view.bounds),
toolbarHeight)];
It could also have to do with the presence of a navigation bar and the status bar

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