PanModal
PanModal copied to clipboard
Pan modal with fixed view bottom
Is it possible to open panModal with fixed view bottom like instagram?
https://user-images.githubusercontent.com/1735944/169672201-4abdf405-a2d7-4048-badd-4dd36ba8e09c.MOV
I have the same problem with this issue. Can anyone help us?
In PanModelPresentable you can find topOffset that will help you
Hi there! I spent a few hours trying to solve this problem. Here is my solution. Possibly it can save you some hours of work :)
import UIKit
import SnapKit
import PanModal
class ExampleViewController: UIViewController, PanModalPresentable {
// MARK: - Outlets
private lazy var tableView: UITableView = {
let view = UITableView(frame: .zero, style: .grouped)
view.dataSource = self
view.alwaysBounceVertical = false
view.contentInsetAdjustmentBehavior = .never
view.automaticallyAdjustsScrollIndicatorInsets = false
view.insetsContentViewsToSafeArea = false
return view
}()
private lazy var footerView: UIView = {
let view = UIView()
view.backgroundColor = .systemYellow
return view
}()
// MARK: - Variables
private var isViewConfigured: Bool = false
private var panModalGestureRecognizer: UIPanGestureRecognizer?
private var contentHeight: CGFloat {
return self.calculateContentHeight()
}
private var minContentHeight: CGFloat {
return min(self.contentHeight, 300)
}
private var maxTopOffset: CGFloat {
return UIScreen.main.bounds.height - self.minContentHeight
}
private var minTopOffset: CGFloat {
return UIScreen.main.bounds.height - self.contentHeight
}
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.configureSubviews()
self.configureConstraints()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let bottomInset = self.footerView.frame.height
self.tableView.contentInset.bottom = bottomInset
self.tableView.verticalScrollIndicatorInsets.bottom = bottomInset
if !self.isViewConfigured {
self.isViewConfigured = true
self.pinFooter(for: .shortForm)
self.panModalSetNeedsLayoutUpdate()
self.panModalTransition(to: .shortForm)
}
}
// MARK: - Configuration
private func configureSubviews() {
self.view.addSubview(self.tableView)
self.view.addSubview(self.footerView)
}
private func configureConstraints() {
self.tableView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
self.footerView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.height.equalTo(100)
}
}
// MARK: - Actions
private func calculateContentHeight() -> CGFloat {
return 1000 // Your calculated content height
}
private func pinFooter(for state: PanModalPresentationController.PresentationState) {
let targetHeight: CGFloat = {
switch state {
case .longForm: return self.contentHeight
case .shortForm: return self.minContentHeight
}
}()
let offset = self.view.frame.height - targetHeight
self.footerView.transform = CGAffineTransform(translationX: 0, y: min(-offset, 0))
}
// MARK: - PanModalPresentable
func willRespond(to panModalGestureRecognizer: UIPanGestureRecognizer) {
self.panModalGestureRecognizer = panModalGestureRecognizer
var yPanTranslation = panModalGestureRecognizer.translation(in: self.footerView).y
if let presentedView = self.presentationController?.presentedView {
let desiredOffset = presentedView.frame.minY + yPanTranslation
if desiredOffset >= self.maxTopOffset {
yPanTranslation = 0 // Disable fixing while hiding
}
if desiredOffset <= self.minTopOffset {
yPanTranslation /= 2 // Compensation of bounce effect
}
}
if yPanTranslation != 0 {
self.footerView.transform = self.footerView.transform.translatedBy(x: 0, y: -yPanTranslation)
}
}
func willTransition(to state: PanModalPresentationController.PresentationState) {
guard self.panModalGestureRecognizer?.state != .changed else {
self.pinFooter(for: state)
return
}
self.panModalAnimate({ [weak self] in
self?.pinFooter(for: state)
})
}
var panScrollable: UIScrollView? {
return self.tableView
}
var longFormHeight: PanModalHeight {
return .contentHeightIgnoringSafeArea(self.contentHeight)
}
var shortFormHeight: PanModalHeight {
return .contentHeightIgnoringSafeArea(self.minContentHeight)
}
}