What I mean by floating view is custom view that is a subview (or appears to be a subview) of scrollview which scrolls along until it anchors on certain point. Similar behavior would be UITableView's section header. Attached image below
My content view (the view underneath the floating view) is not in tableview layout. Meaning if I use tableview only for the floating view, I have to put my content view inside 1 giant cell or break it to several cells with different layouts. The content view will have a lot of dynamic elements which is why I don't want to put it inside UITableViewCell unless I have to. Can I make floating view programmatically / using autolayout on scrollview?
Using the tableview section header is probably the best solution, you can always easily customise the number of cells or cells themselves to achieve a particular layout.
However if you definitely don’t want to deal with a tableview, this component seems really cool, it's actually is meant to be added to a tableview, but I tested it with the twitter example and you can actually add it to a scrollview, so you don’t need a table view and it will work, give props the guy who made it. GSKStretchyHeaderView
Hope this helps, comment if you have any questions, good luck.
Use KVO to update the floating view's frame.
Here is the sample code written in Objective-C:
// ScrollView.m
// ScrollView is a subclass of UIScrollView
#interface ScrollView ()
#property (nonatomic, strong) UIView *floatingView;
#property (nonatomic) CGRect originalBorderFrame;
#property (nonatomic) CGFloat anchorHeight;
#end
#implementation ScrollView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.floatingView = [UIView new];
self.floatingView.backgroundColor = [UIColor colorWithRed:0.8211 green:0.5 blue:0.5 alpha:1.0];
self.floatingView.frame = CGRectMake(0, 150, frame.size.width, 20);
self.originalBorderFrame = self.floatingView.frame;
[self addSubview:self.floatingView];
self.anchorHeight = 44;
[self addObserver:self forKeyPath:#"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
- (void)dealloc {
[self removeObserver:self forKeyPath:#"contentOffset"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([keyPath isEqualToString:#"contentOffset"]) {
if (self.contentOffset.y > self.originalBorderFrame.origin.y-self.anchorHeight) {
self.floatingView.frame = CGRectOffset(self.originalBorderFrame, 0, self.contentOffset.y - (self.originalBorderFrame.origin.y-self.anchorHeight));
}
}
}
#end
Here is the capture:
I have a view controller that can be popped with the new interactivePopGestureRecognizer. If there is a keyboard present and the swipe animation begins the keyboard does not move with the view. I have had a look at this question and implemented it like this in my view controller that gets dismissed
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.transitionCoordinator animateAlongsideTransitionInView:self.aTextInputView.keyboardSuperView animation:^(id<UIViewControllerTransitionCoordinatorContext> context) {
CGRect frame = self.aTextInputView.keyboardSuperView.frame;
frame.origin.x = self.view.frame.size.width;
self.aTextInputView.keyboardSuperView.frame = frame;
} completion:nil];
}
Now what I get when the view animates to disappear is the keyboard animates off the screen to the x point of 320 which makes sense as thats what I set it to, my question is how do I get the keyboard to animate with the swipe back?
Update
For any one that sees a weird animation when the view disappears you can get remove the keyboard by doing this.
[self.transitionCoordinator notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context){
if (![context isCancelled]) {
[keyboardSuperview removeFromSuperview];
}
}];
You have a lot of custom code in your snippet, so correct me if I am wrong, but it seems you have incorrect self.aTextInputView.keyboardSuperView.
Double check that it is not nil. If it is, you forgot to add an inputAccessoryView.
Here is the full code snippet without any extensions:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
UIView *keyboardSuperview = self.textField.inputAccessoryView.superview;
[self.transitionCoordinator animateAlongsideTransitionInView:keyboardSuperview
animation:
^(id<UIViewControllerTransitionCoordinatorContext> context) {
CGRect keyboardFrame = keyboardSuperview.frame;
keyboardFrame.origin.x = self.view.bounds.size.width;
keyboardSuperview.frame = keyboardFrame;
}
completion:nil];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.textField.inputAccessoryView = [[UIView alloc] init];
}
Just found really simple solution for iOS8
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.aTextInputView resignFirstResponder];
}
How is that possible that my editable UITextView (placed inside a straightforward UIViewController inside a UISplitView that acts as delegate for the UITextView) is not showing text from the beginning but after something like 6-7 lines?
I didn't set any particular autolayout or something similar, trying to delete text doesn't help (so no hidden chars or something).
I'm using iOS 7 on iPad, in storyboard looks good...
The problem is the same on iOS simulator and real devices. I'm getting mad :P
Here's some code. This is the ViewController viewDidLoad()
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.itemTextField.delegate = self;
self.itemTextField.text = NSLocalizedString(#"NEWITEMPLACEHOLDER", nil);
self.itemTextField.textColor = [UIColor lightGrayColor]; //optional
}
And here are the overridden functions for the UITextView I'm using some code I've found on StackOverflow to simulate a placeholder for the view (the same stuff on iPhone version of the storyboard works fine)...
// UITextView placeholder
- (void)textViewDidBeginEditing:(UITextView *)textView
{
if ([textView.text isEqualToString:NSLocalizedString(#"NEWITEMPLACEHOLDER", nil)]) {
textView.text = #"";
textView.textColor = [UIColor blackColor]; //optional
}
[textView becomeFirstResponder];
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
if ([textView.text isEqualToString:#""]) {
textView.text = NSLocalizedString(#"NEWITEMPLACEHOLDER", nil);
textView.textColor = [UIColor lightGrayColor]; //optional
}
[textView resignFirstResponder];
}
-(void)textViewDidChange:(UITextView *)textView
{
int len = textView.text.length;
charCount.text = [NSString stringWithFormat:#"%#: %i", NSLocalizedString(#"CHARCOUNT", nil),len];
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
return YES;
}
Try to call -sizeToFit after passing the text. This answer could be useful to Vertically align text within a UILabel.
[UPDATE]
I update this answer o make it more readable.
The issue is that from iOS7, container view controllers such as UINavigationController or UITabbarController can change the content insets of scroll views (or views that inherit from it), to avoid content overlapping. This happens only if the scrollview is the main view or the first subviews. To avoid that you should disable this behavior by setting automaticallyAdjustsScrollViewInsets to NO, or overriding this method to return NO.
I got through the same kind of issue.
Solved it by disabling the automatic scrollView insets adjustement :
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")){
self.automaticallyAdjustsScrollViewInsets = NO; // Avoid the top UITextView space, iOS7 (~bug?)
}
This is a fairly common problem, so I would create a simple UITextView subclass, so that you can re-use it and use it in IB.
I would used the contentInset instead, making sure to gracefully handle the case where the contentSize is larger than the bounds of the textView
#interface BSVerticallyCenteredTextView : UITextView
#end
#implementation BSVerticallyCenteredTextView
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
[self addObserver:self forKeyPath:#"contentSize" options: (NSKeyValueObservingOptionNew) context:NULL];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self addObserver:self forKeyPath:#"contentSize" options: (NSKeyValueObservingOptionNew) context:NULL];
}
return self;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"contentSize"])
{
UITextView *tv = object;
CGFloat deadSpace = ([tv bounds].size.height - [tv contentSize].height);
CGFloat inset = MAX(0, deadSpace/2.0);
tv.contentInset = UIEdgeInsetsMake(inset, tv.contentInset.left, inset, tv.contentInset.right);
}
}
- (void)dealloc
{
[self removeObserver:self forKeyPath:#"contentSize"];
}
#end
use -observerForKeyPath with contentSize KeyPath
Look some code at My Blog (don't focus on Thai Language)
http://www.macbaszii.com/2012/10/ios-dev-uitextview-vertical-alignment.html
Inspired by Kiattisak, I've implemented vertical alignment as a category over UITextView so that you can control the vertical alignment of legacy UITextView.
You can find it as a gist here.
I had the same issue with iOS 8.1, and none of these suggestions worked.
What did work was to go into the Storyboard, and drag my UITableView or UITextView so that it was no longer the first subview of my screen's UIView.
http://www.codeproject.com/Tips/852308/Bug-in-XCode-Vertical-Gap-Above-UITableView
It seems to be linked to having a UIView embedded in a UINavigationController.
Bug ? Bug ? Did I say "bug" ...?
;-)
Swift version of Tanguy.G's answer:
if(UIDevice.currentDevice().systemVersion >= "7.0") {
self.automaticallyAdjustsScrollViewInsets = false; // Avoid the top UITextView space, iOS7 (~bug?)
}
Check top content inset of textView in -viewDidLoad:
NSLog(#"NSStringFromUIEdgeInsets(self.itemTextField.contentInset) = %#", NSStringFromUIEdgeInsets(self.itemTextField.contentInset));
Reset it in storyboard if it is not zero
I realize that this is the inverse of most posts, but I would like for the keyboard to remain up even if the 'keyboard down' button is pressed.
Specifically, I have a view with two UITextFields. With the following delegate method
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
return NO;
}
I am able to keep the keyboard up even if the user presses the Done button on the keyboard or taps anywhere else on the screen EXCEPT for that pesky keyboard down button on the bottom right of the keyboard.
I am using this view like a modal view (though the view is associated with a ViewController that gets pushed in a UINavigationController), so it really works best from a user perspective to keep the keyboard up all of the time. If anyone knows how to achieve this, please let me know! Thanks!
UPDATE Still no solution! When Done is pressed, it triggers textFieldShouldReturn, but when the Dismiss button is pressed, it triggers textFieldDidEndEditing. I cannot block the textField from ending editing or it never goes away. Somehow, I really want to have a method that detects the Dismiss button and ignores it. If you know a way, please enlighten me!
There IS a way to do this. Because UIKeyboard subclasses UIWindow, the only thing big enough to get in UIKeyboard's way is another UIWindow.
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(coverKey) name:UIKeyboardDidShowNotification object:nil];
[super viewDidLoad];
}
- (void)coverKey {
CGRect r = [[UIScreen mainScreen] bounds];
UIWindow *myWindow = [[UIWindow alloc] initWithFrame:CGRectMake(r.size.width - 50 , r.size.height - 50, 50, 50)];
[myWindow setBackgroundColor:[UIColor clearColor]];
[super.view addSubview:myWindow];
[myWindow makeKeyAndVisible];
}
This works on iPhone apps. Haven't tried it with iPad. You may need to adjust the size of myWindow. Also, I didn't do any mem management on myWindow. So, consider doing that, too.
I think I've found a good solution.
Add a BOOL as instance variable, let's call it shouldBeginCalledBeforeHand
Then implement the following methods:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
shouldBeginCalledBeforeHand = YES;
return YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
return shouldBeginCalledBeforeHand;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
shouldBeginCalledBeforeHand = NO;
}
As well as
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
return NO;
}
to prevent the keyboard from disappearing with the return button. The trick is, a focus switch from one textfield to another will trigger a textFieldShouldBeginEditing beforehand. If the dismiss keyboard button is pressed this doesn't happen. The flag is reset after a textfield has gotten focus.
Old not perfect solution
I can only think of a not perfect solution. Listen for the notification UIKeyboardDidHideNotification and make of the textfields first responder again. This will move the keyboard out of sight and back again. You could keep record of which textfield was the last firstResponder by listening for UIKeyboardWillHideNotification and put focus on it in the didHide.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidHide:)
name:UIKeyboardDidHideNotification
object:nil];
...
- (void)keyboardDidHide:(id)sender
{
[myTextField becomeFirstResponder];
}
For iOS 9/10 and Swift 3, use this to create a rect which overlaps the "Hide keyboard" - Button
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(coverKey), name: .UIKeyboardDidShow, object: nil)
}
func coverKey() {
if let keyboardWindow = UIApplication.shared.windows.last {
let r = UIScreen.main.bounds
let myWindow = UIWindow.init(frame: CGRect(x: r.size.width - 50 , y: r.size.height - 50, width: 50, height: 50))
myWindow.backgroundColor = UIColor.clear
myWindow.isHidden = false
keyboardWindow.addSubview(myWindow)
keyboardWindow.bringSubview(toFront: myWindow)
}
}
Notice that this adds a sub view to the keyboard window instead of the main window
Try adding a custom on top of the keyboard dismiss button so that the user won't be able to tab the dismiss button. I have used this method in one of my application.
- (void)addButtonToKeyboard {
// create custom button
UIButton *blockButton = [UIButton buttonWithType:UIButtonTypeCustom];
blockButton.frame = //set the frame here, I don't remember the exact frame
[blockButton setImage:[UIImage imageNamed:#"block_button.png"] forState:UIControlStateNormal];
// locate keyboard view
UIWindow *appWindows = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
UIView *keyboard;
for (int i=0; i<[appWindows.subviews count]; i++) {
keyboard = [appWindows.subviews objectAtIndex:i];
// keyboard found, add the button
if ([[keyboard description] hasPrefix:#"<UIPeripheralHost"] == YES && [self.textField isFirstResponder]) {
[keyboard addSubview:doneButton];
}
}
}
Try this...
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
return NO;
}
You can use notification as mentioned by Nick Weaver.
I have an app where, in Interface Builder, I set up a UIView that has a text field near the bottom of the view. When I run the app and try to enter text into that field, the keyboard slides up overtop of the field so I can't see what I'm typing until I hide the keyboard again.
Has anyone else run into this problem and found a good way to solve it without either making the parent view scrollable or moving the text field farther up the screen?
The usual solution is to slide the field (and everything above it) up with an animation, and then back down when you are done. You may need to put the text field and some of the other items into another view and slide the view as a unit. (I call these things "plates" as in "tectonic plates", but that's just me). But here is the general idea if you don't need to get fancy.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
This worked wonders for me sliding uitextfields
In particular it has the benefit of calculating the slide animation distance depending on the position of the text field.
IQKeyboardManager do this for you with NO LINE OF CODE, only need to drag and drop related source file to project. IQKeyboardManager also support Device Orientation, Automatic UIToolbar Management, keyboardDistanceFromTextField and much more than you think.
Here is the Control Flow Chart:
Step1:- Added global notifications of UITextField, UITextView, and UIKeyboard in a singleton class. I called it IQKeyboardManager.
Step2:- If found UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotification or UITextViewTextDidBeginEditingNotification notifications, then try to get topMostViewController instance from the UIWindow.rootViewController hierarchy. In order to properly uncover UITextField/UITextView on it, topMostViewController.view's frame needs to be adjusted.
Step3:- Calculated expected move distance of topMostViewController.view with respect to first responded UITextField/UITextView.
Step4:- Moved topMostViewController.view.frame up/down according to the expected move distance.
Step5:- If found UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotification or UITextViewTextDidEndEditingNotification notification, then again try to get topMostViewController instance from the UIWindow.rootViewController hierarchy.
Step6:- Calculated disturbed distance of topMostViewController.view which needs to be restored to it's original position.
Step7:- Restored topMostViewController.view.frame down according to the disturbed distance.
Step8:- Instantiated singleton IQKeyboardManager class instance on app load, so every UITextField/UITextView in the app will adjust automatically according to the expected move distance.
That's all
To expand on Amagrammer answer, here is a sample class:
LoginViewController.h
#interface LoginViewController : UIViewController <UITextFieldDelegate> {
}
#property (nonatomic, retain) IBOutlet UITextField *emailTextField;
#property (nonatomic, retain) IBOutlet UITextField *passwordTextField;
Notice we are implementing the "UITextFieldDelegate"
LoginViewController.m
#implementation LoginViewController
#synthesize emailTextField=_emailTextField;
#synthesize passwordTextField=_passwordTextField;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
//Register to receive an update when the app goes into the backround
//It will call our "appEnteredBackground method
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(appEnteredBackground)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}
return self;
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
//This is called when the app goes into the background.
//We must reset the responder because animations will not be saved
- (void)appEnteredBackground{
[self.emailTextField resignFirstResponder];
[self.passwordTextField resignFirstResponder];
}
How about the official solution: Moving Content That Is Located Under the Keyboard
Adjusting your content typically involves temporarily resizing one or
more views and positioning them so that the text object remains
visible. The simplest way to manage text objects with the keyboard is
to embed them inside a UIScrollView object (or one of its subclasses
like UITableView). When the keyboard is displayed, all you have to do
is reset the content area of the scroll view and scroll the desired
text object into position. Thus, in response to a
UIKeyboardDidShowNotification, your handler method would do the
following:
Get the size of the keyboard.
Adjust the bottom content inset of your scroll view by the keyboard
height.
Scroll the target text field into view.
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your app might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
[self.scrollView scrollRectToVisible:activeField.frame animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
I have face the same issue in UITableView textField cells. I solve this issue by implementing following method to listen the keyboard notification.
Observer for the notifications here:
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(keyboardWasShown:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
Handle those notification by using below function:
(void)keyboardWasShown:(NSNotification*)aNotification
(void)keyboardWillBeHidden:(NSNotification*)aNotification
Check this out.
No hassle for you.
This solution is very neat. All you have to do is to add your textfields in a UIScrollView and change its class to TPKeyboardAvoidingScollView, if you are using storyboards. The scroll view is extended in such a way that it would detect when keyboard is visible and will move itself above keyboard at a reasonable distance. It is perfect solution because its independent of your UIViewController. Every necessary thing is done within the the above mentioned class. Thanks Michael Tyson et all.
TPKeyboardAvoiding
Below is a swift version of Amagrammer's answer. Also, a variation using the UIKeyboardWillShowNotification event since I needed to know the keyboards size before moving the view out of the way.
var keyboardHeight:CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillChange:", name: UIKeyboardWillShowNotification, object: nil)
}
func textFieldDidBeginEditing(textField: UITextField) {
//keyboardWillChange (below) is used instead of textFieldDidBeginEditing because textFieldDidBeginEditing
//is called before the UIKeyboardWillShowNotification necessary to determine the keyboard height.
}
func textFieldDidEndEditing(textField: UITextField) {
animateTextField(false)
}
func animateTextField(textFieldUp:Bool) {
let movementDistance:CGFloat = keyboardHeight
let movementDuration = 0.3
let movement:CGFloat = (textFieldUp ? -movementDistance : movementDistance)
UIView.beginAnimations("anim", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration)
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
UIView.commitAnimations()
}
func keyboardWillChange(notification:NSNotification) {
let keyboardRect:CGRect = ((notification.userInfo![UIKeyboardFrameEndUserInfoKey])?.CGRectValue)!
keyboardHeight = keyboardRect.height
animateTextField(true)
}
There was a great walkthrough at editing textfields without obscuring (link dead now, here's a Wayback link: https://web.archive.org/web/20091123074029/http://acts-as-geek.blogspot.com/2009/11/editing-textfields-without-obscuring.html). It shows how to move an existing UIView onto a UIScrollView, and to scroll it automatically when the keyboard appears.
I've updated it a bit to calculate the correct height for the UIScrollView when there are controls (such as a UITabBar) below the UIScrollBar. See post updating uiview.
Here's a solution using Xcode5, iOS7:
Use the UITextfieldDelegate and animation blocks.
This is nearly all the code for the ViewController but I wanted to include the delegate code for those still somewhat unfamiliar with the delegate pattern (like me). I also included code to hide the keyboard when you tap away from the textview.
You can move the views(buttons, textfields, etc) as high as you'd like just make sure to put them back in place (+100 then later -100).
#interface ViewController () <UITextFieldDelegate>
#property (strong, nonatomic) IBOutlet UITextField *MyTextField;
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.MyTextField.delegate = self;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
NSLog(#"text began editing");
CGPoint MyPoint = self.MyTextField.center;
[UIView animateWithDuration:0.3
animations:^{
self.MyTextField.center = CGPointMake(MyPoint.x, MyPoint.y - 100);
}];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
NSLog(#"text ENDED editing");
CGPoint MyPoint = self.MyTextField.center;
[UIView animateWithDuration:0.3
animations:^{
self.MyTextField.center = CGPointMake(MyPoint.x, MyPoint.y + 100);
}];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
I guess one way would be to move your whole views position from (x,y) to (x,y-keybaardHeight) when the textfield is clicked and put it back when the keyboard is dismissed , might look a little odd as the view just comes up (maybe it wouldnt be bad if you animate it).
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
CGRect frame=self.view.frame;
frame.origin=CGPointMake(x...//set point here
self.view.frame=frame;
}
In addition to Amagrammer's solution, if you are using cocos2d in portrait mode change this line:
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
to this:
[CCDirector sharedDirector].openGLView.frame = CGRectOffset([CCDirector sharedDirector].openGLView.frame, movement, 0);
If you are using cocos2d in landscape mode, make the above change and switch the up values in textFieldDidBeginEditing: and textFieldDidEndEditing:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
[self animateTextField:textField up:NO];
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
[self animateTextField:textField up:YES];
}
I had the same problem and found GTKeyboardHelper to be an easy way out.
After drag and drop the framework in your project, include the header file.
Download and open the example project, then drag the "Keyboard Helper" object from the objects section in the xib to the objects section in your project's interface builder.
Drag and drop all your views to be children of the "Keyboard Helper".
Drag and drop framework that I use in my projects. Supports automatic dismissal when you tap outside of a first responder or when you scroll.
GTKeyboardHelper
Just slide the view up and down as needed :
- (void)textFieldDidEndEditing:(UITextField *)textField {
self.currentTextField = nil;
[self animateTextField: textField up: NO];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[self.currentTextField resignFirstResponder];
return YES;
}
- (void) animateTextField:(UITextField*) textField up:(BOOL)up {
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView animateWithDuration:movementDuration animations:^{
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
}];
}
Don't forget to set self as a UITextFieldDelegate and as the actual textField delegate.
(Thanks to Ammagrammer, this is just a shorter answer using blocks for animations)
I have something else if you want. The point here is that you want to set the center your UIView on the text field you are editing.
Before that, you have to save your INITIAL_CENTER, as a CGPoint, from self.view.center and your INITIAL_VIEW as a CGRect from self.view.frame in a const property.
You can create a method like this :
- (void) centerOn: (CGRect) fieldFrame {
// Set up the center by taking the original view center
CGPoint center = CGPointMake(INITIAL_CENTER.x,
INITIAL_CENTER.y - ((fieldFrame.origin.y + fieldFrame.size.height/2) - INITIAL_CENTER.y));
[UIView beginAnimations:#"centerViewOnField" context:nil];
[UIView setAnimationDuration:0.50];
if (CGRectEqualToRect(fieldFrame,INITIAL_VIEW)) {
self.view.frame = INITIAL_VIEW;
[self.view setCenter:INITIAL_CENTER];
} else {
[self.view setCenter:center];
}
[UIView commitAnimations];
}
Then, on your UITextFieldDelegate, you have to call centerOn:(CGRect) in following methods :
textFieldDidBeginEditing:(UITextField*) with, as a parameter, the frame of the text field you want to center on.
And you have to call it in your event handler, where you close your keyboard,
textFieldDidEndEditing:(UITextField*) can be one of the ways to do it, putting the INITIAL_VIEW as a parameter of centerOn:(CGRect).
I believe on newer versions of iOS (6.1+, possibly even earlier), the underlying view, at least for UITableView, auto-shrinks when the keyboard pops up. So you only need to make the text field visible in that view. In init:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
then:
- (void)keyboardWasShown:(NSNotification*)notification
{
// Scroll the text field into view so it's not under the keyboard.
CGRect rect = [self.tableView convertRect:inputView.bounds fromView:inputView];
[self.tableView scrollRectToVisible:rect animated:YES];
}
https://github.com/ZulwiyozaPutra/Shift-Keyboard-Example I hope this solution helped. They are all Swift 3 written.
//
// ViewController.swift
// Shift Keyboard Example
//
// Created by Zulwiyoza Putra on 11/23/16.
// Copyright © 2016 Zulwiyoza Putra. All rights reserved.
//
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
//connecting textfield from storyboard
#IBOutlet weak var textField: UITextField!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
subscribeToKeyboardNotifications()
}
override func viewDidAppear(_ animated: Bool) {
self.textField.delegate = self
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
unsubscribeFromKeyboardNotifications()
}
//Hide keyboard after finished editing
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
//Setup view before keyboard appeared
func keyboardWillAppear(_ notification:Notification) {
view.frame.origin.y = 0 - getKeyboardHeight(notification)
}
//Setup view before keyboard disappeared
func keyboardWillDisappear(_ notification: Notification) {
view.frame.origin.y = 0
}
//Getting keyboard height
func getKeyboardHeight(_ notification:Notification) -> CGFloat {
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue // of CGRect
return keyboardSize.cgRectValue.height
}
//Subscribing to notifications to execute functions
func subscribeToKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear(_:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear(_:)), name: .UIKeyboardWillHide, object: nil)
}
//Unsubscribing from notifications
func unsubscribeFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}
}