Coder Social home page Coder Social logo

ofthewolf / ubottomsheet Goto Github PK

View Code? Open in Web Editor NEW
606.0 7.0 67.0 27.14 MB

iPhone Maps App bottom sheet - A Protocol Oriented Approach

License: MIT License

Swift 96.90% Ruby 3.10%
swift4 apple-maps iphone ios swift bottomsheet bottomsheetbehavior bottom-sheet

ubottomsheet's Introduction

Next Level

Language Version Platform License

Features
📍 Apple Maps bottom sheet behaviour
Supports panning with UIScrollView and UIView
🌒 Allows adding dimmable background view
📱 Set sheet content as UIViewController or UINavigationController
📖 Navigation inside sheet with UINavigationController
🌈 Add one or more sheet stop positions
🎯 Change sheet position programmatically
🌀 Present as many sheet as you want
🚀 Move multiple sheets simultaneously or seperately
🏹 Rubber banding
👋 Dismiss at bottom

Demos

Apple Maps & Childs Navigation In Sheet Pull To Dismiss Multiple Sheet Positions
Tag Tag Tag Tag

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Bottom sheet child view controllers must conform to the Draggable protocol.

class MapsDemoBottomSheetController: UIViewController, Draggable{
    @IBOutlet weak var tableView: UITableView!
      
    override func viewDidLoad() {
        super.viewDidLoad()
        if #available(iOS 11.0, *) {
            tableView.contentInsetAdjustmentBehavior = .never
        } else {
            automaticallyAdjustsScrollViewInsets = false
        }
        tableView.delegate = self
        tableView.dataSource = self
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        //adds pan gesture recognizer to draggableView()
        sheetCoordinator?.startTracking(item: self) 
    }

//  MARK: Draggable protocol implementations

    var sheetCoordinator: UBottomSheetCoordinator? 

    func draggableView() -> UIScrollView? {
        return tableView
    }
}

Create a UBottomSheetCoordinator from the main view controller. Use the UBottomSheetCoordinator to add and configure the sheet.

// parentViewController: main view controller that presents the bottom sheet
// call this within viewWillLayoutSubViews to make sure view frame has measured correctly. see example projects. 
let sheetCoordinator = UBottomSheetCoordinator(parent: parentViewController)

let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MapsDemoBottomSheetController") as! MapsDemoBottomSheetController

vc.sheetCoordinator = sheetCoordinator

sheetCoordinator.addSheet(vc, to: parentViewController)

Requirements

ios9.0+, Xcode10+

Installation

UBottomSheet is available through CocoaPods and Swift Package Manager.

CocoaPods

Add the following line to your Podfile:

pod 'UBottomSheet'

SPM

Add this url in XCode > File > Swift Packages > Add Package Dependency:

https://github.com/OfTheWolf/UBottomSheet

See Also

TwitterProfile Nested scroll view behaviour of Twitter Profile screen.

Author

uğur, [email protected]

License

UBottomSheet is available under the MIT license. See the LICENSE file for more info.

ubottomsheet's People

Contributors

canakkaya1 avatar dmiluski avatar dominicholmes avatar ofthewolf avatar rocketnik avatar vladimirdrndarski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ubottomsheet's Issues

Make the sheet modal?

Instead of passing taps through to the underlying view controller, what is the most straight forward way to intercept them in order to dismiss the sheet, thereby treating the sheet as modal?

SPM install error

Hello, I can't install library with SPM. Error is on the screenshot.

Снимок экрана 2020-10-14 в 10 02 07

Not Scrolling on Pan gesture - issue when adding in TAB Bar controller ,

Hello,

When i am loading screen first time then the Bottomsheet stuck at the initial position not expanding to higher position. When i am going to some other screen in navigation and coming back to initial screen then Bottom sheet starts expanding. This is repeating ever time on first time load. Can you help me out.

Convert initialPosition to initialHeight

