fix: keyboard overlapping for footer and content view of BaseModalViewController

This commit is contained in:
Ivan Smolin 2023-07-25 18:36:24 +03:00
parent 0ef1edfacb
commit 8007532351
8 changed files with 86 additions and 75 deletions

View File

@ -151,9 +151,6 @@ open class BaseModalViewController<ContentView: UIView,
sizeConstraints: sizeConstraints)
}()
public var keyboardDidShownObserver: NSObjectProtocol?
public var keyboardDidHiddenObserver: NSObjectProtocol?
// MARK: - Modal View Controller Configuration
open var viewControllerAppearance: BaseAppearance {
@ -164,6 +161,12 @@ open class BaseModalViewController<ContentView: UIView,
contentView as? UIScrollView
}
public var panScrollableInsets: UIEdgeInsets = .zero {
didSet {
panScrollable?.contentInset = panScrollableInsets
}
}
open var presentationDetents: [ModalViewPresentationDetent] {
[.maxHeight]
}
@ -199,24 +202,23 @@ open class BaseModalViewController<ContentView: UIView,
return detents.min()?.panModalHeight(headerHeight: headerViewHeight) ?? .maxHeight
}
private var keyboardDidShownObserver: NSObjectProtocol?
private var keyboardDidHiddenObserver: NSObjectProtocol?
// MARK: - Life Cycle
deinit {
let notificationCenter = NotificationCenter.default
if let keyboardDidShownObserver {
NotificationCenter.default.removeObserver(keyboardDidShownObserver)
notificationCenter.removeObserver(keyboardDidShownObserver)
}
if let keyboardDidHiddenObserver {
NotificationCenter.default.removeObserver(keyboardDidHiddenObserver)
notificationCenter.removeObserver(keyboardDidHiddenObserver)
}
}
open override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
adjustScrollViewIfNeeded()
}
// MARK: - BaseInitializableViewController
open override func addViews() {
@ -280,33 +282,25 @@ open class BaseModalViewController<ContentView: UIView,
return
}
if let panScrollable {
if isKeyboardHidden {
adjustScrollViewIfNeeded()
} else {
panScrollable.contentInset = .vertical(bottom: keyboardHeight)
}
} else if case let .presented(footerViewAppearance) = viewControllerAppearance.footerViewState {
if case let .presented(footerViewAppearance) = viewControllerAppearance.footerViewState {
let bottomInset = footerViewAppearance.layout.insets.add(\.bottom,
to: \.bottom,
of: .vertical(bottom: keyboardHeight))
footerViewConstraints.edgeConstraints.bottomConstraint.constant = bottomInset
} else if let panScrollable {
var insets = panScrollableInsets
if isKeyboardHidden {
panScrollable.contentInset = insets
} else {
insets.bottom += keyboardHeight
panScrollable.contentInset = insets
}
}
}
open func adjustScrollViewIfNeeded() {
guard let panScrollable, case let .presented(footerViewAppearance) = viewControllerAppearance.footerViewState else {
return
}
let verticalInsets = footerViewAppearance.layout.insets.vertical(onNan: .zero)
let subviewVerticalInsets = footerViewAppearance.subviewAppearance.layout.insets.vertical(onNan: .zero)
let totalHeight = getHeight(of: footerView) + verticalInsets + subviewVerticalInsets
panScrollable.contentInset = .vertical(bottom: totalHeight)
}
// MARK: - Private Methods
private func configureDragViewLayout() {

View File

@ -22,20 +22,26 @@
import UIKit
open class BaseModalWrapperViewController<ContentViewController>: BaseModalViewController<UIView, UIView>
where ContentViewController: UIViewController {
open class DefaultModalWrapperViewController<ContentController: UIViewController>: BaseModalViewController<UIView, UIView> {
private(set) public lazy var contentViewController = createContentViewController()
private(set) public var contentViewController: ContentController
// MARK: - BaseModalViewController
public init(contentViewController: ContentController) {
self.contentViewController = contentViewController
open override func createContentView() -> UIView {
contentViewController.view
super.init(nibName: nil, bundle: nil)
}
// MARK: - Open methods
@available(*, unavailable)
required public init?(coder: NSCoder) {
contentViewController = ContentController()
open func createContentViewController() -> ContentViewController {
ContentViewController()
super.init(coder: coder)
}
}
public extension UIViewController {
func wrappedInBottomSheetController() -> DefaultModalWrapperViewController<UIViewController> {
DefaultModalWrapperViewController(contentViewController: self)
}
}

View File

@ -54,7 +54,7 @@ public final class DragView: BaseInitializableView, AppearanceConfigurable {
public final class Appearance: UIView.BaseWrappedAppearance<UIView.DefaultWrappedLayout>, WrappedViewAppearance {
public static var defaultAppearance: Appearance {
Self().update { dragView in
.make { dragView in
dragView.backgroundColor = Constants.dragViewBackgroundColor
dragView.border = Constants.dragViewBorder

View File

@ -24,13 +24,10 @@ import TIUIElements
import TIUIKitCore
import UIKit
public typealias ModalFooterView<ContentView: UIView> = ContainerView<ContentView>
public extension ModalFooterView {
open class ModalFooterView<ContentView: UIView>: ContainerView<ContentView> {
// MARK: - Nested Types
enum State {
public enum State {
case hidden
case presented(BaseWrappedViewHolderAppearance<DefaultWrappedAppearance, DefaultWrappedLayout>)
}

View File

@ -42,31 +42,31 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
// MARK: - Public properties
public let leftButton = StatefulButton()
public let rightButton = StatefulButton()
public let leadingButton = StatefulButton()
public let trailingButton = StatefulButton()
public private(set) lazy var leftTrailingToRightLeadingConstraint: NSLayoutConstraint = {
leftButton.trailingAnchor.constraint(equalTo: rightButton.leadingAnchor)
leadingButton.trailingAnchor.constraint(equalTo: trailingButton.leadingAnchor)
}()
public private(set) lazy var leftTrailingToSuperviewTrailing: NSLayoutConstraint = {
leftButton.trailingAnchor.constraint(equalTo: rightButton.leadingAnchor)
leadingButton.trailingAnchor.constraint(equalTo: trailingButton.leadingAnchor)
}()
public private(set) lazy var leftButtonConstraints: SubviewConstraints = {
let edgeConstraints = EdgeConstraints(leadingConstraint: leftButton.leadingAnchor.constraint(equalTo: leadingAnchor),
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingButton.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: leftTrailingToRightLeadingConstraint,
topConstraint: leftButton.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: leftButton.bottomAnchor.constraint(equalTo: bottomAnchor))
topConstraint: leadingButton.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: leadingButton.bottomAnchor.constraint(equalTo: bottomAnchor))
let centerXConstraint = leftButton.centerXAnchor.constraint(equalTo: centerXAnchor)
let centerYConstraint = leftButton.centerYAnchor.constraint(equalTo: centerYAnchor)
let centerXConstraint = leadingButton.centerXAnchor.constraint(equalTo: centerXAnchor)
let centerYConstraint = leadingButton.centerYAnchor.constraint(equalTo: centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let sizeConstraints = SizeConstraints(widthConstraint: leftButton.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: leftButton.heightAnchor.constraint(equalToConstant: .zero))
let sizeConstraints = SizeConstraints(widthConstraint: leadingButton.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: leadingButton.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
@ -74,25 +74,26 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
}()
public private(set) lazy var rightLeadingToSuperviewLeadingConstraint: NSLayoutConstraint = {
rightButton.leadingAnchor.constraint(equalTo: leadingAnchor)
trailingButton.leadingAnchor.constraint(equalTo: leadingAnchor)
}()
public private(set) lazy var rightButtonConstraints: SubviewConstraints = {
let trailingConstraint = rightButton.trailingAnchor.constraint(equalTo: trailingAnchor)
let leadingConstraint = trailingButton.leadingAnchor.constraint(equalTo: leadingAnchor)
let trailingConstraint = trailingButton.trailingAnchor.constraint(equalTo: trailingAnchor)
let edgeConstraints = EdgeConstraints(leadingConstraint: rightButton.leadingAnchor.constraint(equalTo: leadingAnchor),
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingConstraint,
trailingConstraint: trailingConstraint,
topConstraint: rightButton.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: rightButton.bottomAnchor.constraint(equalTo: bottomAnchor))
topConstraint: trailingButton.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: trailingButton.bottomAnchor.constraint(equalTo: bottomAnchor))
let centerXConstraint = rightButton.centerXAnchor.constraint(equalTo: centerXAnchor)
let centerYConstraint = rightButton.centerYAnchor.constraint(equalTo: centerYAnchor)
let centerXConstraint = trailingButton.centerXAnchor.constraint(equalTo: centerXAnchor)
let centerYConstraint = trailingButton.centerYAnchor.constraint(equalTo: centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let sizeConstraints = SizeConstraints(widthConstraint: rightButton.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: rightButton.heightAnchor.constraint(equalToConstant: .zero))
let sizeConstraints = SizeConstraints(widthConstraint: trailingButton.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: trailingButton.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
@ -106,14 +107,15 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
open override func addViews() {
super.addViews()
addSubviews(leftButton, rightButton)
addSubviews(leadingButton, trailingButton)
}
open override func configureLayout() {
super.configureLayout()
[leftButton, rightButton]
.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
for view in [leadingButton, trailingButton] {
view.translatesAutoresizingMaskIntoConstraints = false
}
}
// MARK: - AppearanceConfigurable
@ -154,8 +156,8 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
view.configureUIView(appearance: appearance)
}
configure(buttonStyle: leadingButtonStyle, forButton: leftButton, constraints: leftButtonConstraints)
configure(buttonStyle: trailingButtonStyle, forButton: rightButton, constraints: rightButtonConstraints)
configure(buttonStyle: leadingButtonStyle, forButton: leadingButton, constraints: leftButtonConstraints)
configure(buttonStyle: trailingButtonStyle, forButton: trailingButton, constraints: rightButtonConstraints)
}
// MARK: - Private methods

View File

@ -101,8 +101,8 @@ open class BasePlaceholderView<ImageView: UIView>: BaseInitializableView {
sizeConstraints: sizeConstraints)
}()
public var keyboardDidShownObserver: NSObjectProtocol?
public var keyboardDidHiddenObserver: NSObjectProtocol?
private var keyboardDidShownObserver: NSObjectProtocol?
private var keyboardDidHiddenObserver: NSObjectProtocol?
open var isImageViewHidden: Bool {
imageView.isHidden
@ -112,6 +112,18 @@ open class BasePlaceholderView<ImageView: UIView>: BaseInitializableView {
controlsStackView.isHidden || controlsStackView.arrangedSubviews.isEmpty
}
deinit {
let notificationCenter = NotificationCenter.default
if let keyboardDidShownObserver {
notificationCenter.removeObserver(keyboardDidShownObserver)
}
if let keyboardDidHiddenObserver {
notificationCenter.removeObserver(keyboardDidHiddenObserver)
}
}
// MARK: - BaseInitializableView
open override func addViews() {

View File

@ -52,12 +52,12 @@ open class CollectionTableViewCell<CollectionView: UICollectionView>: ContainerT
}
let cachedCollectionFrame = wrappedView.frame
wrappedView.frame.size.width = targetSize.width - wrappedContentInsets.left - wrappedContentInsets.right
wrappedView.frame.size.width = targetSize.width - wrappedContentInsets.horizontal(onNan: .zero)
let collectionContentHeight = wrappedView.collectionViewLayout.collectionViewContentSize.height
wrappedView.frame = cachedCollectionFrame
return CGSize(width: targetSize.width,
height: collectionContentHeight + wrappedContentInsets.top + wrappedContentInsets.bottom)
height: collectionContentHeight + wrappedContentInsets.vertical(onNan: .zero))
}
// MARK: - Open methods

View File

@ -23,7 +23,7 @@
import TIUIKitCore
import UIKit
public final class ContainerView<View: UIView>: BaseInitializableView, WrappedViewHolder {
open class ContainerView<View: UIView>: BaseInitializableView, WrappedViewHolder {
public private(set) lazy var wrappedView = View()