Uiview Atop the Keyboard Similar to Imessage App

UIView atop the Keyboard similar to iMessage App

I have been successful at solving the problem in quite an elegant manner (I think,...).

The code will be released on Github next week and linked to in this answer.

--

How it's done: I made the rotation work by choosing the inputAccessoryView-way of doing it.

Nomenclature:

  1. 'MessageInputView' is a UIView containing my 'GrowingUITextView' (it also contains a "Send" Button and the background image).
  2. 'ChatView' is the view that belongs to the ChatViewController that displays all the Chatbubbles and has my 'MessageInputView' docked at the bottom.
  3. 'keyboardAccessoryView' is an empty UIView sized: CGRect(0,0,0,0).

I needed to figure out how to have the MessageInputView stick around on the screen when the keyboard was dismissed. That was the tricky part. I did this by creating another view (keyboardAccessoryView) and had my GrowingUITextView use it as its inputAccessoryView. I retained the keyboardAccessoryView because I'd need the reference to it later on.

Then I remembered some of the stuff I did in my other attempt (animating the MessageInputView's frames around the screen whenever a keyboard notification arrived).

I added my MessageInputView as a subview to my ChatView (at the very bottom). Whenever it is activated and the willShow: methods is called by a keyboard notification, I manually animate the MessageInputView's frame to it's designated position up top. When the animation finishes and the completion block executes I remove the subview from the ChatView and add it to the keyboardAccessoryView. This causes another notification to be fired off because the keyboard is re-loaded EVERY time the inputAccessoryView's frame/bounds are changed!. You need to be aware of that and handle it appropriately!

When the keyboard is about to dismissed, I convert my MessageInputView's frame to my ChatView's coordinate system and add it as a subview. Thus it is removed from my keyboardAccessoryView. I then resize the keyboardAccessoryView's frame back to CGRect(0,0,0,0) because otherwise the UIViewAnimationDuration will not match! Then I allow the keyboard to be dismissed and I have my MessageInputView follow it from above and eventually dock at the bottom of the screen.

This is quite a lot of work for very little gain though.

--

Take care.

PS: If someone figures out an easier way to do it (perfectly) let me know.

Keyboard handling just like in Messages app in iOS 7

I made an open source lib for exactly this purpose. It works on iOS 7 and 8 and is set up to work as a cocoapod as well.

https://github.com/oseparovic/MessageComposerView

Here's a sample of what it looks like:

MessageComposerView

You can use a very basic init function as shown below to create it with screen width and default height e.g.:

self.messageComposerView = [[MessageComposerView alloc] init];
self.messageComposerView.delegate = self;
[self.view addSubview:self.messageComposerView];

There are several other initializers that are also available to allow you to customize the frame, keyboard offset and textview max height as well as some delegates to hook into frame changes and button clicks. See readme for more!

iMessage Style Receding Keyboard in an iOS App

This is an incomplete solution, however it should give you a good starting point.

Add the following ivars to your UIViewController:

CGRect        keyboardSuperFrame; // frame of keyboard when initially displayed
UIView * keyboardSuperView; // reference to keyboard view

Add an inputAccessoryView to your text controller. I created an small view to insert as the accessoryView:

accView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
accView.backgroundColor = [UIColor clearColor];
textField.inputAccessoryView = accView;

I added the above code to -(void)loadView

Register to receive UIKeyboardDidShowNotification and UIKeyboardDidHideNotification when view is loaded:

- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];
return;
}

Add methods to specified as the selectors for the notifications:

// method is called whenever the keyboard is about to be displayed
- (void)keyboardWillShow:(NSNotification *)notification
{
// makes keyboard view visible incase it was hidden
keyboardSuperView.hidden = NO;
return;
}
// method is called whenever the keyboard is displayed
- (void) keyboardDidShow:(NSNotification *)note
{
// save reference to keyboard so we can easily determine
// if it is currently displayed
keyboardSuperView = textField.inputAccessoryView.superview;

// save current frame of keyboard so we can reference the original position later
keyboardSuperFrame = textField.inputAccessoryView.superview.frame;
return;
}

Add methods to track touched and update keyboard view:

// stops tracking touches to divider
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect newFrame;
CGRect bounds = [[UIScreen mainScreen] bounds];

newFrame = keyboardSuperFrame;
newFrame.origin.y = bounds.size.height;

if ((keyboardSuperView.superview))
if (keyboardSuperFrame.origin.y != keyboardSuperView.frame.origin.y)
[UIView animateWithDuration:0.2
animations:^{keyboardSuperView.frame = newFrame;}
completion:^(BOOL finished){
keyboardSuperView.hidden = YES;
keyboardSuperView.frame = keyboardSuperFrame;
[textField resignFirstResponder]; }];
return;
}

// updates divider view position based upon movement of touches
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch * touch;
CGPoint point;
CGFloat updateY;

if ((touch = [touches anyObject]))
{
point = [touch locationInView:self.view];
if ((keyboardSuperView.superview))
{
updateY = keyboardSuperView.frame.origin.y;
if (point.y < keyboardSuperFrame.origin.y)
return;
if ((point.y > updateY) || (point.y < updateY))
updateY = point.y;
if (keyboardSuperView.frame.origin.y != updateY)
keyboardSuperView.frame = CGRectMake(keyboardSuperFrame.origin.x,
point.y,
keyboardSuperFrame.size.width,
keyboardSuperFrame.size.height);
};
};
return;
}

Disclaimers:

  • When resigning as first responded, the keyboard moves back to its original position before sliding off screen. To make dismissing the keyboard more fluid, you first need to create an animation to move the keyboard off of the screen and then hide the view. I'll leave this part as an exercise to the readers.
  • I've only tested this on the iOS 5 simulator and with an iPhone with iOS 5. I have not tested this with earlier versions of iOS.

The SlidingKeyboard project I created to test this concept is available from GitHub in the examples directory of BindleKit:

https://github.com/bindle/BindleKit

Edit: Updating example to address first disclaimer.

Move UIView at same rate when keyboard is displayed

You should be able to use the information provided in the UIKeyboardWillChangeFrameNotification to for this. The information in this notification tells you the beginning and ending frames for the keyboard, the type of animation curve, and the duration.

To calculate the point of the keyboard at any given time in the animation, you can either use this code from Jeff LaMarche, or use the CAMediaTimingFunction class and plug in the animation type (into function with name) and figure out where the keyboard should be at certain points in time.

Once you know where the keyboard will be at a given point in time, you can set up an animation for your UIView to move with the keyboard.



Related Topics



Leave a reply



Submit