my problem is related with UIScrollview. lets describe it,
I have signup screen which has a scrollview, initially scrolling is not enabled. when keyboard appears I will enable scrollview and when keyboard hides again I am disabling scrolling. my scrollview’s width and height is same as its default view, I have applied horizontal, and vertical centre in container as well as top, bottom, leading and trailing edges are zero (i.e. it is equal to default view). I have a signup button which navigates to signup screen, and applied bottom constraints (bottom space constraints = 0), also I m using keyboard notification for appear and hide.
Actual Problem:
when tap textfield keyboard appears, scrollview scrolls, and when I dismiss the keyboard the scrollview came down, but this time the signup button will move little bit up (like bottom space has 20 points constraints).
First time its like scrollview starts after status bar, but when keyboard appears and hides its like scrollview is renders over view including status bar.
Do I need to add any constraints related to Top/Bottom Layout Guide in IB?
or do I need to add any constraints related to in viewDidLoad
Code for Keyboard notification.
-(void)keyboardWillShow:(NSNotification *)notification {
[self.navigationController.navigationBar setBackgroundImage:nil
forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage = nil;
self.ContentScrollView.scrollEnabled=YES;
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardFrameInWindow;
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow];
// the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view
CGRect keyboardFrameInView = [self.ContentScrollView convertRect:keyboardFrameInWindow fromView:nil];
CGRect scrollViewKeyboardIntersection = CGRectIntersection(self.ContentScrollView.frame, keyboardFrameInView);
UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0);
// this is an old animation method, but the only one that retains compatibility between parameters (duration, curve) and the values contained in the userInfo-Dictionary.
[UIView animateWithDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue] delay:0.0 options:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue] animations:^{
self.ContentScrollView.contentInset = newContentInsets;
self.ContentScrollView.scrollIndicatorInsets = newContentInsets;
/*
* Depending on visual layout, _activeField should either be the input field (UITextField,..) or another element
* that should be visible, e.g. a purchase button below an amount text field
* it makes sense to set _activeField in delegates like -textFieldShouldBeginEditing: if you have multiple input fields
*/
if (_activeField) {
CGRect controlFrameInScrollView = [self.ContentScrollView convertRect:_activeField.bounds fromView:_activeField]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview
controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, 0); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view.
CGFloat controlVisualOffsetToTopOfScrollview = (controlFrameInScrollView.origin.y - self.ContentScrollView.contentOffset.y)+10;
CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height;
// this is the visible part of the scroll view that is not hidden by the keyboard
CGFloat scrollViewVisibleHeight = self.ContentScrollView.frame.size.height - scrollViewKeyboardIntersection.size.height;
if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question
// scroll up until the control is in place
CGPoint newContentOffset = self.ContentScrollView.contentOffset;
newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight);
// make sure we don't set an impossible offset caused by the "nice visual offset"
// if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies
CGFloat maxScrollViewHeight = MAX(self.ContentScrollView.frame.size.height, self.ContentScrollView.contentSize.height);
newContentOffset.y = MIN(newContentOffset.y, maxScrollViewHeight - scrollViewVisibleHeight);
[self.ContentScrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
} else if (controlFrameInScrollView.origin.y < self.ContentScrollView.contentOffset.y) {
// if the control is not fully visible, make it so (useful if the user taps on a partially visible input field
CGPoint newContentOffset = self.ContentScrollView.contentOffset;
newContentOffset.y = controlFrameInScrollView.origin.y;
[self.ContentScrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
}
}
} completion:NULL];
}
- (void)keyboardWillHide:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
[UIView animateWithDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey]doubleValue ] delay:0.01 options:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey]intValue] animations:^{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.ContentScrollView.contentInset = contentInsets;
self.ContentScrollView.scrollIndicatorInsets = contentInsets;
CGPoint scrollPoint;
self.ContentScrollView.scrollEnabled=NO;
scrollPoint = CGPointMake(0.0, 0.0);
[self.ContentScrollView setContentOffset:scrollPoint animated:YES];
} completion:^(BOOL finished){
__weak typeof(self) weakSelf=self;
[weakSelf.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
weakSelf.navigationController.navigationBar.shadowImage = [UIImage new];
}];
}
Image
If further required I will send the screen shots of before and after keyboard notification.
Thank You.
From Documentation
A Boolean value that indicates whether the view controller should
automatically adjust its scroll view insets.
Declaration
#property(nonatomic, assign) BOOL automaticallyAdjustsScrollViewInsets
Discussion
Default value is YES, which allows the view controller to adjust its
scroll view insets in response to the screen areas consumed by the
status bar, navigation bar, and toolbar or tab bar. Set to NO if you
want to manage scroll view inset adjustments yourself, such as when
there is more than one scroll view in the view hierarchy.
Write self. automaticallyAdjustsScrollViewInsets=NO; in viewDidLoad and try
Related
I created new Xcode single view project. In interface builder, I added a UIScrollView to cover complete view. Over this scrollview, I added a UITextField. The resulting UI looked like this:
Note that the scrollview does not scroll at this point as the content takes only the size of view and not greater than it.
Now for bringing UITextField on top of keyboard while editing, I followed the way described by Apple on Managing The Keyboard page. After doing this, it gave me expected behavior which brought the text field right above keyboard on editing begin as shown in following screenshot:
Now, after calling [textfield endEditing:YES], the keyboard hides itself, but the textfield does not return to its original place. It return to the place just little above its original place and now the scroll view becomes scrollable as if little height was added to it:
Notice the scroll bar in the above screenshot!
I want help in bring back the original behavior of the view after editing ends (when keyboard hides) i.e. the textField to return to exact same place and scroll should be happening as it was not happening before editing begin.
Project URL: - https://github.com/therohansanap/keyboard.git
You need adjust scrollview contentOffset textFieldDidBeginEditing and textFieldDidEndEditing.
or
One controller is available for scrollview auto scroll.
https://github.com/simonbs/BSKeyboardControls
I think the official way specified by Apple here is easiest and best way to keep this functionality working.
You can do something similar without Using Keyboard notifications as well. As you may know that we have TextField delegate methods , we can use those to set scrollView contentOffset and acquire the same behaviour
- (void)textFieldDidBeginEditing:(UITextField *)textField{
scrollView.contentOffset = CGPointMake(0, textField.center.y-80); // you can change 80 to whatever which fits your needs
}
the above method sets contentOffset Value of scroll view and your textFiled moves up, while the textField resignFirstResponder the below delegate method gets called, where you can set back the contentOffset value
- (void)textFieldDidEndEditing:(UITextField *)textField{
scrollView.contentOffset = CGPointMake(0,-80);
}
Note: you need to make every text field in your view to have their delegate as your UIViewController instance. You also need need your UIViewController to adopt UITextFieldDelegate
Took a look at your code, you dont need to change the content insets etc when trying to position the scroll view. You just need to modify the content offset property.
Here is the modified code :
#interface ViewController () {
UITextField *activeField;
CGPoint scrollViewOldPosition;
}
Modify the keyboardWasShow as follows :
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGFloat someSpaceBetweenKeyBoardAndField = 20.0;
scrollViewOldPosition = self.scrollView.contentOffset;
self.scrollView.contentOffset = CGPointMake(0, kbSize.height - (self.view.frame.size.height - activeField.frame.origin.y - activeField.frame.size.height) + someSpaceBetweenKeyBoardAndField);
}
Keyboard will be hidden method :
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
self.scrollView.contentOffset = scrollViewOldPosition;
}
Not the best code ever but it have some more features u can use, anything with _ is global variable
//Handle notification when keyboard appear
- (void)keyboardOnScreen:(NSNotification *)notification
{
if (_isKeyboardShow) {
return; //If keyboard is showing then return
}
_keyboardHeight = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
[self animateTextFieldUp: YES];
_isKeyboardShow = YES;
}
//Handle notification when keyboard hide
- (void)keyboardOffScreen:(NSNotification *)notification
{
if(!_isKeyboardShow) return;
[self animateTextFieldUp: NO];
_isKeyboardShow = NO;//Missed this line
}
//Push view up with animation when keyboard show
- (void) animateTextFieldUp: (BOOL) up
{
UITextField *textfield = [UIResponder currentFirstResponder];
CGPoint windowPoint = [textfield convertPoint:textfield.bounds.origin toView:self.view];
int movementDistance;
CGPoint point = [_mainScrollView contentOffset];
//Push up only when blocked by keyboard
if (windowPoint.y + textfield.frame.size.height >= self.view.frame.size.height - _keyboardHeight) {
movementDistance = windowPoint.y - (self.view.frame.size.height - _keyboardHeight) + textfield.frame.size.height + 10;
_oldMovementDistance = movementDistance;
int movement = (up ? -movementDistance : movementDistance);
[_mainScrollView setContentOffset:CGPointMake(0, point.y - movement) animated:YES];
}
else { //Push view down the same amount
int movement = (up ? -movementDistance : _oldMovementDistance);
[_mainScrollView setContentOffset:CGPointMake(0, point.y - movement) animated:YES];
_oldMovementDistance = 0;
}
}
move UITextField and UITextView out of the way of the keyboard during editing:
For non-UITableViewControllers, drop the TPKeyboardAvoidingScrollView.m and TPKeyboardAvoidingScrollView.h source files into your project, pop a UIScrollView into your view controller's xib or storyboard, set the scroll view's class to TPKeyboardAvoidingScrollView, and put all your controls within that scroll view. You can also create it programmatically, without using a xib - just use the TPKeyboardAvoidingScrollView as your top-level view.
For use with UITableViewController classes, drop TPKeyboardAvoidingTableView.m and TPKeyboardAvoidingTableView.h into your project, and make your UITableView a TPKeyboardAvoidingTableView in the xib. If you're not using a xib with your controller, I know of no easy way to make its UITableView a custom class: The path of least resistance is to create a xib for it.
You can get reference from here.
Hope this helps.
I am trying to make a UITableView appear when a user taps on a button and disappear when the button is re-tapped.
I implemented the following code but nothing seems to appear when I tap the button.
- (IBAction)dropDown:(UIButton *)sender
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.6];
CGAffineTransform transfrom = CGAffineTransformMakeTranslation(0, 200);
self.markersTableView.transform = transfrom;
self.markersTableView.alpha = self.markersTableView.alpha * (-1) + 1;
[UIView commitAnimations];
}
What may potentially be the issue?
EDIT:
I was able to make the UITableView appear and disappear by adding self.markersTableView.hidden = YES; in viewDidLoad() and self.markersTableView.hidden = NO; in the IBAction method.
However, the table view disappears when I initially tap on the button as shown in the screenshot:
The fading of the rows is an indication it is moving down the screen, and then it disappears.
It only reappears when I re-tap on the UIButton the 2nd time.
Any clues?
Don't use a transform on your table view. That will make it LOOK like it's in a different position, but it won't respond to taps correctly.
Instead, use the newer UIView block-based animation methods and change the view's center.
The code might look like this (if you're NOT using auto-layout)
[UIView AnimateWithDuration: .2
animations: ^
{
CGPoint center = self.markersTableView.center;
center.y += 200;
self.markersTableView.center = center;
}
];
If you're using auto-layout, though, that won't work and you will need to animate the view's position using constraints.
From memory, here's an outline of how to do auto-layout based animations: You would create a vertical position constraint on your table view and connect the constraint to an IBOutlet in your view controller. In your animation code you change the constant of the constraint (the vertical position) and then in the animation block, send the view a needsLayout message.
In an iPhone layout, I am hiding the UINavigationBar and at the same time I want the content of the UIScrollView beneath it to stay in the same place (scroll negatively while animating the nav bar height to zero):
[UIView animateWithDuration:0.3 animations:^{
CGFloat navBarHeight = CGRectGetHeight(weakSelf.navigationController.navigationBar.frame);
[[weakSelf navigationController] setNavigationBarHidden:YES animated:NO];
weakSelf.scrollView.bounds = CGRectOffset(weakSelf.scrollView.bounds, 0, -navBarHeight);
}];
The end result of the animation is ok - but during the animation, the scroll view jumps to its new position (- navbar height) and remains there until the end.
Do these two property animations interfere with each other?
Has anyone tried using a UISearchDisplayController with a UISearchBar that is a UIBarButtonItem in a UIToolbar?
I would appreciate tips on how to do this successfully.
Currently whenever the search controller pops up it redraws the UISearchBar and I'm struggling to maintain a similar look to the UIToolbar
Usually you don't want to put a search bar inside a toolbar, however, it seems you want to do something similar to what I did.
So here is how I did it, it may be called a hack, but it works like a charm :)
First you have to set it up in interface builder like this:
Notice that the search is not a child of toolbar, instead it is above.
The search bar should have "clear color" background and flexible left, right and width autoresizing masks.
You put a 1-pixel label with black background below the toolbar, ie. [x=0, y=44, width=320 or frame width, height=1], also flexible left, right and width autoresizing masks.This is to hide the one visible pixel you get, after the search display controller shows the table view. Try it without it to understand what I mean.
You setup any tool bar items and be sure to have outlets for them, since you will be needing those.
and now for the code ...
when you start searching:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
// animate the search bar to the left ie. x=0
[UIView animateWithDuration:0.25f animations:^{
CGRect frame = controller.searchBar.frame;
frame.origin.x = 0;
controller.searchBar.frame = frame;
}];
// remove all toolbar items
[self.toolbar setItems:nil animated:YES];
}
when you end searching
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
// animate search bar back to its previous position and size
// in my case it was x=55, y=1
// and reduce its width by the amount moved, again 55px
[UIView animateWithDuration:0.25f
delay:0.0f
// the UIViewAnimationOptionLayoutSubviews is IMPORTANT,
// otherwise you get no animation
// but some kind of snap-back movement
options:UIViewAnimationOptionLayoutSubviews
animations:^{
CGRect frame = self.toolbar.frame;
frame.origin.y = 1;
frame.origin.x = 55;
frame.size.width -= 55;
controller.searchBar.frame = frame;
}
completion:^(BOOL finished){
// when finished, insert any tool bar items you had
[self.toolbar setItems:[NSArray arrayWithObject:self.currentLocationButton] animated:YES];
}];
}
With this I get the following with a nice animation :)
I have standard iPad view controller which has a custom navigation bar at the top. In the xib-file I've added a UISearchBar aligned to the right edge of the view. The search bar is 320px in width. I init a searchdisplaycontroller like this:
// Search display controller
self.mySearchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:_searchBar
contentsController:self];
_mySearchDisplayController.delegate = self;
_mySearchDisplayController.searchResultsDataSource = self;
_mySearchDisplayController.searchResultsDelegate = self;
The problem is that when I press the search bar, the bar resizes to be the full width of the entire view, but keeps its x-position. This means that it stretches far outside the screen. I'm guessing it has something to do with the "Cancel" button that slides in next to a search bar. If I place the search bar to the far left in the screen, it animates to the full width of the screen and the cancel button is visible.
Anyone has a solution for this?
You can animate the frame of your UISearchBar in the searchDisplayControllerWillBeginSearch method to correct its position like this:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
// animate the search bar to the left ie. x=0
[UIView animateWithDuration:0.25f animations:^{
CGRect frame = controller.searchBar.frame;
frame.origin.x = 0;
controller.searchBar.frame = frame;
}];
// remove all toolbar items if you need to
[self.toolbar setItems:nil animated:YES];
}
and the animate it back again when finished searching:
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
// animate search bar back to its previous position and size
// in my case it was x=55, y=1
// and reduce its width by the amount moved, again 55px
[UIView animateWithDuration:0.25f
delay:0.0f
// the UIViewAnimationOptionLayoutSubviews is IMPORTANT,
// otherwise you get no animation
// but some kind of snap-back movement
options:UIViewAnimationOptionLayoutSubviews
animations:^{
CGRect frame = self.toolbar.frame;
frame.origin.y = 1;
frame.origin.x = 55;
frame.size.width -= 55;
controller.searchBar.frame = frame;
}
completion:^(BOOL finished){
// when finished, insert any tool bar items you had
[self.toolbar setItems:[NSArray arrayWithObjects: /* put your bar button items here */] animated:YES];
}];
}
I have answered a similar question, you can check it here, I've put some images too.
The only thing you have to do is to adapt the code for the iPad.