Swift 3.1 chat interface keyboard effects

brief introduction

Recently written in the Swift project to achieve a chat interface, when dealing with the keyboard pops up a little trouble.

The trouble is how to deal with the relationship between the screen and keyboard after the keyboard pops up

After a death, finally made the desired effect, the effect is as follows:

Note: the original project is written in Swift 2.3, in order to write this blog, using Swift 3.1 to re implement the. Experience: a method name really shortened a lot,

Swift 3.1 chat interface keyboard effects

Analysis

Now, let me examine the history of sike.

One began to think of the two methods, one is the keyboard pops up disappear at the same time, the input field with a mobile keyboard, the keyboard is pop disappears, the whole screen with mobile keyboard, these two methods have shortcomings, let us discuss the classification:

1 the input bar moves with the keyboard

  • When the number of messages is small, the keyboard will not block the message
  • After the number of messages, the keyboard will block the screen in the keyboard position of the message
  • Each time a new message is sent, the user can’t see it in time (because it is blocked by the keyboard)

Conclusion: experience is not good

2 the screen moves with the keyboard

  • After a lot of news, can see the latest news on the screen
  • But when the message is small, because the keyboard to the entire view out of the screen, the user can not see the first few messages
  • When the message does not fill the entire screen, the keyboard to the top of the view, the bottom of the view will leave a blank

Conclusion: experience is not good

I do not send pictures of the above two cases, we have to make up their brains

So as obsessive-compulsive disorder, how can tolerate this kind of bad experience? Began Sike, the first reference to the daily use of most of the WeChat, QQ, WeChat, summed up the case in the QQ keyboard pop-up effect

  • Case 1: when the message is less (when the keyboard pops up will not block the message) chat interface does not move, when the keyboard is only the slider on the input bar, so as to ensure that the first few messages can be fully displayed
  • Situation two: the message is more but has not occupied the screen (when the keyboard will pop up to block part of the message), when the keyboard pops up on the slider, while the chat interface is also slippery. Note: the height of the slider on the input bar is the height of the keyboard, and the sliding distance on the chat interface is the height of the keyboard that may block the message
  • Case three: when the message is full or out of the screen, the keyboard pops up on the entire view

This also includes sending messages, the chat interface on the slide, to ensure that the last message is displayed above the keyboard processing.

If you do not make up the brain, directly pulled out his cell phone, with WeChat or QQ and goddess chat a day

Below, we give out code analysis:

layout

First import the SnapKit layout framework, the chat interface and input bar constraints

Because I am lazy, how to use Snapkit do not need

{toolBarView.snp.makeConstraints (make) in make.left.equalTo (view.snp.left) make.right.equalTo (view.snp.right) make.height.equalTo (toolBarHeight) make.bottom.equalTo (view.snp.bottom)} {chatTableView.snp.makeConstraints (make) in make.left.equalTo (view.snp.left) make.right.equalTo (view.snp.right) make.bottom.equalTo (toolBarView.snp.top) make.top.equalTo (view.snp.top).Offset (64)}

The bottom of the chat interface and the top of the input bar

Monitor

Monitor the keyboard pop up and disappear

