I have a UITableView with a search bar and a search result display controller:
[self.searchDisplayController.searchResultsTableView addSubview:searchIndicator];
searchIndicator.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
The code above places the indicator at the center of the screen. However I want to place the indicator at the center of the frame excluding the keyboard. How can I do this?
The indicator is defined like this:
#property (retain, nonatomic) IBOutlet UIActivityIndicatorView *searchIndicator;
#SaurabhPrajapati has the right idea in his comment. You'll need to subscribe to one of the keyboard willShow/didShow notifications (UIKeyboardDidShowNotification or
UIKeyboardWillShowNotification) and when you get a keyboard notification, collect information about the keyboard height from the Keyboard Notification User Info Keys (Search in the Xcode help system on that string for more information.) Save the keyboard height to an instance variable, and then when you get ready to display your activity indicator, use the keyboard height to adjust the position of the indicator as outlined in Saurabh's comment.
add this in viewDidLoad
//KeyBoard Helpers
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyBoardWillShow:) name:UIKeyboardWillShowNotification object:nil];
Then implement this method
-(void)keyBoardWillShow:(NSNotification*)notification
{
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyBoardHeight = keyboardFrame.size.height;
searchIndicator.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height - keyBoardHeight/ 2.0);
}
You could just add a constraint to the top of your UIActivityIndicatorView and inside the to notifications of your keyboard. When keyboard will be shown, you shrink it by the keyboard height, if keyboard will be hide, you add the keyboard height again to the top constraint.
Related
Consider this scenario, I have a textview with keyboard Dismiss interactively set in storyboard, so when user scroll down and able to dismiss keyboard interactively.
I have constraints on the textview to bottom to make sure it is always fully displayed on the view.
Current problem is, when user gradually scroll down to dismiss the keyboard, I can not detect the keyboard frame changes. I tried UIKeyboardWillHideNotification and UIKeyboardWillChangeFrameNotification, they were only called after the keyboard dismissed.
So my question is, how can we detect keyboard frame changes simultaneously when dismiss the keyboard interactively?
If you want to observe keyboard frame changes even when the keyboard is being dragged you can use this: https://github.com/brynbodayle/BABFrameObservingInputAccessoryView
Basically you create a placeholder input view for keyboard (which sticks to the keyboard all the time, even while dragging) and you observe it's frame changes. Those changes are being returned in a block, so you get current frame of the keyboard all the time.
You shouldn't change the textView height to fit all view. Instead - you should change contentInset field so your textView will stay the same height and you won't have to bother about tracking frame of the interactive keyboard.
See answer here:
How do I scroll the UIScrollView when the keyboard appears?
In your viewDidLoad method add these line:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
the add these methods to your viewController
- (void)keyboardWillShow:(NSNotification *)notification
{
NSDictionary *userInfo = [notification userInfo];
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
[UIView animateWithDuration:0.30
delay:0.0
options:(7 << 16) // This curve ignores durations
animations:^{
self.buttonsBottomConstraint.constant = keyboardSize.height - self.footerView.bounds.size.height + 4.0;
[self.view layoutIfNeeded];
}
completion:nil];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
[UIView animateWithDuration:0.30
delay:0.0
options:(7 << 16) // This curve ignores durations
animations:^{
self.buttonsBottomConstraint.constant = 0.0;
[self.view layoutIfNeeded];
}
completion:nil];
}
In my native iOS app, I have a screen that contains a simple textview. I need to adjust the size/frame of the text view when keyboard appears. I've succeeded it with UIKeyboardDidShowNotification as below:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myKeyBoardIsOnScreen:) name:UIKeyboardDidShowNotification object:nil];
And setting the frame on:
- (void)myKeyBoardIsOnScreen:(NSNotification*)notification {
NSDictionary* keyboardInfo = [notification userInfo];
NSValue* keyboardFrameBegin = [keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey];
CGRect keyboardFrameBeginRect = [keyboardFrameBegin CGRectValue];
self.textView.frame = CGRectMake(self.textView.frame.origin.x, self.textView.frame.origin.y, self.textView.frame.size.width, self.view.frame.size.height-keyboardFrameBeginRect.size.height-self.textView.frame.origin.y);
}
Problem: This looks ok for the first moment. But later I realised that the frame of the keyboard is with the height of its accessory view included. So when I hide the accessory view by dragging it down, the textview appears to be broken.
Hence can anyone suggest me any possible ways/delegates to identify the state of input accessory view of a textview's keyboard (like: Input accessory view is shown/hidden,etc.)
NB: I need the accessory view. Hence I don't need to remove it.
Register as an observer for the UIKeyboardDidChangeFrameNotification to update your view's frame.
Apple Docs
I have a UITextView (which is also a UIScrollView) which contains a bunch of text. In the screenshot there is more text underneath the keyboard. I can't scroll up to see that text - no matter what I do that text remains underneath the keyboard.
How can I fix things so that I can scroll to see all the text?
This works and it's pretty simple.
In .h
#property (weak, nonatomic) IBOutlet UITextView *tv;
#property CGSize keyboardSize;
In .m
- (void)viewDidLoad {
[super viewDidLoad];
// Register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void) keyboardWillShow: (NSNotification*) aNotification {
// Get the keyboard size from the notification userInfo
NSDictionary *info = [aNotification userInfo];
self.keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// Adjust the content inset from the bottom by the keyboard's height
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0, 0, self.keyboardSize.height, 0);
self.tv.contentInset = contentInsets;
}
- (void) keyboardWillHide: (NSNotification*) aNotification {
// Reset the content inset when the keyboard is dismissed
self.tv.contentInset = UIEdgeInsetsZero;
}
The scrollView has a property called contentSize which determines the area up to which the user can scroll. You would have to manually change this value to compensate the extra scroll space due to the keyboard.
What I suggest is to register for the notifications UIKeyboardWillHideNotification UIKeyboardWillShowNotification.
When the keyboard is about to show, the UIKeyboardWillShowNotification notification is fired and in the corresponding method add the keyboard height to the scroll contentSize height.
Similarly, deduct this height fom the scroll contentSize height in the UIKeyboardWillHideNotification notification.
Hope this helps! :)
To avoid all the manual resizing and stuff, I recommend using this great library - https://github.com/hackiftekhar/IQKeyboardManager. It will do all the hard work for you.
I had a iPad project, inside one of its view controllers, there are two TextFields inside a panel view (Views are build in Storyboard). What I would like to achieve is when any of those textfield become first responder (i.e. Keyboard Appears), the panel view will move up and if keyboard disappear it will move down to origin position.
Once I test move up, I found the view will automatically move back to origin position after keyboard disappear, but I didn't write any code to do that.
Code to move up:
- (void)moveUpTextFields {
if ([self.emailTextField isFirstResponder] || [self.passwordTextField isFirstResponder]) {
CGRect frame = self.textFieldPanel.frame;
frame.origin.y -= 50;
self.textFieldPanel.frame = frame;
}
}
So I would like to figure out how does that happen, and how should I achieve my goal (i.e. Move up if keyboard appear and back to original position)?
UPDATE:
To achieve my goals, it should use Keyboard Notification. Register these notifications in viewWillAppear
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
I did this in another project long time ago, just didn't remember it at the first place :(
You have to listen to the keyboard events in the Notification Center, and lucky you, this is a functionality almost every app needs, so check this :)
https://github.com/aceontech/KBKeyboardHandler
It does exactly what you need but I prefer if you would understand how it behaves (registering for system notifications).
It's also available through cocoapods if you want
pod 'KBKeyboardHandler'
OK, so the weird behaviour is because when Keyboard Appear or Disappear, it will relayout view controller's sub views and trigger -(void)viewDidLayoutSubviews and reposition view back to StoryBoard's position. If the view is created by code then this won't be a issue.
In addition to add notifications for KeyBoard, we also need to reposition the view in viewDidLayoutSubviews since the notification triggered before layout subviews.
- (void)keyboardWillShow:(NSNotification *)notification {
CGSize keyboardSize = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
CGFloat y = self.view.frame.size.height - keyboardSize.height - self.textFieldPanel.frame.size.height - 5;
CGRect frame = self.textFieldPanel.frame;
panelViewYOffset = frame.origin.y - y;
frame.origin.y -= panelViewYOffset;
// Since it will layout subviews in viewDidLayout, why we set frame here?
// I tried to move this line, turns out the panel will move up but the animation is gone
// So this line will have a nice liner animation curve but I don't why, may be someone can explain it.
self.textFieldPanel.frame = frame;
}
- (void)keyboardWillHide:(NSNotification *)notification {
CGRect frame = self.textFieldPanel.frame;
frame.origin.y += panelViewYOffset;
self.textFieldPanel.frame = frame;
panelViewYOffset = 0;
}
In viewDidLayoutSubviews:
- (void)viewDidLayoutSubviews {
CGRect frame = self.textFieldPanel.frame;
frame.origin.y -= panelViewYOffset;
self.textFieldPanel.frame = frame;
}
Like I mentioned, this approach only works for view from StoryBoard (Interface Builder), if self.textFieldPanel is created by code, then the code in viewDidLayoutSubviews should be removed.
I try to get a content editable UIWebView with a "normal" scrolling operation, so that when the text cursor is going to be hidden by the keyboard, the UIWebView scrolls to prevent that. I read the "Managing the Keyboard" document from iOS developer library and got no result, using the scrollView property of my UIWebView. I also found numerous tricks on the web, using ScrollView properties/methods or javascript commands but cannot obtain a normal scrolling operation, like any NSTextView does on MacOS for example. Do you know any solution to this problem?
[EDITED] Got the solution, and created a method fired by a NSTimer. To get the caret Y position, one should get the selection of the active element, and insert a dummy node. Then getting the node.offsetTop property gives the caretY. Do not forget the remove the node...
Ok, here is the way Apple does it in their examples (I no longer remember what project name that was):
1.You register for keyboard notifications(the keyboard send a notification every time it shows up or it gets hidden):
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
2.Get the keyboard's size (you are interested in the height) and scroll if that point is not contained in the view:
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// If active text field is hidden by keyboard, scroll it so it's visible
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
CGPoint point;
point = CGPointMake(0, activeTextField.frame.origin.y + activeTextField.frame.size.height);
if (!CGRectContainsPoint(aRect, point ))
{
CGPoint scrollPoint = CGPointMake(0.0, activeTextField.frame.origin.y+activeTextField.frame.size.height-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
}
Don't forget do reverse things when hiding keyboard and remove the observers if you push to another view controller.
This example is used with text fields on a UIScrollView but I'm sure you can easily adapt it.
Personally if I also manage the web side, I prefer to create the webpages 320px wide, set my webView sizeToFit, add it on top of a UIScrollView and manage the scrolling from there. For textFields, I simply get the container for the selected textField using javascript and get it's coordinates.