Compare commits

..

17 Commits

Author SHA1 Message Date
Shin Yamamoto b7a7e0d4ad Release v1.3.2 2019-01-17 10:47:45 +09:00
Shin Yamamoto dc7f6d58f9 Fix the swift version in podspec 2019-01-17 10:47:45 +09:00
Shin Yamamoto a2a10bd0d3 Merge pull request #104 from SCENEE/fix-modal-presentation
Fix the modal presentation
2019-01-15 09:31:39 +09:00
Shin Yamamoto b54c8ee6ee Merge pull request #105 from CedricGatay/fix/delegateIgnored
fix(delegateIgnored): Allow to set delegate on init
2019-01-14 19:43:39 +09:00
Cedric Gatay 08d275690a fix(delegateIgnored): fix for animation use case
Do not force update layout to allow the user to animate the change later on.
2019-01-14 11:05:50 +01:00
Cedric Gatay 1c307f751e fix(delegateIgnored): listen on delegate change
Properly listens for delegate changes and trigger behavior / layout changes accordingly.

Made constructor parameter name explicit.
2019-01-14 08:49:23 +01:00
Cedric Gatay 6f06a0f7fc fix(delegateIgnored): Allow to set delegate on init
Typical use case is :

```
let floatingVC = FloatingPanelController()
floatingVC.delegate = self
```

This PR allows to set the delegate straight away by using `let floatingVC = FloatingPanelController(self)`
Its true reason is that the setup code that fetches behavior / layout through delegate is called without the delegate being set is useless on `init`ing.
Another implementation could be observing the `didSet` event on delegate to do the `setUp` for `FloatingPanel`
2019-01-12 15:15:48 +01:00
Shin Yamamoto a095ace30e Fix the modal presentation 2019-01-11 19:04:39 +09:00
Shin Yamamoto a486f61f5f Merge pull request #102 from SCENEE/fix-secound-touch-crash
Fix a crash on tap dismissal with a second touch
2019-01-10 09:38:06 +09:00
Shin Yamamoto 14e0abc240 Merge pull request #103 from SCENEE/fix-unsatisfiable-constraints-error
Fix unsatisfiable constraints error for safe area bottom on full state
2019-01-10 09:37:46 +09:00
Shin Yamamoto 32203c48bd Fix unsatisfiable constraints error for safe area bottom on full state 2019-01-10 09:05:17 +09:00
Shin Yamamoto 9d6024f603 Fix a crash on tap dismissal with a second touch
For example, a user tap the backdrop view to dismiss it while dragging
the surface view on presentation modally.
2019-01-10 08:43:46 +09:00
Shin Yamamoto a4dd4e48e7 Merge pull request #98 from SCENEE/support-swift4.1
Support Swift 4.1
2019-01-10 08:25:50 +09:00
Shin Yamamoto e6f7456a0f Merge pull request #101 from SCENEE/fix-touch-handling-on-presentation-modally
Fix the event handling on presentation modally
2019-01-10 08:25:26 +09:00
Shin Yamamoto fca79c9b0c Fix the event handling on presentation modally
If an alpha of the controller's backdrop view is zero, the presentation controller
must not block any touch event outside of surface view.
2019-01-09 09:32:17 +09:00
Shin Yamamoto 4ad7f11e93 Support Swift 4.1 2019-01-05 21:44:05 +09:00
Shin Yamamoto 31faeaada3 Merge pull request #93 from SCENEE/release-1.3.1
Release v1.3.1
2018-12-30 09:52:25 +09:00
12 changed files with 114 additions and 64 deletions
+17 -7
View File
@@ -5,10 +5,10 @@ branches:
- next
cache:
directories:
- build
- vendor
- /usr/local/Homebrew
- $HOME/Library/Caches/Homebrew
before_cache:
- brew cleanup
env:
global:
- LANG=en_US.UTF-8
@@ -16,7 +16,17 @@ env:
skip_cleanup: true
jobs:
include:
- stage: carthage
- stage: Build framework(swift 4.1)
osx_image: xcode9.4
script:
- xcodebuild -scheme FloatingPanel clean build
- stage: Build framework(swift 4.2)
osx_image: xcode10
script:
- xcodebuild -scheme FloatingPanel clean build
- stage: Carthage
osx_image: xcode10
before_install:
- brew update
@@ -24,22 +34,22 @@ jobs:
script:
- carthage build --no-skip-current
- stage: podspec
- stage: Podspec
osx_image: xcode10
script:
- pod spec lint
- stage: check Maps example
- stage: Build maps example
osx_image: xcode10
script:
- xcodebuild -scheme Maps -sdk iphonesimulator clean build
- stage: check Stocks example
- stage: Build stocks example
osx_image: xcode10
script:
- xcodebuild -scheme Stocks -sdk iphonesimulator clean build
- stage: check Samples example
- stage: Build samples example
osx_image: xcode10
script:
- xcodebuild -scheme Samples -sdk iphonesimulator clean build
+2 -2
View File
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "FloatingPanel"
s.version = "1.3.1"
s.version = "1.3.2"
s.summary = "FloatingPanel is a clean and easy-to-use UI component of a floating panel interface."
s.description = <<-DESC
FloatingPanel is a clean and easy-to-use UI component for a new interface introduced in Apple Maps, Shortcuts and Stocks app.
@@ -13,7 +13,7 @@ The new interface displays the related contents and utilities in parallel as a u
s.platform = :ios, "10.0"
s.source = { :git => "https://github.com/SCENEE/FloatingPanel.git", :tag => "v#{s.version}" }
s.source_files = "Framework/Sources/*.swift"
s.swift_version = "4.2"
s.swift_version = "4.0"
s.pod_target_xcconfig = { 'SWIFT_WHOLE_MODULE_OPTIMIZATION' => 'YES', 'APPLICATION_EXTENSION_API_ONLY' => 'YES' }
s.framework = "UIKit"
@@ -7,8 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
54352E9821A521CA00CBCA08 /* FloatingPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54352E9721A521CA00CBCA08 /* FloatingPanelView.swift */; };
54352E9621A51A2500CBCA08 /* FloatingPanelTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54352E9521A51A2500CBCA08 /* FloatingPanelTransitioning.swift */; };
54352E9821A521CA00CBCA08 /* FloatingPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54352E9721A521CA00CBCA08 /* FloatingPanelView.swift */; };
5450EEE421646DF500135936 /* FloatingPanelBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5450EEE321646DF500135936 /* FloatingPanelBehavior.swift */; };
545DB9CB2151169500CA77B8 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 545DB9C12151169500CA77B8 /* FloatingPanel.framework */; };
545DB9D02151169500CA77B8 /* ViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9CF2151169500CA77B8 /* ViewTests.swift */; };
@@ -34,8 +34,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
54352E9721A521CA00CBCA08 /* FloatingPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelView.swift; sourceTree = "<group>"; };
54352E9521A51A2500CBCA08 /* FloatingPanelTransitioning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelTransitioning.swift; sourceTree = "<group>"; };
54352E9721A521CA00CBCA08 /* FloatingPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelView.swift; sourceTree = "<group>"; };
5450EEE321646DF500135936 /* FloatingPanelBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelBehavior.swift; sourceTree = "<group>"; };
545DB9C12151169500CA77B8 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
545DB9C42151169500CA77B8 /* FloatingPanelController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FloatingPanelController.h; sourceTree = "<group>"; };
@@ -406,7 +406,7 @@
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.2;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -432,7 +432,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanel;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 4.2;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
+40 -26
View File
@@ -101,7 +101,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate
}
animator.addAnimations { [weak self] in
guard let self = self else { return }
guard let `self` = self else { return }
self.updateLayout(to: to)
self.state = to
@@ -360,6 +360,12 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate
private func panningEnd(with translation: CGPoint, velocity: CGPoint) {
log.debug("panningEnd")
guard state != .hidden else {
log.debug("Already hidden")
return
}
if interactionInProgress == false {
initialFrame = surfaceView.frame
}
@@ -381,9 +387,9 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate
viewcontroller.delegate?.floatingPanelDidEndDraggingToRemove(viewcontroller, withVelocity: velocity)
self.startRemovalAnimation(with: velocityVector) { [weak self] in
guard let self = self else { return }
guard let `self` = self else { return }
self.viewcontroller.dismiss(animated: false, completion: { [weak self] in
guard let self = self else { return }
guard let `self` = self else { return }
self.viewcontroller.delegate?.floatingPanelDidEndRemove(self.viewcontroller)
})
}
@@ -436,16 +442,20 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate
viewcontroller.delegate?.floatingPanelWillBeginDragging(viewcontroller)
viewcontroller.contentViewController?.view?.constraints.forEach({ (const) in
switch viewcontroller.contentViewController?.layoutGuide.bottomAnchor {
case const.firstAnchor:
(const.secondItem as? UIView)?.disableAutoLayout()
case const.secondAnchor:
(const.firstItem as? UIView)?.disableAutoLayout()
default:
break
}
})
if state == .full {
viewcontroller.contentViewController?.view?.constraints.forEach({ (const) in
switch viewcontroller.contentViewController?.layoutGuide.bottomAnchor {
case const.firstAnchor:
(const.secondItem as? UIView)?.disableAutoLayout()
const.isActive = false
case const.secondAnchor:
(const.firstItem as? UIView)?.disableAutoLayout()
const.isActive = false
default:
break
}
})
}
interactionInProgress = true
}
@@ -459,16 +469,20 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate
lockScrollView()
}
viewcontroller.contentViewController?.view?.constraints.forEach({ (const) in
switch viewcontroller.contentViewController?.layoutGuide.bottomAnchor {
case const.firstAnchor:
(const.secondItem as? UIView)?.enableAutoLayout()
case const.secondAnchor:
(const.firstItem as? UIView)?.enableAutoLayout()
default:
break
}
})
if state == .full {
viewcontroller.contentViewController?.view?.constraints.forEach({ (const) in
switch viewcontroller.contentViewController?.layoutGuide.bottomAnchor {
case const.firstAnchor:
(const.secondItem as? UIView)?.enableAutoLayout()
const.isActive = true
case const.secondAnchor:
(const.firstItem as? UIView)?.enableAutoLayout()
const.isActive = true
default:
break
}
})
}
}
private func getCurrentY(from rect: CGRect, with translation: CGPoint) -> CGFloat {
@@ -497,7 +511,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate
let velocityVector = (distance != 0) ? CGVector(dx: 0, dy: max(min(velocity.y/distance, 30.0), -30.0)) : .zero
let animator = behavior.interactionAnimator(self.viewcontroller, to: targetPosition, with: velocityVector)
animator.addAnimations { [weak self] in
guard let self = self else { return }
guard let `self` = self else { return }
if self.state == targetPosition {
self.surfaceView.frame.origin.y = targetY
self.layoutAdapter.setBackdropAlpha(of: targetPosition)
@@ -507,7 +521,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate
self.state = targetPosition
}
animator.addCompletion { [weak self] pos in
guard let self = self else { return }
guard let `self` = self else { return }
guard
self.interactionInProgress == false,
animator == self.animator,
@@ -616,7 +630,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate
// Distance travelled after decelerating to zero velocity at a constant rate.
// Refer to the slides p176 of [Designing Fluid Interfaces](https://developer.apple.com/videos/play/wwdc2018/803/)
private func project(initialVelocity: CGFloat) -> CGFloat {
let decelerationRate = UIScrollView.DecelerationRate.normal.rawValue
let decelerationRate = UIScrollViewDecelerationRateNormal
return (initialVelocity / 1000.0) * decelerationRate / (1.0 - decelerationRate)
}
+28 -16
View File
@@ -51,7 +51,8 @@ public extension FloatingPanelControllerDelegate {
func floatingPanel(_ vc: FloatingPanelController, shouldRecognizeSimultaneouslyWith gestureRecognizer: UIGestureRecognizer) -> Bool { return false }
}
public enum FloatingPanelPosition: Int, CaseIterable {
public enum FloatingPanelPosition: Int {
case full
case half
case tip
@@ -69,7 +70,11 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
}
/// The delegate of the floating panel controller object.
public weak var delegate: FloatingPanelControllerDelegate?
public weak var delegate: FloatingPanelControllerDelegate?{
didSet{
didUpdateDelegate()
}
}
/// Returns the surface view managed by the controller object. It's the same as `self.view`.
public var surfaceView: FloatingPanelSurfaceView! {
@@ -133,14 +138,15 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
private var safeAreaInsetsObservation: NSKeyValueObservation?
private let modalTransition = FloatingPanelModalTransition()
required init?(coder aDecoder: NSCoder) {
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
/// Initialize a newly created floating panel controller.
public init() {
public init(delegate: FloatingPanelControllerDelegate? = nil) {
super.init(nibName: nil, bundle: nil)
self.delegate = delegate
setUp()
}
@@ -155,6 +161,11 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
behavior: fetchBehavior(for: self.traitCollection))
}
private func didUpdateDelegate(){
floatingPanel.layoutAdapter.layout = fetchLayout(for: traitCollection)
floatingPanel.behavior = fetchBehavior(for: self.traitCollection)
}
// MARK:- Overrides
/// Creates the view that the controller manages.
@@ -269,7 +280,7 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
// 2. The safe area top inset can be variable on the large title navigation bar(iOS11+).
// That's why it needs the observation to keep `adjustedContentInsets` correct.
safeAreaInsetsObservation = self.observe(\.view.safeAreaInsets) { [weak self] (vc, chaneg) in
guard let self = self else { return }
guard let `self` = self else { return }
self.update(safeAreaInsets: vc.layoutInsets)
}
} else {
@@ -312,7 +323,8 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
parent.view.addSubview(self.view)
}
parent.addChild(self)
parent.addChildViewController(self)
view.frame = parent.view.bounds // Needed for a correct safe area configuration
view.translatesAutoresizingMaskIntoConstraints = false
@@ -324,8 +336,8 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
])
show(animated: animated) { [weak self] in
guard let self = self else { return }
self.didMove(toParent: parent)
guard let `self` = self else { return }
self.didMove(toParentViewController: self)
}
}
@@ -340,10 +352,10 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
}
hide(animated: animated) { [weak self] in
guard let self = self else { return }
self.willMove(toParent: nil)
guard let `self` = self else { return }
self.willMove(toParentViewController: nil)
self.view.removeFromSuperview()
self.removeFromParent()
self.removeFromParentViewController()
completion?()
}
}
@@ -361,16 +373,16 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
/// Sets the view controller responsible for the content portion of the floating panel..
public func set(contentViewController: UIViewController?) {
if let vc = _contentViewController {
vc.willMove(toParent: nil)
vc.willMove(toParentViewController: nil)
vc.view.removeFromSuperview()
vc.removeFromParent()
vc.removeFromParentViewController()
}
if let vc = contentViewController {
addChild(vc)
addChildViewController(vc)
let surfaceView = floatingPanel.surfaceView
surfaceView.add(contentView: vc.view)
vc.didMove(toParent: self)
vc.didMove(toParentViewController: self)
}
_contentViewController = contentViewController
@@ -408,7 +420,7 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
if #available(iOS 11.0, *) {
scrollView.contentInsetAdjustmentBehavior = .never
} else {
children.forEach { (vc) in
childViewControllers.forEach { (vc) in
vc.automaticallyAdjustsScrollViewInsets = false
}
}
+1 -1
View File
@@ -226,7 +226,7 @@ class FloatingPanelLayoutAdapter {
}
func updateIntrinsicHeight() {
let fittingSize = UIView.layoutFittingCompressedSize
let fittingSize = UILayoutFittingCompressedSize
var intrinsicHeight = surfaceView.contentView?.systemLayoutSizeFitting(fittingSize).height ?? 0.0
var safeAreaBottom: CGFloat = 0.0
if #available(iOS 11.0, *) {
@@ -70,7 +70,7 @@ public class FloatingPanelSurfaceView: UIView {
render()
}
required init?(coder aDecoder: NSCoder) {
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
render()
}
@@ -49,6 +49,10 @@ class FloatingPanelPresentationController: UIPresentationController {
let fpView = fpc.view
else { fatalError() }
/*
* Layout the views managed by `FloatingPanelController` here for the
* sake of the presentation and disimissal modally from the controller.
*/
containerView.addSubview(fpView)
fpView.frame = containerView.bounds
fpView.translatesAutoresizingMaskIntoConstraints = false
@@ -59,6 +63,10 @@ class FloatingPanelPresentationController: UIPresentationController {
fpView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0.0),
])
// Forward touch events to the presenting view controller
(fpView as? FloatingPanelPassThroughView)?.eventForwardingView = presentingViewController.view
// Set tap-to-dimiss in the backdrop view
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleBackdrop(tapGesture:)))
fpc.backdropView.addGestureRecognizer(tapGesture)
}
@@ -83,6 +91,9 @@ class FloatingPanelModalPresentTransition: NSObject, UIViewControllerAnimatedTra
let fpc = transitionContext.viewController(forKey: .to) as? FloatingPanelController
else { fatalError() }
// Must set the container's bounds to the floating panel view
fpc.view?.frame = transitionContext.containerView.bounds
fpc.show(animated: true) {
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
+6 -5
View File
@@ -6,13 +6,14 @@
import UIKit
class FloatingPanelPassThroughView: UIView {
public weak var eventForwardingView: UIView?
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
switch view {
case is FloatingPanelPassThroughView:
return nil
let hitView = super.hitTest(point, with: event)
switch hitView {
case self:
return eventForwardingView?.hitTest(self.convert(point, to: eventForwardingView), with: event)
default:
return view
return hitView
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ public class GrabberHandleView: UIView {
public static let barColor = UIColor(displayP3Red: 0.76, green: 0.77, blue: 0.76, alpha: 1.0)
}
required init?(coder aDecoder: NSCoder) {
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
render()
}
+2 -1
View File
@@ -73,7 +73,8 @@ extension UIView {
}
}
extension UIGestureRecognizer.State: CustomDebugStringConvertible {
extension UIGestureRecognizerState: CustomDebugStringConvertible {
public var debugDescription: String {
switch self {
case .began: return "Began"
+1
View File
@@ -2,6 +2,7 @@
[![Version](https://img.shields.io/cocoapods/v/FloatingPanel.svg)](https://cocoapods.org/pods/FloatingPanel)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Platform](https://img.shields.io/cocoapods/p/FloatingPanel.svg)](https://cocoapods.org/pods/FloatingPanel)
[![Swift 4.1](https://img.shields.io/badge/Swift-4.1-orange.svg?style=flat)](https://swift.org/)
[![Swift 4.2](https://img.shields.io/badge/Swift-4.2-orange.svg?style=flat)](https://swift.org/)
# FloatingPanel