Hi, thanks for the great library.
I think you should change initialPosition to initialHeight. Developers should not worry about the position, they almost always working on height property.

Thanks.

Bottom sheet cannot be dragged gradually when UIScrollView contents are able to fit the screen and bounce is off

Thanks for the awesome library!

There's a small issue where the bottom sheet cannot be dragged gradually but will jump instantly to the next position if all contents in a scroll view are able to fit and scrollView.alwaysBounceVertical = false (which is the default in current iOS version)
No issue when content is long enough that it can be scrolled even when bounce is disabled.

The workaround is to set alwaysBounceVertical = true, however this will have a minor side effect of having the content to bounce when user tries to scroll the content up, even though content fits the screen.

Cheers
Bruce

topY position

I'm trying to set my topY to be a little bit higher on the screen.

When setting it to:
UIScreen.main.bounds.height * 0.10
It moves up great and the table scrolls, however when I change to:
UIScreen.main.bounds.height * 0.05
The bottom sheet moves up to my ideal position but the tableview will no longer scroll. I'm assuming I have to adjust something in the handlePan func?

How do I implement sheetPositions in SheetViewController?

In my SheetViewController I copied the default implementations of UBottomSheetCoordinatorDataSource

public func sheetPositions(_ availableHeight: CGFloat) -> [CGFloat]{
    return [0.2, 0.7].map{$0*availableHeight}
}

public func initialPosition(_ availableHeight: CGFloat) -> CGFloat{
    return availableHeight*0.2
}

and set breakpoints.

extension UIViewController: UBottomSheetCoordinatorDataSource{}

adds the methods to all UIViewController.

I set the SheetViewController as dataSource before adding it:

    sheetCoordinator.dataSource = transitPlanViewController
    sheetCoordinator.addSheet(transitPlanViewController, to: self, didContainerCreate: { container in
        let f = self.view.frame
        let rect = CGRect(x: f.minX, y: f.minY, width: f.width, height: f.height)
        container.roundCorners(corners: [.topLeft, .topRight], radius: 10, rect: rect)
    })

Whatever I do, breakpoints show that always the methods of the default implementations are called, not the methods in the SheetViewController.
The reason to implement it in the SheetViewController is because the SheetViewController knows the size of the data it displays.

What do I have to do that my implementation is called?

I try to do what #33 suggests.

In the examples, there is no UIViewController that implements this methods.

Use with different view type

More like a feature as an issue: How to use the example with a different view type (for example UIStackView). I can’t get it to work and it seems to be pretty tight coupled to the tableView.

Keyboard does not pull the card up

In the first "Apple Maps" example, if you tap on the search field, the card should pull up automatically to allow text input. This seems broken / half-baked.

DimmingBackView not dimming gradually when bottom sheet slides to its initial position the first time

First of all thanks for this wonderful library! After experimenting with different libraries for bottom sheet (including Pulley) I find this library really easy to implement on existing project whereas some of the other libraries like the popular Pulley require major redesign of the view controllers navigation stack. It has all the features that's expected of how a typical user uses the bottom sheet, including the pull to dismiss (which pulley lacks!), and it includes working example! thanks a lot

There's this UI bug that I found, which can be reproduced on the example project. On the Apple Map example, if we uncomment the line self.addBackDimmingBackView(below: container!) in MapsViewController it dims the backview when user drags the bottom sheet up or down. However the effect does not apply when the sheet slides into its initial position when the sheet appears for the first time. It just dims to 100% all of the sudden after the animation is over.

The delegate method bottomSheet(_ container: UIView?, didChange state: SheetTranslationState) with state .progressing is not called for the initial slide in, only .willFinish and .finished state gets called.

Thanks and cheers
Bruce

Fixed minor bug

There is a comment in this answer by Jakub Truhlář at Sep 25 at 13:13.
https://stackoverflow.com/a/51768193/8963597

I applied this comment to this code, and what I did was, adding this to BottomSheetViewController
` var initialContentOffset: CGFloat = 0

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
	initialContentOffset = tableView.contentOffset.y
}`

