scenee / floatingpanel Goto Github PK
View Code? Open in Web Editor NEWA clean and easy-to-use floating panel UI component for iOS
License: MIT License
A clean and easy-to-use floating panel UI component for iOS
License: MIT License
When using a UIVisualEffectView
as the background of the content view, the background is displayed in a blurred style. Now when the panel is moved to the .full
position, the backdropView
is faded in, e.g. with 30% black. Since the backdropView
doesn't mask the surfaceView
, the blurred background gets slightly darker.
Would be great to fully replicate the Maps.app behavior where only the backdrop view is faded in but the panel background color is unchanged.
The issue can best be observed in the Maps sample app.
Please see my variation of the sample app where I added a panel presented below a subview which uses a UITableViewController as content vc that has only 3 cells.
When I pan rather slowly, the panel doesn't track the movement and one can not change the panel positions. Only a fast swipe works but the gesture in general becomes very hard to use.
The problem seems to be related to the contentSize of the tableView. When I increase the number of cells to 20, everything works fine (albeit with the bottom inset problem I described).
Hi, nice library! Thanks for providing such helpful library!
Is there any way for us to activate the panel just by tapping on the top of the panel?
Thanks!
I discovered that my viewcontroller, which has a FloatingPanelController added to it, never gets released. The offending line seems to be this one in my viewDidLoad:
self.fpc.addPanel(toParent: self)
If I comment out that line, my VC gets released when it should, so I'm guessing that this creates a retain cycle of some kind. Any ideas on how to break this cycle?
I store my FloatingPanelController as a property in my VC and the FloatingPanelController's contentController is stored in a weak property in my VC.
On the Maps example and running it on an iPhone without notches (5s or 6), when the main ViewController has preffersStatusBarHidden set to true, the TableView doesn't appear anymore:
Is there a way to solve that issue so that the floating panel can appear even if the ViewController don't have a status bar?
I'm trying to make same behaviour like in google maps:
Move panel to .half position every time i click on button on tabbar. The problem that when i dismiss panel by swipe, fpc is not nil and position is set to .tip, so i'm trying to move it to .half instead of presenting panel
if fpc == nil { //presenting new instance of fpc } else { // if fpc.position is .tip or .full - move panel to .half, else just addPanel. So when i dismiss by gesture my app is crashing while i'm trying to show panel again }
Or maybe i should check another parameter for this?
Thank you forehead
Not sure what I am doing wrong here. I am trying to set up and getting this error:
Value of type 'FloatingPanelController?' has no member 'set'
at this line:
fpc.set(contentViewController: contentVC)
I tried to use fps.show(contentViewController: contentVC) but then only get the clear background,
Hi
How to open the panel with the custom height value?
Hello, first of all, thank you so much for sharing this library. It's very awesome.
However, I found that there is an issue (please check my screenshot and video)
I set hidesBottomBarWhenPushed = true on TestVC. When running on iOS 10, there is a white bar in bottom, but it did not happen on iOS 11.
Here is the video when running the demo on simulator:
https://youtu.be/WIwcAvNYEIs
I attached the demo as well in "Samples" target
Hope you could help. Thank you!
Hi @scenee, I wanted to request an enhancement to FloatingPanel, and to say thank you for the work you've put into it.
I've modified the Stocks example to reflect the changes I'd like to see ideally.
Currently on an iPhone X/XS up to the red bar is the highest that a floating panel can go up to when setting insetFor(position: FloatingPanelPosition) -> CGFloat?
.
The user experience I'm trying to recreate allows the view controller to go full screen, more akin to what you see in the Uber and Lyft apps. Below is what it would look like in the ideal circumstance, if I was able to set to a ViewController's topAnchor
rather than it's safeArea's topAnchor
.
I messed around with the stocks example for a bit but ran into edge cases, and was wondering if you would consider implementing functionality that would allow this to be a part of the library itself.
Please let me know if I can be more clear, provide more information, or be helpful another way. Thank you so much!
As a follow up of the offset changes mentioned at the end of #30, this issue occurs when the content view controller is a scroll view, e.g., a UITableView
which gets scrolled programmatically, e.g., by using scrollToRow(at: IndexPath, at: ScrollPosition, animated: Bool)
.
Moving the floating panel, e.g., from .half
to .full
after using scrollToRow
or similar programmatic ways to scroll a scroll view should not cause a change in the contentOffset
of the scroll view.
Video
Moving the floating panel after using programmatic ways to scroll a scroll view will set the contentOffset
to contentOffsetZero
causing the content to jump to the top.
Video
Changing the code to achieve the expected result would also cause that manually scrolled scrollviews are not at contentOffsetZero
when the floating panel is moved from .full
to .half
, as mentioned in #30 (comment). But this would align with the behavior of Apple's Maps and Stocks apps.
Hi just started using this library, and it is really clean, so well done on that.
Jut wondering how to go about performing a swipe to dismiss style move?
Thanks
Hi @scenee ,
I would like to use this library inside a objective C project. Is there any efficient way to integrate ?
Thank you
How can I show my TableViewHeader? At the moment all my content in the DetailViewController shows (tableview) except for the TableViewHeader. Any Solution?
I have a collection view (horizontal scrolling) nested inside of a scroll view in the floating panel and scrolling does not work.
Any suggestion on a fix?
Hi,
thank you for this great library. I faced a sneaky corner case that would require me to be able to interrupt my UIViewPropertyAnimator in specific conditions (I hide the tab bar dynamically as the FloatingPanel is revealed and if the user is fast enough, state is not properly synchronized). This is due to the fact that the animation is still ongoing and further seems not enqueued but rather ignored.
My workaround consists in stopping the animation (if any) before creating a new interruptible UIViewPropertyAnimator.
But in https://github.com/SCENEE/FloatingPanel/blob/v1.2.0/Framework/Sources/FloatingPanel.swift#L445 you force the animator to be un-interruptible, which causes the app to crash. If I comment out this, everything is ok for me.
Do you think we can provide a way to setup this flag ?
If it is a yes, I'll be happy to contribute such a feature.
In the delegate method floatingPanelDidMove
the position property of the FloatingPanelController is still the old, previous position. Why is this? After the panel has been moved up or down, I would expect the position property to return the new position (tip, half or full) after the move.
Also, floatingPanelDidMove doesn't seem to be called when the position is changed programmatically?
I'm trying to detect when the panel is moved, by the user or programmatically, and run different code depending on the new position.
When using the floating panel in the iPad version of my app, when the iPad is rotated, the panel’s layout doesn’t update. You can see the issue with the panel when the device rotates from being landscape to portrait, the floating panel floats above the bottom of the screen. Note that I am using a custom layout for the iPad version as seen in the example project.
And when rotating back to landscape the floating panel’s layout is correct.
Also note that the incorrect layout occurs when the device rotates into a different orientation than when my app is opened, so the issue occurs with both landscape and portrait orientations.
When I drag floating panel from half position up or down (it does not matter) and tap backdrop view by second finger it closes and immediately crashes with error:
Fatal error: A floating panel hidden must not be used by a user
It caused by handling pan gesture cancelled
like ended
here:
@objc func handle(panGesture: UIPanGestureRecognizer) {
...
case .ended, .cancelled, .failed:
panningEnd(with: translation, velocity: velocity)
How can I fix it?
When I used the test application i noticed that the small view on the left side could use some stretching. Having it compact is nice but when you want to type to search, IMO, you arent looking at how aesthetically pleasing it is. Functionality would trump all else.
I would love to give a go at this feature.
I am facing a problem with changing the ScrollView
of my PanelController
. I have added a UIPageViewController
to my PanelController
which shows one TableView on each page and should be the scrollview being tracked when presented. The viewcontroller which presents the panel is being notified about changes by the PageViewController
.
This is looking like the following:
func timetableViewController(_ timetableViewController: TimetableViewController, didChange index: Int, with viewController: TimetableItemViewController) {
viewController.tableView.delegate = viewController
panel.track(scrollView: viewController.tableView)
}
I have debugged this and found that scrollViewDelegate
of FloatingPanel
is actually overwritten but does not respond to scrolls except to the first TableView being initialised.
Please let me know if I should provide you with a Demo Project.
Thanks in advance!
Is something like this possible with FloatingPanel
library?
Instead of panel starting on the bottom of the screen, it would be on top and would silde down with a pull gesture.
When trying out the floating panel, the panel’s height is correct and the bottom safe area sticks to the screen correctly as seen in this screen shot...
But when the large title navigation bar shrinks to the normal size due to scrolling the collection view on my home VC, pulling up the floating panel quickly causes the bottom safe area constraint to be above the bottom of the screen as seen in this screen shot... notice the large grey bar underneath the pink button, that shouldn’t be there
But when panning the floating panel, it snaps back down so that the bottom of my stack view touches the bottom safe area of the home vc as the stack view in the floating panel has a constraint to the bottom safe area of the floating panel. This snapping also causes the floating panel to ignore my 18 point gap inset between the top of the panel and the nav bar which is set in the panel’s custom layout
Hey, I'm just starting to use your lib and wondering why you don't allow the contentViewController to be an UITableViewController
. The reason UITableViewController should not be the parent because the view hierarchy will be break in reusing cells.
seems a little bit odd to me. It's too bad to lose all features from UITableViewController...
Or maybe I'm doing something wrong and it's possible? Thanks.
Hey. Thank you so much for your work.
In version 1.1, I got a problem.
If I forbid .full state, then when I swipe up, the background turns black. I recorded a video
https://monosnap.com/file/3zniKRgfEHHnCXIYZCc4bSUQsDMNhf
extension CurrentFlightViewController: FloatingPanelLayout {
public var initialPosition: FloatingPanelPosition {
return .tip
}
public var supportedPositions: Set<FloatingPanelPosition> {
return [.tip, .half]
}
public func insetFor(position: FloatingPanelPosition) -> CGFloat? {
switch position {
case .tip:
return insuranceViewController.headerHeight
case .half:
return insuranceViewController.allContentHeight
default:
return 0
}
}
public func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
if #available(iOS 11.0, *) {
return [
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 0.0),
surfaceView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: 0.0)
]
} else {
return [
surfaceView.leftAnchor.constraint(equalTo: view.layoutMarginsGuide.leftAnchor, constant: 0.0),
surfaceView.rightAnchor.constraint(equalTo: view.layoutMarginsGuide.rightAnchor, constant: 0.0)
]
}
}
}
Could you help me with this issue?
Thanks!
In the 1.2 version of the framework, the floating panel in my app that is used as a pull up calculator worked well but after upgrading to 1.3, the floating panel refuses to even display at all. The strange part is that when using the user interface inspector in Xcode I can see that the floating panel is there and is the same height as my home VC. You can see the working version and non-working version below...
The strange part is I can get it to show up by setting the initial position as .full but it doesn't render correctly as only the handle of the floating panel shows up as well as the home VC being darkened...
Another weird issue is that unlike 1.2, 1.3 doesn't actually show the whole of my calculator VC on screen, its like its ignoring the safe area again like the other issue I had in 1.1. you can see that here
What it should look like....
I've tried for 3-4 hours to get it to work, the only thing that got close to what it used to look like on 1.2 was to use self.present(floatingPanel, animated: true, completion: nil)
as suggested by you in the 1.3 branch which allowed the .tip and .full custom insets to work correctly. Unfortunately, this doesn't allow the collection view in the background to be panned until the floating panel is popped or dismissed.
You can see that here...
This is the only code that I am using..
Inside view did load
` //setting calculatorVC the calculator class
let storyboard = UIStoryboard(name: "Features", bundle: nil)
let calculatorVC = storyboard.instantiateViewController(withIdentifier: "calculator") as! CalculatorViewController
//set the delegate for the calculator
calculatorVC.calculatorDelegate = self
//set up floating panel
floatingPanel.delegate = self
floatingPanel.isRemovalInteractionEnabled = false
floatingPanel.set(contentViewController: calculatorVC)
floatingPanel.surfaceView.cornerRadius = 12.0
floatingPanel.surfaceView.borderColor = UIColor.black
floatingPanel.surfaceView.borderWidth = 0.5
floatingPanel.surfaceView.backgroundColor = darkThemeColors.tableViewCellColor
floatingPanel.surfaceView.shadowHidden = false
floatingPanel.addPanel(toParent: self, belowView: nil, animated: true)`
extension HomeViewController: FloatingPanelControllerDelegate {
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? {
return calculatorFloatingPanelLayout()
}
}
class calculatorFloatingPanelLayout: FloatingPanelLayout {
var initialPosition: FloatingPanelPosition {
return .tip
}
var supportedPositions: Set<FloatingPanelPosition> {
return [.full, .tip]
}
public func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
if UIDevice.current.userInterfaceIdiom == .pad {
//iPad layout
if #available(iOS 11.0, *) {
return [
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 20.0),
surfaceView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -20.0)
]
} else {
return [
surfaceView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20.0),
surfaceView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20.0),
]
}
}
//iPhone layout
else {
return [
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 0.0),
surfaceView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: 0.0)
]
}
return [NSLayoutConstraint]()
}
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
switch position {
case .full: return 16.0
case .tip: return 90.0
default: return nil
}
}
}`
```
Im sorry if its a simple mistake but it is completely escaping me on what I am doing wrong. Keep up the awesome work on this library.
Hello! First off, thanks for all your work on this Framework.
I'm running into an issue where the frame of a FloatingPanel Controller isn't being updated when I change it's layout and contentView.
For example, I initialize a FPC with a specific contentVC and Layout and add it to a parent VC. I then later tap a button to swap out the FPC's contentVC and change its Layout and do something like:
self.floatingController.set(contentViewController: vc1)
self.activeLayout = VC1FloatingPanelLayout()
self.floatingController.updateLayout()
The Layout and contentVC changes happen successfully, but if the .full position inset of the original Layout is larger than the .full position inset of the new layout, you end up with a gap of that difference at the bottom of the FPC as shown in the image.
All the viewControllers I'm using are made in Storyboards and use Autolayout/Safe Area Margins.
If you want more info or I'm doing something wrong, let me know!
I have a use case where I need to show FloatingPanel above some view.
I have a container viewcontroller which contains view controller with FloatingPanel and when FloatingPanel is scroll up, I want it to be above the container viewcontroller's navigation bar. Is this possible.
I've run into a couple of problems when trying to present view controllers and wanted to raise them in case I'm using the API wrong, or in case it's something that can be fixed. I've attached a sample project with the way to reproduce the issues I'm seeing, running them on an iPhone XS. The three files to look at are KeyboardTestViewController.swift
(addition), KeyboardLayoutGuide.swift
(addition), and ViewController.swift
(modification to add two new test cases - .showFullScreenExampleModal
and .showFullScreenKeyboardExampleModal
).
The first issue is when presenting a view controller directly to the .full
position using the new .present
API. The animation doesn't seem to present as smoothly as I would expect, compared to the other options.
To see how this works, run the updated sample project with the "Show Full Screen Example Modal" option (.showFullScreenExampleModal
).
The second issue is when presenting a view controller with a UITextField
with the .present
API. In my case the UITextField is the first responder, and pinned to a a keyboard layout guide, which works perfectly when using UIKit's present viewController API, other third party options, but not when using FloatingPanel. The problem seems to be on initialization of the presentation, as you can see the problem corrects itself when hiding and showing the keyboard (by pressing cmd + k in the simulator, which is obviously not an option on a device).
To see how this works, run the updated sample project with the "Show Full Screen Example Modal" option (.showFullScreenExampleModal
).
Let me know if you need any more information, happy to help assess and debug as needed. Thanks for all the hard work @scenee!
Currently, it appears you cannot change the style or position of the grabber handle. I'm building an app that calls for a different width, height, corner radius, and background color compared to the default style. It would be great to allow this to be configurable.
Hi, I have found a strange problem.
If I add any gestures to the panel - the gestures will be not recognized.
Can you have an add-on you example additional gestures recognizing?
Thanks in advance.
Hi,
First of all thank you so much for the beautiful library.
I am facing an issue when I have a horizontal collection/scroll view inside the content view controller. It handled well for vertical scroll but not for horizontal I suspect surface view's pan gesture recognizer collide with the scroll. It would be really appreciable if you help me to fix this.
Thanks in advance
Thavasi
I have UI similar to your tab bar with floating panel example just 10px will be shown initially. Here I would like to drag the view even if I drag the tab bar. In other words, I have to detect the tab bar drag event and pass it to the underneath view.
I tried passing all the tab bar touch event to underneath view by overriding pointInside
withEvent method but tab bar actual behavior (tap detection) stopped working.
Is there any way to differentiate the touch and drag event based on that can we pass it to the required view? if not, can you please guide me how to solve this?
I set the floating panel as follow
// Initialize FloatingPanelController
floatingPanel = FloatingPanelController()
floatingPanel.delegate = self
// Initialize FloatingPanelController and add the view
floatingPanel.surfaceView.backgroundColor = UIColor(displayP3Red: 30.0/255.0, green: 30.0/255.0, blue: 30.0/255.0, alpha: 1.0)
floatingPanel.surfaceView.cornerRadius = 12.0
floatingPanel.surfaceView.shadowHidden = true
floatingPanel.surfaceView.borderWidth = 1.0 / traitCollection.displayScale
floatingPanel.surfaceView.borderColor = UIColor.black.withAlphaComponent(0.2)
appointmentsRoot = storyboard?.instantiateViewController(withIdentifier: "AppointmentsRootVC") as? AppointmentsRootVC
// Set a content view controller
floatingPanel.set(contentViewController: appointmentsRoot)
floatingPanel.track(scrollView: appointmentsRoot.tableView)
floatingPanel.addPanel(toParent: self, belowView: self.tabBarController?.view, animated: false)
and it works fine in iOS 12
but in iOS 10 and iOS 11 contentviewcontoller has a gray layer on it.
any idea how to resolve this>
I have downloaded the sample then I changed numberOfRowsInSection to 20. there is an issue on the last cell of table view (Hidden).
I hesitate to add this here, as I'm sure I am just doing something wrong.
I am having some trouble with segues. I would like to perform some segues from the floating panel. Currently when I do this, it brings the new view controller in and it replaces the floating panel. I'd like the segues to perform just as they would from a normal view controller.
I hope I described this adequately and I really appreciate any help on this.
Hi
How can I open the panel first time in tip position ?
Hi this looks really great, I have a question more than an issue, is it possible to initially fit the height to content, instead of using the 3 positions?
If content is larger than screen height, it should go to .full and scroll should be enabled.
When I embed and track a scroll view in the floating panel, and the position is .full
, it is not possible to close the panel without scrolling to the top of the scroll view.
I'd expect to be able to drag the panel using the grab handler regardless of the scrolling position, but this doesn't work.
I tried to investigate the behavior: to solve the issue I think it is necessary to somehow avoid calling this code-path when the pan was started at the grab handler, what do you think?
After looking through the sample project and seeing how content in the drawer is correctly pinned to the bottom of the safe area thus preventing any content from being off screen. How ever, when trying to make the floating panel work with an existing view controller in my own project, any views that are pinned to the bottom safe area are cut off. With further testing by creating a test project, the issue doesn't occur as seen in the following screen shot.
But the same pink rectangle is cut off in my own project..
Not entirely sure if its a project issue with mine or some code that you have used in the sample project.
How can I relayout again after floatingpanelposition inset value was changed?
Hi and thanks for the library :)
I've been trying to add a gesture recognizer to the backdropView
to dismiss the panel, but I can't get it to work. It's like if the selector attached to the gesture never gets called.
I'm on the last version.
This is how I do it (as described here https://github.com/SCENEE/FloatingPanel#add-tap-gestures-to-the-surface-or-backdrop-views)
let backdropTapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleBackdropTapGesture(_:)))
self.floatingPanelController.backdropView.addGestureRecognizer(backdropTapGesture)
and
@objc private func handleBackdropTapGesture(_ gesture: UITapGestureRecognizer) {
// Never gets called
print("DISMISS")
}
If I add the tapGesture on the surfaceView
everything works.
Any idea?
Thanks!
I have a use case where in some cases I need to hide the floating panel entirely, preferrably with a slide-to-bottom animation.
What would the most appropriate way to do this?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.