NotificationCenter.default.addObserver (self, selector:, #selector (keyBoardWillShow (notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil NotificationCenter.default.addObserver (self), selector: #selector (keyBoardWillHide (notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: Nil)

When the keyboard pops up, will trigger keyBoardWillShow (notification:) method, the keyboard disappears, will trigger keyBoardWillHide (notification:) method, we have many complex logic, in order to achieve these two methods. In addition, Swift 3.1 version, a lot of ways to remove the NS prefix, so still using Swift 2.3 children’s shoes, in front of the NotificationCenter plus prefix can be NS.

The following highlights to achieve the effect of the above three situations

Effect

Pop animation

Want to view with the keyboard pop-up slide, you need to get the height of the keyboard and the keyboard pop-up animation time, here we get the following code:

Func keyBoardWillShow (notification: Notification) {let userInfo = as Dictionary value = let notification.userInfo! UserInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue let keyBoardRect = value.cgRectValue / let = keyBoardRect.size.height keyBoardHeight get keyboard height mKeyBoardHeight = keyBoardHeight / / get the time required for let duration pop-up keyboard = userInfo[UIKeyboardAnimationDurationUserInfoKey] NSNumber mKeyBoardAnimateDuration = duration.doubleValue as!}...

Then animate

Prior to the implementation of the input bar with the keyboard pop up, tried two kinds of writing:

  • Update frame
Var (animate:) (-> Void) {let = newFrame = CGRect (x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT - mKeyBoardHeight) self.toolBarView.frame = newFrame (withDuration: mKeyBoardAnimateDuration, UIView.animate} delay: 0, options: options, animations: animate)
  • Update constraint
Var (animate:) (-> Void) = {{self.toolBarView.snp.updateConstraints (closure: (make) in make.bottom.equalTo (self.view.snp_bottom).Offset (-mKeyBoardHeight)}} UIView.animate (withDuration: mKeyBoardAnimateDuration, delay: 0, options: options, animations: animate)

But in the end, because of the speed of sliding is not the same, it will cause the keyboard to enter and enter the slider when there is a gap. In a word, the experience is not good.

So go online to find a way (we must thank the eldest brother), the use of an animated options, and view’s transform method to solve the problem. Let view and keyboard slide seamless fit, silky.

Methods are as follows:

Handle the required animation

Var animate: (() -> Void) = {self.toolBarView.transform = CGAffineTransform (translationX: 0, y: -keyBoardHeight)}

Create animation options

Let options = UIViewAnimationOptions (userInfo[UIKeyboardAnimationCurveUserInfoKey] (as! NSNumber).IntValue < < 16); rawValue: UInt

Achieve animation

UIView.animate (withDuration: mKeyBoardAnimateDuration, delay: 0, options: options, animations: animate)

So and so! Kiss

Now, with the sliding effect of the silky slip, we deal with the three cases of the above analysis

Definition situation

First, we define the type of enumeration of effects, and the benefits of enumeration are not repeated

Enum AnimateType {case animate1 / / keyboard does not block pop-up message case animate2 / / keyboard popup would cover news, but the last message from the input box for a distance of case animate3 / / the last message from the input box in a small range, it is set to 30}

Enumeration types correspond to the three effects of the above analysis

Let’s review the three cases

  • Case 1: when the message is less (when the keyboard pops up will not block the message) chat interface does not move, when the keyboard is only the slider on the input bar, so as to ensure that the first few messages can be fully displayed
  • Situation two: the message is more but has not occupied the screen (when the keyboard will pop up to block part of the message), when the keyboard pops up on the slider, while the chat interface is also slippery. Note: the height of the slider on the input bar is the height of the keyboard, and the sliding distance on the chat interface is the height of the keyboard that may block the message
  • Case three: when the message is full or out of the screen, the keyboard pops up on the entire view

Realization

When the number of messages is 0, the default animation is the input box

Var animate: (() -> Void) = {self.toolBarView.transform = CGAffineTransform (translationX: 0, y: -keyBoardHeight)}

When the number of messages is not 0, the need to calculate the judgment

First get the last message in the screen position, where cellDistance is the last message relative to the current screen y value

Let lastIndex = IndexPath (row: msgList.count - 1, section: 0) let rectCellView = chatTableView.rectForRow (at: lastIndex) let rect = chatTableView.convert (rectCellView, to: chatTableView.superview) let cellDistance = rect.origin.y + rect.height

Limit two positions distance1 and distance2

Distance1 represents a pop-up keyboard at the top of the keyboard position relative to the current screen y value corresponding to the first and second case judgment, distance2 represents the pop-up keyboard input box at the top of the screen when the position for the current value of Y.

Let distance1 = SCREEN_HEIGHT - keyBoardHeight distance2 = SCREEN_HEIGHT - toolBarHeight - 2 * fitBlank * let * toolBarHeight

Calculate the position of the last message and the difference of the distance1

In this case, when in second cases, the input box on the sliding distance for the keyboard height, chat interface sliding distance for the calculation of the difference, the perfect realization of the corresponding results

The corresponding code is as follows:

Let difY = cellDistance - distance1 if cellDistance < distance1 = {animate = {self.toolBarView.transform = CGAffineTransform (translationX: 0, y: -keyBoardHeight animateType.Animate1 else}}) = if distance1 < cellDistance & & cellDistance = < distance2 = {animate {self.toolBarView.transform = CGAffineTransform (translationX: 0, y: self.chatTableView.transform = CGAffineTransform (-keyBoardHeight) translationX: 0, y: -difY) self.lastDifY = difY / / here recorded last slide DIF, after animateType.Animate2 else}} useful = {animate = {self.view.transform = CGAffineTransform (translationX: 0, y: -keyBoardHeight)} AnimateType =.Animate3}

The above code in keyBoardWillShow (notification: Notification), each judge animation situation, record animation, and then when the keyboard disappears, the keyBoardWillHide (notification: Notification) reduction

The code is as follows:

View or toolBarView or chatTableView / / return to original state switch animateType {case.Animate1: animate = self.toolBarView.transform = self.chatTableView.transform = CGAffineTransform.identity {CGAffineTransform.identity} case {animate =.Animate2: self.toolBarView.transform = CGAffineTransform.identity self.chatTableView.transform CGAffineTransform.identity = case.Animate3: animate = {self.view.transform}}} = CGAffineTransform.identity

So and so, to achieve the effect of three kinds of sliding. But don’t worry, there’s a problem. In the case of the first and the second, the chat interface on the slide, how to ensure that the last message displayed on the keyboard?

This requires us to send the message, refresh the list of methods for processing, where the entire refresh list method

Implementation ideas for:

  • In the case of three, as a result of the previous constraint on the chat interface above the input bar, and the entire interface together on the slide, the constraint is still set up, just put the last message chat interface scroll to the bottom of the chat interface
  • In the case of a situation and two, if the chat interface sliding total distance (lastDifY + difY) is less than the height of the keyboard, you can continue to slide on the sliding distance for the height of the new message
  • Once the total distance on the chat interface will be more than the height of the keyboard, the total height of the sliding distance is set to the keyboard height, if the total distance on the chat interface sliding over the height of the keyboard, the interface will appear redundant blank
  • Once the total distance on the chat interface is the height of the keyboard, it will be dealt with in accordance with the situation three

Tried, probably that is not clear, so it code:

Refresh the list of func reloadTableView / / () chatTableView.reloadData () chatTableView.layoutIfNeeded () {/ / the last message in the location in the view let lastIndex IndexPath (row: = msgList.count - 1, section: 0) let rectCellView = chatTableView.rectForRow (at: lastIndex) let rect = chatTableView.convert (rectCellView, to: chatTableView.superview) let cellDistance = rect.origin.y + rect.height let distance1 = SCREEN_HEIGHT - toolBarHeight - mKeyBoardHeight / / calculation keyboard can cover the length of the message let difY distance1 if = cellDistance - animateType = =.Animate3 {/ / in three, due to prior constraints in the input bar (chat interface Party, and is the interface) / slide together, so the constraint is still established just send a message / chat interface finally scroll to the bottom of the chat interface can be scrollToBottom (else)} if (animateType = =.Animate1 || animateType = =.Animate2) & & difY > 0{; / / in a situation and 2 if the total distance is less than the chat interface sliding on the keyboard height, you can continue to slide / once total distance lastDifY + difY chat interface slip will exceed the height of the keyboard, then slip on the total distance for keyboard height when the execution of trans / / / / animation once the chat interface slip distance is changed to the height of the keyboard, three, the chat interface finally a message / roll To chat at the bottom of the screen can be if lastDifY + difY < mKeyBoardHeight difY let = {lastDifY (animate:) (-> Void) {self.chatTableView.transform = CGAffineTransform (translationX: = 0, y: -self.lastDifY UIView.animate (withDuration:)} mKeyBoardAnimateDuration, delay: 0, options: animateOption, animations: animate else if lastDifY difY)} + {if mKeyBoardHeight > lastDifY! = mKeyBoardHeight {let (animate:) (-> Void) {self.chatTableView.transform = CGAffineTransform (translationX: = 0, y: -self.mKeyBoardHeight)} UIView.animate (withDuration: mKeyBoardAnimateDuration, delay: 0, options: animateOption, animations: animate) lastDifY = mKeyBoardHeight (scrollToBottom)}}}}

Then roll the last message to the bottom of the chat interface code:

Func scrollToBottom () {msgList.count > 0 {chatTableView.scrollToRow (at: IndexPath (row: msgList.count, section:,, if), at:.Bottom, animated: true)}}

At this point, will really be accomplished,

summary

Just want to start to achieve a simple chat, because I did not expect OCD and achieve excellent experience, in effect on the keyboard for a long time sike. Spent a total of around one and a half days, really Chafanbusi, awake at night. The middle trying numerous sliding method, drawing simulation situations in a notebook, finally do it, like what the people instantly, weak, and can’t wait to sleep, but the heart is very excited.

Calm after writing this blog, and we encourage each other.

In addition, this is my second technical blog, welcome a lot Tucao, exchange

Finally, with the source address: https://github.com/Newbeeee/NbChatView-Swift

If the reader look pleasing to the eye, at star, n (or at less than n)