Skip to content

Commit

Permalink
Fixes some keyboard issues
Browse files Browse the repository at this point in the history
  • Loading branch information
diegosanchezr committed Apr 20, 2016
1 parent c2c2d30 commit f3e3556
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 38 deletions.
2 changes: 1 addition & 1 deletion Chatto/Source/ChatController/BaseChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public class BaseChatViewController: UIViewController, UICollectionViewDataSourc
super.viewDidLayoutSubviews()

self.adjustCollectionViewInsets()
self.keyboardTracker.layoutTrackingViewIfNeeded()
self.keyboardTracker.adjustTrackingViewSizeIfNeeded()

if self.isFirstLayout {
self.updateQueue.start()
Expand Down
67 changes: 34 additions & 33 deletions Chatto/Source/ChatController/Collaborators/KeyboardTracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ class KeyboardTracker {
private lazy var keyboardTrackerView: KeyboardTrackingView = {
let trackingView = KeyboardTrackingView()
trackingView.positionChangedCallback = { [weak self] in
self?.layoutInputAtTrackingViewIfNeeded()
guard let sSelf = self else { return }
if !sSelf.isPerformingForcedLayout {
sSelf.layoutInputAtTrackingViewIfNeeded()
}
}
return trackingView
}()
Expand Down Expand Up @@ -76,23 +79,23 @@ class KeyboardTracker {
@objc
private func keyboardWillShow(notification: NSNotification) {
guard self.isTracking else { return }
guard !self.isPerformingForcedLayout else { return}
let bottomConstraint = self.bottomConstraintFromNotification(notification)
guard bottomConstraint > 0 else { return } // Some keyboards may report initial willShow/DidShow notifications with invalid positions
self.keyboardStatus = .Showing
self.inputContainerBottomConstraint.constant = bottomConstraint
self.view.layoutIfNeeded()
self.adjustTrackingViewSizeIfNeeded()
self.layoutInputContainer(withBottomConstraint: bottomConstraint)
}

@objc
private func keyboardDidShow(notification: NSNotification) {
guard self.isTracking else { return }
guard !self.isPerformingForcedLayout else { return}

let bottomConstraint = self.bottomConstraintFromNotification(notification)
guard bottomConstraint > 0 else { return } // Some keyboards may report initial willShow/DidShow notifications with invalid positions
self.keyboardStatus = .Shown
self.inputContainerBottomConstraint.constant = bottomConstraint
self.view.layoutIfNeeded()
self.layoutTrackingViewIfNeeded()
self.layoutInputContainer(withBottomConstraint: bottomConstraint)
self.adjustTrackingViewSizeIfNeeded()
}

@objc
Expand All @@ -117,49 +120,46 @@ class KeyboardTracker {
guard rect.height > 0 else { return 0 }
let rectInView = self.view.convertRect(rect, fromView: nil)
guard rectInView.maxY >= self.view.bounds.height else { return 0 } // Undocked keyboard
return max(0, self.view.bounds.height - rectInView.minY - self.trackingView.bounds.height)
return max(0, self.view.bounds.height - rectInView.minY - self.keyboardTrackerView.intrinsicContentSize().height)
}

private func bottomConstraintFromTrackingView() -> CGFloat {
guard self.keyboardTrackerView.superview != nil else { return 0 }
let trackingViewRect = self.view.convertRect(self.keyboardTrackerView.bounds, fromView: self.keyboardTrackerView)
return max(0, self.view.bounds.height - trackingViewRect.maxY)
}

var ios8WorkaroundIsInProgress = false
func layoutTrackingViewIfNeeded() {
func adjustTrackingViewSizeIfNeeded() {
guard self.isTracking && self.keyboardStatus == .Shown else { return }
self.adjustTrackingViewSizeIfNeeded()
if #available(iOS 9, *) {
// Working fine on iOS 9
} else {
// Workaround for iOS 8
guard !self.ios8WorkaroundIsInProgress else { return }
self.ios8WorkaroundIsInProgress = true
self.trackingView.window?.setNeedsLayout()
self.trackingView.window?.layoutIfNeeded()
self.ios8WorkaroundIsInProgress = false
}
self.adjustTrackingViewSize()
}

private func adjustTrackingViewSizeIfNeeded() {
private func adjustTrackingViewSize() {
let inputContainerHeight = self.inputContainer.bounds.height
let trackerViewHeight = self.trackingView.bounds.height
if trackerViewHeight != inputContainerHeight {
self.keyboardTrackerView.bounds.size.height = inputContainerHeight
if self.keyboardTrackerView.preferredSize.height != inputContainerHeight {
self.keyboardTrackerView.preferredSize.height = inputContainerHeight
self.isPerformingForcedLayout = true
self.keyboardTrackerView.window?.layoutIfNeeded()
self.isPerformingForcedLayout = false
}
}

private func layoutInputAtBottom() {
self.keyboardTrackerView.bounds.size.height = 0
self.inputContainerBottomConstraint.constant = 0
self.view.layoutIfNeeded()
self.layoutInputContainer(withBottomConstraint: 0)
}

var isPerformingForcedLayout: Bool = false
func layoutInputAtTrackingViewIfNeeded() {
guard self.isTracking && self.keyboardStatus == .Shown else { return }
let newBottomConstraint = self.bottomConstraintFromTrackingView()
self.inputContainerBottomConstraint.constant = newBottomConstraint
self.layoutInputContainer(withBottomConstraint: self.bottomConstraintFromTrackingView())
}

private func layoutInputContainer(withBottomConstraint constraint: CGFloat) {
self.isPerformingForcedLayout = true
self.inputContainerBottomConstraint.constant = constraint
self.view.layoutIfNeeded()
self.isPerformingForcedLayout = false
}
}

Expand All @@ -170,7 +170,7 @@ private class KeyboardTrackingView: UIView {

deinit {
if let observedView = self.observedView {
observedView.removeObserver(self, forKeyPath: "center")
observedView.removeObserver(self, forKeyPath: "frame")
}
}

Expand All @@ -191,16 +191,17 @@ private class KeyboardTrackingView: UIView {
self.hidden = true
}

override var bounds: CGRect {
private var preferredSize: CGSize = .zero {
didSet {
if oldValue.size != self.bounds.size {
if oldValue != self.preferredSize {
self.invalidateIntrinsicContentSize()
self.window?.setNeedsLayout()
}
}
}

private override func intrinsicContentSize() -> CGSize {
return self.bounds.size
return self.preferredSize
}

override func willMoveToSuperview(newSuperview: UIView?) {
Expand Down
18 changes: 14 additions & 4 deletions ChattoAdditions/Source/Input/ExpandableTextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,27 +93,37 @@ public class ExpandableTextView: UITextView {
func textDidChange() {
self.updatePlaceholderVisibility()
self.scrollToCaret()

if #available(iOS 9, *) {
// Bugfix:
// 1. Open keyboard
// 2. Paste very long text (so it snaps to nav bar and shows scroll indicators)
// 3. Select all and cut
// 4. Paste again: Texview it's smaller than it should be
self.scrollEnabled = false
self.scrollEnabled = true
}
}

private func scrollToCaret() {
if selectedTextRange != nil {
var rect = caretRectForPosition(self.selectedTextRange!.end)
if let textRange = self.selectedTextRange {
var rect = caretRectForPosition(textRange.end)
rect = CGRect(origin: rect.origin, size: CGSize(width: rect.width, height: rect.height + textContainerInset.bottom))

self.scrollRectToVisible(rect, animated: false)
}
}

private func updatePlaceholderVisibility() {
if text == "" {
if self.text == "" {
self.showPlaceholder()
} else {
self.hidePlaceholder()
}
}

private func showPlaceholder() {
self.addSubview(placeholder)
self.addSubview(self.placeholder)
}

private func hidePlaceholder() {
Expand Down

0 comments on commit f3e3556

Please sign in to comment.