Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pan modal with fixed view bottom #180

Open
CavalcanteLeo opened this issue May 21, 2022 · 3 comments
Open

Pan modal with fixed view bottom #180

CavalcanteLeo opened this issue May 21, 2022 · 3 comments

Comments

@CavalcanteLeo
Copy link

Is it possible to open panModal with fixed view bottom like instagram?

Upload.from.GitHub.for.iOS.MOV
@anh-le9-tiki
Copy link

I have the same problem with this issue. Can anyone help us?

@concentrat1on
Copy link

In PanModelPresentable you can find topOffset that will help you

@ilia3546
Copy link

ilia3546 commented Sep 5, 2022

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)
    }
    
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants