Skip to content

Commit

Permalink
Adjust the keyboard insets in both setFrame and layoutSubviews to ens…
Browse files Browse the repository at this point in the history
…ure it is always correct
  • Loading branch information
kyleve committed Aug 28, 2020
1 parent 09e1eff commit 745ac28
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### Fixed

- [Adjust calculated keyboard inset in both `setFrame` and `layoutSubviews`](https://github.com/kyleve/Listable/pull/200). This resolves issues that can occur if the list frame changes while the keyboard is visible.

### Added

- [Add support for `onInsert` , `onRemove`, `onMove`, `onUpdate`, on `Item`](https://github.com/kyleve/Listable/pull/196) to track when when items are added, removed, moved, or updated. Changed `onContentChanged` to `onContentUpdated` on `ListStateObserver`; it is always called during updates; you can check the `hadChanges` property.
Expand Down
4 changes: 4 additions & 0 deletions Demo/Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
0A07119324BA798400CDF65D /* ListStateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A07119224BA798400CDF65D /* ListStateViewController.swift */; };
0A0E070423870A5700DDD27D /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 0A0E070323870A5700DDD27D /* README.md */; };
0A49210424E5E11300D17038 /* AccordionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A49210324E5E11300D17038 /* AccordionViewController.swift */; };
0A5DC1A924F6FD4200DC7C14 /* ListAppearsAfterKeyboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5DC1A824F6FD4200DC7C14 /* ListAppearsAfterKeyboardViewController.swift */; };
0A793B5824E4B53500850139 /* ManualSelectionManagementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A793B5724E4B53500850139 /* ManualSelectionManagementViewController.swift */; };
0A87BA652463567B0047C3B5 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 0A87BA642463567B0047C3B5 /* CHANGELOG.md */; };
0AA4D9B9248064A300CF95A5 /* CustomLayoutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA4D9A8248064A200CF95A5 /* CustomLayoutsViewController.swift */; };
Expand Down Expand Up @@ -47,6 +48,7 @@
0A07119224BA798400CDF65D /* ListStateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListStateViewController.swift; sourceTree = "<group>"; };
0A0E070323870A5700DDD27D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
0A49210324E5E11300D17038 /* AccordionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccordionViewController.swift; sourceTree = "<group>"; };
0A5DC1A824F6FD4200DC7C14 /* ListAppearsAfterKeyboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListAppearsAfterKeyboardViewController.swift; sourceTree = "<group>"; };
0A793B5724E4B53500850139 /* ManualSelectionManagementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualSelectionManagementViewController.swift; sourceTree = "<group>"; };
0A87BA642463567B0047C3B5 /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = "<group>"; };
0AA4D9A8248064A200CF95A5 /* CustomLayoutsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomLayoutsViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -133,6 +135,7 @@
0ACF96D524A0094D0090EAC4 /* ItemInsertAndRemoveAnimationsViewController.swift */,
0AA4D9B3248064A300CF95A5 /* ItemizationEditorViewController.swift */,
0AA4D9AB248064A300CF95A5 /* KeyboardTestingViewController.swift */,
0A5DC1A824F6FD4200DC7C14 /* ListAppearsAfterKeyboardViewController.swift */,
0A07119224BA798400CDF65D /* ListStateViewController.swift */,
0AC2A1952489F93E00779459 /* PagedViewController.swift */,
0AA4D9B7248064A300CF95A5 /* ReorderingViewController.swift */,
Expand Down Expand Up @@ -425,6 +428,7 @@
0AA4D9C6248064A300CF95A5 /* SwipeActionsViewController.swift in Sources */,
0AA4D9BF248064A300CF95A5 /* WidthCustomizationViewController.swift in Sources */,
0AA4D9C8248064A300CF95A5 /* ReorderingViewController.swift in Sources */,
0A5DC1A924F6FD4200DC7C14 /* ListAppearsAfterKeyboardViewController.swift in Sources */,
0A07119324BA798400CDF65D /* ListStateViewController.swift in Sources */,
0AA4D9C0248064A300CF95A5 /* InvoicesPaymentScheduleDemoViewController.swift in Sources */,
0AEB96E222FBCC1D00341DFF /* AppDelegate.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// ListAppearsAfterKeyboardViewController.swift
// Demo
//
// Created by Kyle Van Essen on 8/26/20.
// Copyright © 2020 Kyle Van Essen. All rights reserved.
//

import UIKit
import BlueprintLists
import BlueprintUICommonControls


final class ListAppearsAfterKeyboardViewController : UIViewController {

let blueprintView = BlueprintView()

override func loadView() {
self.view = self.blueprintView

self.blueprintView.element = self.element
}

override func viewDidLoad() {
super.viewDidLoad()

self.navigationItem.rightBarButtonItems = [
UIBarButtonItem(title: "Toggle List", style: .plain, target: self, action: #selector(toggleList)),
UIBarButtonItem(title: "Dismiss", style: .plain, target: self, action: #selector(dismissKeyboard))
]
}

var showingList : Bool = false

var element : Element {
EnvironmentReader { env in
Column { column in
column.horizontalAlignment = .fill
column.verticalUnderflow = .growProportionally

column.add(growPriority: 0, shrinkPriority: 0, child: TextField(text: "") {
$0.placeholder = "Tap Into This Field To Show The Keyboard"
$0.textAlignment = .center
})

if self.showingList {
column.add(child: List { list in

list.behavior.keyboardDismissMode = .none

list("section") { section in
section += (1...20).map { index in
DemoItem(text: "Item \(index)")
}
}
})
}
}.inset(by: env.safeAreaInsets)
}
}

@objc func toggleList() {
self.showingList.toggle()

self.blueprintView.element = self.element
}

@objc func dismissKeyboard() {
self.view.endEditing(true)
}
}
9 changes: 8 additions & 1 deletion Demo/Sources/Demos/DemosRootViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,19 @@ public final class DemosRootViewController : ListViewController
})

section += Item(
DemoItem(text: "Keyboard Testing"),
DemoItem(text: "Keyboard Inset (Full Screen List)"),
selectionStyle: .selectable(),
onSelect : { _ in
self.push(KeyboardTestingViewController())
})

section += Item(
DemoItem(text: "Keyboard Inset (Appears Later)"),
selectionStyle: .selectable(),
onSelect : { _ in
self.push(ListAppearsAfterKeyboardViewController())
})

section += Item(
DemoItem(text: "Reordering (Experimental)"),
selectionStyle: .selectable(),
Expand Down
71 changes: 51 additions & 20 deletions Listable/Sources/ListView/ListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,10 @@ public final class ListView : UIView
*/
self.collectionView.frame = self.bounds

