// // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import Foundation import UIKit open class OWSTableSheetViewController: InteractiveSheetViewController { open var tableViewController = OWSTableViewController2() open override var interactiveScrollViews: [UIScrollView] { [tableViewController.tableView] } open override var sheetBackgroundColor: UIColor { OWSTableViewController2.tableBackgroundColor(isUsingPresentedStyle: true, forceDarkMode: tableViewController.forceDarkMode) } open var bottomSafeAreaContentPadding: CGFloat { var padding = view.safeAreaInsets.bottom if padding == 0 { // For home button devices, add generous bottom padding // so it isn't right up on the edge. padding = 36 } else { // For other devices, just add a touch extra. padding += 12 } return padding } private var contentSizeHeight: CGFloat { let tableView = tableViewController.tableView // The `adjustedContentInset` property diverges from its stable value during // interactive dismiss operations. This causes weird drag/height jump behavior. // Instead, compute the height using `view.safeAreaInsets`, which remains stable // during animations. (Note that `.top` isn't considered here since // `maximumHeight` prevents the view's height from extending into top safe area.) return tableView.contentSize.height + tableView.contentInset.totalHeight + footerStack.frame.height + bottomSafeAreaContentPadding } private var contentSizeObservation: NSKeyValueObservation? public init() { super.init() tableViewController.shouldDeferInitialLoad = false super.allowsExpansion = false contentSizeObservation = tableViewController.tableView.observe(\.contentSize, changeHandler: { [weak self] (_, _) in guard let self = self else { return } self.updateMinimizedHeight() }) } public func updateMinimizedHeight() { self.minimizedHeight = self.contentSizeHeight } deinit { contentSizeObservation?.invalidate() } open var footerStack: UIStackView = { let view = UIStackView() view.axis = .vertical view.distribution = .fill view.alignment = .center view.preservesSuperviewLayoutMargins = true return view }() public override func viewDidLoad() { super.viewDidLoad() addChild(tableViewController) contentView.addSubview(tableViewController.view) contentView.addSubview(footerStack) tableViewController.view.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .bottom) footerStack.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top) tableViewController.view.autoPinEdge(.bottom, to: .top, of: footerStack) minimizedHeight = contentSizeHeight updateViewState() } public override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() updateViewState() } private var previousMinimizedHeight: CGFloat? private var previousSafeAreaInsets: UIEdgeInsets? public func updateViewState() { if previousSafeAreaInsets != tableViewController.view.safeAreaInsets { updateTableContents() previousSafeAreaInsets = tableViewController.view.safeAreaInsets } // The table view might not have its final size when this method is called. // Run a layout pass so that we compute the correct height constraints. self.tableViewController.tableView.layoutIfNeeded() } public override func themeDidChange() { super.themeDidChange() updateTableContents() } open func updateTableContents(shouldReload: Bool = true) { } }