and modify one line in handlePan():

let maxY = max(topY, lastY + dy - initialContentOffset)

now, when user drags down while table view shows, like from cell 3, after table shows cell 1 fully, the bottom sheet goes down without any jump.

How to catch when sheet is dismissed? in delegate or other method

First of all thank you for your source.

I'm trying to find out how to know when the sheet is dismissed? I have tried to find it inUBottomSheetCoordinatorDelegate but there is only there three function. I'm using Pull To Dismiss` as in the demo. Could you help me with it?

tableView( didSelectRowAt ) is not called after...

Before applying commit "topY related bug fix (issue #3)" or after, this problem persists:

after I close the bottom sheet vc, if I make it show to the top, then tapping the first time on one of the table view cells doesn't call tableView( didSelectRowAt ) on (though cell's background becomes darker then normal during tapping). Tapping the second or later time, or after I scroll the tableView of bottom sheet vc, it works very good.

For better understanding, I made a short captured video and uploaded into dropbox:
https://www.dropbox.com/s/322x7bk30ndh2d8/ScreenRecording_11-17-2018%2019-26-20.MP4?dl=0

I moved down the bottom sheet vc by hand first. then when it is showed up, tapping on one of the cells doesn't work (though you can see the cell's background becomes darker for a second), but then the second time, it works.

Now I applied your commit.

[Question] Way to scroll tableView in middle position?

Hi!

Good job on this! I love it! I added and extra position to the default one, so now I have 3. I have a tableview in the sheet and I'd like to it to be able to scroll the table view cells while in the middle position. It works when on top but in the middle it only detects my swiping so it goes down or up.

Is there a way to add this? I imported your code so I can make modifications.

Thanks!

Not able to build and run UBottomSheet-Example using XCode 12.3 (12C33)

Hi,

Thank you for creating useful bottom sheet library.

I am using Xcode 12.3 (12C33), macOS Catalina 10.15.7

I tried to build and run UBottomSheet-Example, after checkout without any modification, from [email protected]:OfTheWolf/UBottomSheet.git

I am getting the following error

/Users/yccheok/Desktop/github/UBottomSheet/Example/Pods/Target Support Files/Pods-UBottomSheet_Example/Pods-UBottomSheet_Example.debug.xcconfig: unable to open file (in target "UBottomSheet_Example" in project "UBottomSheet")

Screenshot 2021-01-02 at 9 55 15 PM

Note that, under Pods and Frameworks, I am getting red color error.

Screenshot 2021-01-02 at 10 02 25 PM

Any idea on how to resolve this is very much appreciated. Thank you.

Sheet becomes stuck in any position in a specific condition, and how to make the offset using Safe Area?

Really love your approach in making the bottom sheet.

I've followed your setup and I got it working but I seem to have a problem when I pan down the tableview until the sheet is dragged down in any place, then pan back up, the sheet becomes stuck until I scroll to the top of the tableview. Is there a way to fix it? (logically, a way to disable the tableview scroll during the pan gesture handling, but I still haven’t found the correct way)

The whole sheet:
Simulator Screen Shot - iPhone SE - 2019-09-15 at 08 41 21

Described case:
Simulator Screen Shot - iPhone SE - 2019-09-15 at 08 40 59

Storyboard:
Screen Shot 2019-09-15 at 8 44 07 AM

Maybe with this approach it is not allowed to have buttons at the bottom part?

Second question: How can I make the offset universal? Seems like the offset is determined programmatically fixed, but when I correctly set up in the iPhone X style, the bottom sheet in the iPhone 5s/SE becomes too high

Check if a ViewController is attached

Is there anyway to check if a viewcontroller is attached because if i by mistake pop a view controller while a bottom sheet controller is attached, the app crashes. Any way to prevent it?

Thanks for this awesome library it is really easy to use!

Handle provided at the top of bottom sheet is not responsive

If tableview is scrolled (content offset is not equal to zero), user is not able to drag the bottom sheet down. User has to scroll the total tableview down (make content offset as zero) then only user can drag bottom sheet.
Is there any way we can drag bottom sheet up/down using the handle provided at top irrespective of the scrollview content offset.

let location = pan.location(in: panView)
location.y <= 50

How to use the code way?

Thank you very much for contributing this library.

func initContainer() {
        container = UIView()
        container.backgroundColor = UIColor.white
        container.frame = CGRect(x: 0, y: 300, width: boundW, height: boundH/2)
        container.layer.cornerRadius = 15
        container.layer.masksToBounds = true
        view.addSubview(container)
        
        bottomSheetController = BottomSheetController()
        bottomSheetController.bottomSheetDelegate = self
        bottomSheetController.parentView = container
        addChildViewController(bottomSheetController)
        container.addSubview(bottomSheetController.view)
        bottomSheetController.didMove(toParentViewController: self)
        
    }

In this way, I seem to have problems, the table can't scroll

Use case: make positions depend on size of content of sheet

I'd like to have positions of the sheet depend on the size of the content of the sheet.

There were some tweaks necessary, but it works for me now.

One problem I had to fix is this:
UBottomSheetCoordinator sets its private variables minSheetPosition and maxSheetPosition very early in the process, when it is impossible that the sheet had any chance to do a layout. So the height of the content of the sheet is 0 at this point of time.

my workaround is this:

    self.sheetCoordinator.addSheet(transitPlanViewController, to: self, didContainerCreate: { container in
        let f = self.view.frame
        let rect = CGRect(x: f.minX, y: f.minY, width: f.width, height: f.height)
        container.roundCorners(corners: [.topLeft, .topRight], radius: 10, rect: rect)
        self.sheetCoordinator.dataSource = dataSource // side effect: calculates private vars minSheetPosition + maxShetPosition from size of sheet after layout
    })

You might decide to do something similar in the library.
Maybe even a public method to recalculate minSheetPosition and maxSheetPosition after rotating the device.

Customise drop shadow radius and height.

Hello @OfTheWolf can you please tell how to customise the drop shadow radius.

I tried using as below but of no use

sheetCoordinator.addSheet(vc, to: self, didContainerCreate: { container in
            container.newShadow() //<<<============= here
            let f = self.view.frame
            let rect = CGRect(x: f.minX, y: f.minY, width: f.width, height: f.height)
            container.roundCorners(corners: [.topLeft, .topRight], radius: 25, rect: rect)
        })

Can you please help.

Greish background

Is it possible to somehow add background view which disable interaction with parent view?

Use CollectionView with horizontal scroll direction

Hi, thanks for the awesome library.
Just one to ask, I use collection view instead of table view, it works well when it is on vertical direction.
But, when I use horizontal direction, it does not goes well, when I swap the collection view to right or left, the sheet keep move along upside and downside sometime., I have used below code, but still does not work.

   override var scrollView: UIScrollView?{
        return collectionView
    }

Thanks.

How do you change the position of the initial height of the modal view?

In the default behavior, the modal view pops up to fill 3/4 of the screen. In my particular use case, it'd be nice of the view only fill the bottom 1/4 of the screen. Changing the initial height and y in didContainerCreate has no effect:

` sheetCoordinator.addSheet(modal, to: self, didContainerCreate: { container in

        let f = self.view.frame
        
        container.frame = CGRect(
              x: 0
            , y: f.height*3/4 
            , width: f.width
            , height: f.height/4
            
        )`

TableView inside Sheet controller won't register first touch.

So I have a tableView inside the sheet controller and the didSelectRowAtIndexpath doesn't trigger on first touch right after I end up dragging the sheet. After dragging the sheet then all touches are working as expected in the cells, however if you scroll the sheet again it will make the bug happen again. Any idea of what could be happening?

Can't override UBottomSheetCoordinatorDataSource functions

Hello!

Thanks for all your work on this project.

For some reason, I'm having difficulties overriding sheetPositions and initialPosition.

When I add this to my UIViewController:

    func initialPosition(_ availableHeight: CGFloat) -> CGFloat {
        return 1
    }

nothing happens. Default functionality continues. And when I debug what the coordinator is calling, the default function's value is returned.

I have verified I am calling UBottomSheetCoordinator(parent: self) and adding the initialPosition function to self.

I've also tried what the examples say (haven't actually tried running them) like this:

import UBottomSheet

class MyDataSource: UBottomSheetCoordinatorDataSource {
    func initialPosition(_ availableHeight: CGFloat) -> CGFloat {
        return 1
    }
}

but that doesn't work, UBottomSheetCoordinator.dataSource is private so I can't set it directly like in SimpleViewController.swift,

Any help is appreciated!

Bottom sheet has starting position at the bottom of the screen

@OfTheWolf Hi! Thanks for posting this demo! It is a great starting point. My question is:

I would like the default position of the sheet to be the bottom position, but I could not find a way to do that.

Some things I tried that did not work:

  • Setting container.frame in the main ViewController's viewWillAppear
  • Increasing the container's Top Space constraint in the assistant editor
  • In BottomSheetVC: changing lastLevel to .bottom, and calling updateBottomSheet() with .bottomY instead of .middleY

Maybe it is a combination of all of those things that might work but I could not figure it out :( Let me know if you can give me some pointers. Thanks again, it's a great demo!

What if I want to fix the view but still want to make tableView scrollable?

Thank you again for your project. But new issue is that I use bottom up view sometime as bottom up, sometimes as a 'fixed' one using a variable isFixed. But then, just putting the code below inside handlePan() (as well as disable initial resizing) doesn't work.

    if let fixed = isFixed, fixed == true {
        print("\(getTimeMS()) BottomUpVC.handlePan(): isFixed!")
        return
    }

Any solution?

Auto size UINavigationController

Hi there,
How can I implement UINavigationController inside bottom sheet with auto height?
I have a UINavigationController with multiple UIVIewControllers and all views inside are constrained to the "superview" and I've tried using translatesAutoresizingMaskIntoConstraints = false with no success.

Any ideas?
Thanks!

Thank you for providing a good solution.

Thank you for providing a good solution.
But I found a serious problem.
Running without connecting with Xcode will crash.
I used the sample provided.
Running an app built without connecting with Xcode will result in a 100% crash.
Can you determine the cause of the problem?
Thank you.

Quick question...

Is there a way to detect when the user pulls own the sheet? I was wondering because I want to dismiss the keyboard when they close down the view.

Thanks for the help!!

DropShadowView (PassThroughView) is not removed when we call the removeSheet()

When the removeSheet() is called, it only removes the bottom sheet. The dropShadowView behind remains on the screen. Besides, dropShadowView property is private which makes it hard to remove it manually. It would be convenient to add a public function to remove the dropShadowView or automatically remove it from inside the removeSheet()

How can check in bottom sheet have sheet or not?

  1. I want to check if in bottom sheet have sheet, i'll remove it and add new sheet. How can i do? When i click button, first time app will crash:
//app will crash when run first time
_sheetCoordinator.removeSheet()
let MeasurementVC = UIStoryboard(name: "Measurement", bundle: nil).instantiateViewController(withIdentifier: "MeasurementViewController") as! MeasurementViewController
MeasurementVC.sheetCoordinator = self._sheetCoordinator
MeasurementVC.dataSource = MyDataSource()
MeasurementVC.mapView = self.mapView
_sheetCoordinator.addSheet(MeasurementVC, to: self, didContainerCreate: { container in
    let f = self.view.frame
    let rect = CGRect(x: f.minX, y: f.minY, width: f.width, height: f.height)
    container.roundCorners(corners: [.topLeft, .topRight], radius: 10, rect: rect)
})
  1. how can remove sheet when dismiss bottomsheet?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.