guard oldValue != self.frame else {
return
}

/**
Once the view actually has a size, we can provide content.

Expand All @@ -516,15 +520,16 @@ public final class ListView : UIView
self.updatePresentationState(for: .transitionedToBounds(isEmpty: true))
}

if oldValue != self.frame {
ListStateObserver.perform(self.stateObserver.onFrameChanged, "Frame Changed", with: self) { actions in
ListStateObserver.FrameChanged(
actions: actions,
positionInfo: self.scrollPositionInfo,
old: oldValue,
new: self.frame
)
}
/// Our frame changed, update the keyboard inset in case the inset should now be different.
self.setContentInsetWithKeyboardFrame()

ListStateObserver.perform(self.stateObserver.onFrameChanged, "Frame Changed", with: self) { actions in
ListStateObserver.FrameChanged(
actions: actions,
positionInfo: self.scrollPositionInfo,
old: oldValue,
new: self.frame
)
}
}
}
Expand Down Expand Up @@ -558,6 +563,9 @@ public final class ListView : UIView
super.layoutSubviews()

self.collectionView.frame = self.bounds

/// Our layout changed, update the keyboard inset in case the inset should now be different.
self.setContentInsetWithKeyboardFrame()
}

//
Expand Down Expand Up @@ -868,6 +876,27 @@ public final class ListView : UIView
}


public extension ListView
{
///
/// Call this method to force an immediate, synchronous re-render of the list
/// and its content when writing unit or snapshot tests. This avoids needing to
/// spin the runloop or needing to use test expectations to wait for content
/// to be rendered asynchronously.
///
/// **WARNING**: You must **not** call this method outside of tests. Doing so will cause a fatal error.
///
func testing_forceLayoutUpdateNow()
{
guard NSClassFromString("XCTestCase") != nil else {
fatalError("You must not call testing_forceLayoutUpdateNow outside of an XCTest environment.")
}

self.collectionView.reloadData()
}
}


extension ListView : ItemContentCoordinatorDelegate
{
func coordinatorUpdated(for : AnyItem, animated : Bool)
Expand Down Expand Up @@ -960,19 +989,21 @@ extension ListView : KeyboardObserverDelegate
return
}

let inset : CGFloat

switch self.behavior.keyboardAdjustmentMode {
case .none: inset = 0.0

case .adjustsWhenVisible:
switch frame {
case .nonOverlapping: inset = 0.0
let inset : CGFloat = {
switch self.behavior.keyboardAdjustmentMode {
case .none:
return 0.0

case .overlapping(let frame):
inset = (self.bounds.size.height - frame.origin.y) - self.lst_safeAreaInsets.bottom
case .adjustsWhenVisible:
switch frame {
case .nonOverlapping:
return 0.0

case .overlapping(let frame):
return (self.bounds.size.height - frame.origin.y) - self.lst_safeAreaInsets.bottom
}
}
}
}()

if self.collectionView.contentInset.bottom != inset {
self.collectionView.contentInset.bottom = inset
Expand Down

0 comments on commit 745ac28

Please sign in to comment.