Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d6c0220e0 | |||
| ecbd318186 | |||
| 34809bd8ea | |||
| aa5fbe9e94 | |||
| 9fbb7df15a | |||
| d55f9a0abf | |||
| a932f3b782 | |||
| 9ea95d69a1 | |||
| e783b92905 | |||
| d380fdeb13 | |||
| 987bf79121 | |||
| be0ebd0fae | |||
| a6538b7a2a | |||
| 40194d91c0 | |||
| c8b2b33de0 | |||
| 7114f545ff |
@@ -0,0 +1,81 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macOS-10.15
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "Swift 5.1"
|
||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.1 clean build
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.3.1.app/Contents/Developer
|
||||
- name: "Swift 5.2"
|
||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.2 clean build
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.7.app/Contents/Developer
|
||||
- name: "Swift 5.3"
|
||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.3 clean build
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_12.3.app/Contents/Developer
|
||||
|
||||
testing:
|
||||
runs-on: macOS-10.15
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "Testing in iOS 13.7"
|
||||
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=13.7,name=iPhone 11 Pro'
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.7.app/Contents/Developer
|
||||
- name: "Testing in iOS 14.3"
|
||||
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=14.3,name=iPhone 12 Pro'
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_12.3.app/Contents/Developer
|
||||
|
||||
example:
|
||||
runs-on: macOS-10.15
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_12.3.app/Contents/Developer
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "Build Maps"
|
||||
run: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Maps -sdk iphonesimulator clean build
|
||||
- name: "Build Stocks"
|
||||
run: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Stocks -sdk iphonesimulator clean build
|
||||
- name: "Build Samples"
|
||||
run: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Samples -sdk iphonesimulator clean build
|
||||
|
||||
swiftpm:
|
||||
runs-on: macOS-10.15
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_12.3.app/Contents/Developer
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "Swift Package build"
|
||||
run: swift build -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" -Xswiftc "-target" -Xswiftc "x86_64-apple-ios14.3-simulator"
|
||||
|
||||
carthage:
|
||||
runs-on: macOS-10.15
|
||||
env:
|
||||
# Carthage doesn't fix issues on Xcode 12: https://github.com/Carthage/Carthage/releases/tag/0.36.0
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.7.app/Contents/Developer
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "Carthage build"
|
||||
run: carthage build --no-skip-current
|
||||
|
||||
cocoapods:
|
||||
runs-on: macOS-10.15
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "CocoaPods: pod lib lint"
|
||||
run: pod lib lint --allow-warnings
|
||||
- name: "CocoaPods: pod spec lint"
|
||||
run: pod spec lint --allow-warnings
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
--header "// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license."
|
||||
--disable andOperator,anyObjectProtocol,blankLinesAroundMark,blankLinesAtEndOfScope,blankLinesAtStartOfScope,blankLinesBetweenScopes,braces,consecutiveBlankLines,consecutiveSpaces,duplicateImports,elseOnSameLine,emptyBraces,hoistPatternLet,indent,isEmpty,leadingDelimiters,linebreakAtEndOfFile,linebreaks,numberFormatting,ranges,redundantBackticks,redundantBreak,redundantExtensionACL,redundantFileprivate,redundantGet,redundantInit,redundantLet,redundantLetError,redundantNilInit,redundantObjc,redundantParens,redundantPattern,redundantRawValues,redundantReturn,redundantSelf,redundantVoidReturnType,semicolons,sortedImports,spaceAroundBraces,spaceAroundBrackets,spaceAroundComments,spaceAroundGenerics,spaceAroundOperators,spaceAroundParens,spaceInsideBraces,spaceInsideBrackets,spaceInsideComments,spaceInsideGenerics,spaceInsideParens,specifiers,strongOutlets,strongifiedSelf,todos,trailingClosures,trailingCommas,trailingSpace,typeSugar,unusedArguments,void,wrapArguments,yodaConditions
|
||||
--disable andOperator,anyObjectProtocol,blankLinesAroundMark,blankLinesAtEndOfScope,blankLinesAtStartOfScope,blankLinesBetweenScopes,braces,consecutiveBlankLines,consecutiveSpaces,duplicateImports,elseOnSameLine,emptyBraces,hoistPatternLet,indent,isEmpty,leadingDelimiters,linebreakAtEndOfFile,linebreaks,numberFormatting,redundantBackticks,redundantBreak,redundantExtensionACL,redundantFileprivate,redundantGet,redundantInit,redundantLet,redundantLetError,redundantNilInit,redundantObjc,redundantParens,redundantPattern,redundantRawValues,redundantReturn,redundantSelf,redundantVoidReturnType,semicolons,sortedImports,spaceAroundBraces,spaceAroundBrackets,spaceAroundComments,spaceAroundGenerics,spaceAroundOperators,spaceAroundParens,spaceInsideBraces,spaceInsideBrackets,spaceInsideComments,spaceInsideGenerics,spaceInsideParens,specifiers,strongOutlets,strongifiedSelf,todos,trailingClosures,trailingCommas,trailingSpace,typeSugar,unusedArguments,void,wrapArguments,yodaConditions
|
||||
|
||||
|
||||
-54
@@ -8,18 +8,6 @@ env:
|
||||
- LC_ALL=en_US.UTF-8
|
||||
jobs:
|
||||
include:
|
||||
- stage: "Builds"
|
||||
script: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.1 SUPPORTS_MACCATALYST=NO clean build
|
||||
# SUPPORTS_MACCATALYST=NO because Xcode 11 runs on macOS 10.14 in Travis CI
|
||||
osx_image: xcode11.3
|
||||
name: "Swift 5.1"
|
||||
- script: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.2 clean build
|
||||
osx_image: xcode11.6
|
||||
name: "Swift 5.2"
|
||||
- script: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.3 clean build
|
||||
osx_image: xcode12
|
||||
name: "Swift 5.3"
|
||||
|
||||
- stage: "Tests"
|
||||
script: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=10.3.1,name=iPhone SE (1st generation)'
|
||||
osx_image: xcode11.6
|
||||
@@ -30,45 +18,3 @@ jobs:
|
||||
- script: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=12.4,name=iPhone X'
|
||||
osx_image: xcode11.6
|
||||
name: "iPhone X (iOS 12.4)"
|
||||
- script: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=13.6,name=iPhone 11'
|
||||
osx_image: xcode11.6
|
||||
name: "iPhone X (iOS 13.6)"
|
||||
|
||||
- stage: "Build examples"
|
||||
osx_image: xcode11.6
|
||||
script: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Maps -sdk iphonesimulator clean build
|
||||
name: "Maps"
|
||||
- script: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Stocks -sdk iphonesimulator clean build
|
||||
osx_image: xcode11.6
|
||||
name: "Stocks"
|
||||
- script: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Samples -sdk iphonesimulator clean build
|
||||
osx_image: xcode11.6
|
||||
name: "Samples"
|
||||
- script: xcodebuild -workspace FloatingPanel.xcworkspace -scheme SamplesObjC -sdk iphonesimulator clean build
|
||||
osx_image: xcode11.6
|
||||
name: "SamplesObjC"
|
||||
|
||||
- stage: "Swift Package Manager"
|
||||
script: swift build -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphoneos --show-sdk-path`" -Xswiftc "-target" -Xswiftc "arm64-apple-ios13.0"
|
||||
osx_image: xcode11.6
|
||||
name: "iPhone OS"
|
||||
- script: swift build -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" -Xswiftc "-target" -Xswiftc "x86_64-apple-ios13.0-simulator"
|
||||
osx_image: xcode11.6
|
||||
name: "iPhone Simulator"
|
||||
|
||||
- stage: "Carthage"
|
||||
# Carthage doesn't fix the issue with Xcode 12: https://github.com/Carthage/Carthage/releases/tag/0.36.0
|
||||
osx_image: xcode11.6
|
||||
before_install:
|
||||
- brew update
|
||||
- brew outdated carthage || brew upgrade carthage
|
||||
script:
|
||||
- carthage build --no-skip-current
|
||||
|
||||
- stage: "CocoaPods"
|
||||
osx_image: xcode11.6
|
||||
before_install:
|
||||
- gem install cocoapods
|
||||
script:
|
||||
- pod spec lint --allow-warnings
|
||||
- pod lib lint --allow-warnings
|
||||
|
||||
@@ -83,14 +83,17 @@ class ViewController: UIViewController {
|
||||
func layoutPanelForPhone() {
|
||||
fpc.track(scrollView: searchVC.tableView) // Only track the tabvle view on iPhone
|
||||
fpc.addPanel(toParent: self, animated: true)
|
||||
fpc.setApearanceForPhone()
|
||||
detailFpc.setApearanceForPhone()
|
||||
fpc.setAppearanceForPhone()
|
||||
detailFpc.setAppearanceForPhone()
|
||||
}
|
||||
}
|
||||
|
||||
extension FloatingPanelController {
|
||||
func setApearanceForPhone() {
|
||||
func setAppearanceForPhone() {
|
||||
let appearance = SurfaceAppearance()
|
||||
if #available(iOS 13.0, *) {
|
||||
appearance.cornerCurve = .continuous
|
||||
}
|
||||
appearance.cornerRadius = 8.0
|
||||
appearance.backgroundColor = .clear
|
||||
surfaceView.appearance = appearance
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "FloatingPanel"
|
||||
s.version = "2.1.0"
|
||||
s.version = "2.2.0"
|
||||
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.
|
||||
|
||||
@@ -35,6 +35,7 @@ The new interface displays the related contents and utilities in parallel as a u
|
||||
- [Support your landscape layout](#support-your-landscape-layout)
|
||||
- [Use the intrinsic size of a content in your panel layout](#use-the-intrinsic-size-of-a-content-in-your-panel-layout)
|
||||
- [Specify an anchor for each state by an inset of the `FloatingPanelController.view` frame](#specify-an-anchor-for-each-state-by-an-inset-of-the-floatingpanelcontrollerview-frame)
|
||||
- [Change the backdrop alpha](#change-the-backdrop-alpha)
|
||||
- [Customize the behavior with `FloatingPanelBehavior` protocol](#customize-the-behavior-with-floatingpanelbehavior-protocol)
|
||||
- [Modify your floating panel's interaction](#modify-your-floating-panels-interaction)
|
||||
- [Activate the rubber-band effect on panel edges](#activate-the-rubber-band-effect-on-panel-edges)
|
||||
@@ -370,6 +371,23 @@ class MyFullScreenLayout: FloatingPanelLayout {
|
||||
|
||||
:pencil2: `FloatingPanelFullScreenLayout` is deprecated on v1.
|
||||
|
||||
#### Change the backdrop alpha
|
||||
|
||||
You can change the backdrop alpha by `FloatingPanelLayout.backdropAlpha(for:)` for each state(`.full`, `.half` and `.tip`).
|
||||
|
||||
For instance, if a panel seems like the backdrop view isn't there on `.half` state, it's time to implement the backdropAlpha API and return a value for the state as below.
|
||||
|
||||
```swift
|
||||
class MyPanelLayout: FloatingPanelLayout {
|
||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||
switch state {
|
||||
case .full, .half: return 0.3
|
||||
default: return 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Customize the behavior with `FloatingPanelBehavior` protocol
|
||||
|
||||
#### Modify your floating panel's interaction
|
||||
|
||||
@@ -43,6 +43,12 @@ public protocol FloatingPanelBehavior {
|
||||
/// This method allows a panel to activate the rubber band effect to a given edge of the surface view. By default, the effect is disabled.
|
||||
@objc optional
|
||||
func allowsRubberBanding(for edge: UIRectEdge) -> Bool
|
||||
|
||||
/// Returns the velocity threshold for the default interactive removal gesture.
|
||||
///
|
||||
/// In case `floatingPanel:shouldRemoveAt:with` is implemented, this value will not be used. The default value of `FloatingPanelDefaultBehavior` is 5.5
|
||||
@objc optional
|
||||
var removalInteractionVelocityThreshold: CGFloat { get }
|
||||
}
|
||||
|
||||
/// The default behavior object for a panel
|
||||
@@ -70,6 +76,8 @@ open class FloatingPanelDefaultBehavior: FloatingPanelBehavior {
|
||||
open func allowsRubberBanding(for edge: UIRectEdge) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
open var removalInteractionVelocityThreshold: CGFloat = 5.5
|
||||
}
|
||||
|
||||
class BehaviorAdapter {
|
||||
@@ -92,6 +100,10 @@ class BehaviorAdapter {
|
||||
var momentumProjectionRate: CGFloat {
|
||||
behavior.momentumProjectionRate ?? FloatingPanelDefaultBehavior().momentumProjectionRate
|
||||
}
|
||||
|
||||
var removalInteractionVelocityThreshold: CGFloat {
|
||||
behavior.removalInteractionVelocityThreshold ?? FloatingPanelDefaultBehavior().removalInteractionVelocityThreshold
|
||||
}
|
||||
|
||||
func redirectionalProgress(from: FloatingPanelState, to: FloatingPanelState) -> CGFloat {
|
||||
behavior.redirectionalProgress?(vc, from: from, to: to) ?? FloatingPanelDefaultBehavior().redirectionalProgress(vc,from: from, to: to)
|
||||
|
||||
@@ -498,7 +498,7 @@ open class FloatingPanelController: UIViewController {
|
||||
])
|
||||
|
||||
show(animated: animated) { [weak self] in
|
||||
guard let `self` = self else { return }
|
||||
guard let self = self else { return }
|
||||
self.didMove(toParent: parent)
|
||||
}
|
||||
}
|
||||
@@ -517,7 +517,7 @@ open class FloatingPanelController: UIViewController {
|
||||
delegate?.floatingPanelWillRemove?(self)
|
||||
|
||||
hide(animated: animated) { [weak self] in
|
||||
guard let `self` = self else { return }
|
||||
guard let self = self else { return }
|
||||
|
||||
self.willMove(toParent: nil)
|
||||
|
||||
@@ -537,7 +537,6 @@ open class FloatingPanelController: UIViewController {
|
||||
/// - completion: The block to execute after the view controller has finished moving. This block has no return value and takes no parameters. You may specify nil for this parameter.
|
||||
@objc(moveToState:animated:completion:)
|
||||
public func move(to: FloatingPanelState, animated: Bool, completion: (() -> Void)? = nil) {
|
||||
assert(floatingPanel.layoutAdapter.vc != nil, "Use show(animated:completion)")
|
||||
floatingPanel.move(to: to, animated: animated, completion: completion)
|
||||
}
|
||||
|
||||
@@ -673,7 +672,7 @@ extension FloatingPanelController {
|
||||
}
|
||||
|
||||
extension FloatingPanelController {
|
||||
private static let dismissSwizzling: Any? = {
|
||||
private static let dismissSwizzling: Void = {
|
||||
let aClass: AnyClass! = UIViewController.self //object_getClass(vc)
|
||||
if let imp = class_getMethodImplementation(aClass, #selector(dismiss(animated:completion:))),
|
||||
let originalAltMethod = class_getInstanceMethod(aClass, #selector(fp_original_dismiss(animated:completion:))) {
|
||||
@@ -682,10 +681,8 @@ extension FloatingPanelController {
|
||||
let originalMethod = class_getInstanceMethod(aClass, #selector(dismiss(animated:completion:)))
|
||||
let swizzledMethod = class_getInstanceMethod(aClass, #selector(fp_dismiss(animated:completion:)))
|
||||
if let originalMethod = originalMethod, let swizzledMethod = swizzledMethod {
|
||||
// switch implementation..
|
||||
method_exchangeImplementations(originalMethod, swizzledMethod)
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
+32
-12
@@ -91,6 +91,11 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
backdropView.addGestureRecognizer(tapGesture)
|
||||
}
|
||||
|
||||
deinit {
|
||||
// Release `NumericSpringAnimator.displayLink` from the run loop.
|
||||
self.moveAnimator?.stopAnimation(false)
|
||||
}
|
||||
|
||||
func move(to: FloatingPanelState, animated: Bool, completion: (() -> Void)? = nil) {
|
||||
move(from: state, to: to, animated: animated, completion: completion)
|
||||
}
|
||||
@@ -121,11 +126,13 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
switch (from, to) {
|
||||
case (.hidden, let to):
|
||||
animator = vc.animatorForPresenting(to: to)
|
||||
case (let from, .hidden):
|
||||
case (_, .hidden):
|
||||
let animationVector = CGVector(dx: abs(removalVector.dx), dy: abs(removalVector.dy))
|
||||
animator = vc.animatorForDismissing(with: animationVector)
|
||||
default:
|
||||
move(to: to, with: 0) {
|
||||
move(to: to, with: 0) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.moveAnimator = nil
|
||||
updateScrollView()
|
||||
completion?()
|
||||
@@ -138,7 +145,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
&& layoutAdapter.isIntrinsicAnchor(state: to)
|
||||
|
||||
animator.addAnimations { [weak self] in
|
||||
guard let `self` = self else { return }
|
||||
guard let self = self else { return }
|
||||
|
||||
self.state = to
|
||||
self.updateLayout(to: to)
|
||||
@@ -149,7 +156,8 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
}
|
||||
}
|
||||
animator.addCompletion { [weak self] _ in
|
||||
guard let `self` = self else { return }
|
||||
guard let self = self else { return }
|
||||
|
||||
self.animator = nil
|
||||
updateScrollView()
|
||||
self.ownerVC?.notifyDidMove()
|
||||
@@ -530,15 +538,20 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
return false
|
||||
}
|
||||
|
||||
// When the current and initial point within grabber area, do scroll.
|
||||
// When the current point is within grabber area but the initial point is not, do scroll.
|
||||
if grabberAreaFrame.contains(point), !grabberAreaFrame.contains(initialLocation) {
|
||||
return true
|
||||
}
|
||||
|
||||
// When the initial point is within grabber area and the current point is out of surface, don't scroll.
|
||||
if grabberAreaFrame.contains(initialLocation), !surfaceView.frame.contains(point) {
|
||||
return false
|
||||
}
|
||||
|
||||
let scrollViewFrame = scrollView.convert(scrollView.bounds, to: surfaceView)
|
||||
guard
|
||||
scrollViewFrame.contains(initialLocation), // When initialLocation not in scrollView, don't scroll.
|
||||
!grabberAreaFrame.contains(point) // When point within grabber area, don't scroll.
|
||||
scrollViewFrame.contains(initialLocation), // When the initial point not in scrollView, don't scroll.
|
||||
!grabberAreaFrame.contains(point) // When point within grabber area, don't scroll.
|
||||
else {
|
||||
return false
|
||||
}
|
||||
@@ -566,6 +579,9 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
if scrollView.isDecelerating {
|
||||
return true
|
||||
}
|
||||
if let tableView = (scrollView as? UITableView), tableView.isEditing {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -647,7 +663,8 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
stopScrollDeceleration = (0 > layoutAdapter.offsetFromEdgeMost + (1.0 / surfaceView.fp_displayScale)) // Projecting the dragging to the scroll dragging or not
|
||||
if stopScrollDeceleration {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let `self` = self else { return }
|
||||
guard let self = self else { return }
|
||||
|
||||
self.stopScrolling(at: self.initialScrollOffset)
|
||||
}
|
||||
}
|
||||
@@ -713,7 +730,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
if let result = vc.delegate?.floatingPanel?(vc, shouldRemoveAt: vc.surfaceLocation, with: velocityVector) {
|
||||
return result
|
||||
}
|
||||
let threshold = CGFloat(5.5)
|
||||
let threshold = behaviorAdapter.removalInteractionVelocityThreshold
|
||||
switch layoutAdapter.position {
|
||||
case .top:
|
||||
return (velocityVector.dy <= -threshold)
|
||||
@@ -820,15 +837,18 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
decelerationRate: behaviorAdapter.springDecelerationRate,
|
||||
responseTime: behaviorAdapter.springResponseTime,
|
||||
update: { [weak self] data in
|
||||
guard let self = self else { return }
|
||||
guard let self = self,
|
||||
let ownerVC = self.ownerVC // Ensure the owner vc is existing for `layoutAdapter.surfaceLocation`
|
||||
else { return }
|
||||
animationConstraint.constant = data.value
|
||||
let current = self.value(of: self.layoutAdapter.surfaceLocation)
|
||||
let translation = data.value - initialData.value
|
||||
self.backdropView.alpha = self.getBackdropAlpha(at: current, with: translation)
|
||||
self.ownerVC?.notifyDidMove()
|
||||
ownerVC.notifyDidMove()
|
||||
},
|
||||
completion: { [weak self] in
|
||||
guard let self = self else { return }
|
||||
guard let self = self,
|
||||
self.ownerVC != nil else { return }
|
||||
self.layoutAdapter.activateLayout(for: targetPosition, forceLayout: true)
|
||||
completion()
|
||||
})
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.1.0</string>
|
||||
<string>2.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
</dict>
|
||||
|
||||
@@ -60,7 +60,7 @@ struct LayoutSegment {
|
||||
}
|
||||
|
||||
class LayoutAdapter {
|
||||
weak var vc: FloatingPanelController!
|
||||
private unowned var vc: FloatingPanelController
|
||||
private let defaultLayout = FloatingPanelBottomLayout()
|
||||
|
||||
fileprivate var layout: FloatingPanelLayout {
|
||||
@@ -76,7 +76,7 @@ class LayoutAdapter {
|
||||
return vc.backdropView
|
||||
}
|
||||
private var safeAreaInsets: UIEdgeInsets {
|
||||
return vc?.fp_safeAreaInsets ?? .zero
|
||||
return vc.fp_safeAreaInsets
|
||||
}
|
||||
|
||||
private var initialConst: CGFloat = 0.0
|
||||
@@ -503,7 +503,7 @@ class LayoutAdapter {
|
||||
constraint = surfaceView.leftAnchor.constraint(equalTo: vc.view.leftAnchor, constant: initialConst)
|
||||
}
|
||||
|
||||
constraint.priority = .defaultHigh
|
||||
constraint.priority = .required
|
||||
constraint.identifier = "FloatingPanel-interaction"
|
||||
|
||||
NSLayoutConstraint.activate([constraint])
|
||||
@@ -646,7 +646,6 @@ class LayoutAdapter {
|
||||
// The method is separated from prepareLayout(to:) for the rotation support
|
||||
// It must be called in FloatingPanelController.traitCollectionDidChange(_:)
|
||||
func updateStaticConstraint() {
|
||||
guard let vc = vc else { return }
|
||||
NSLayoutConstraint.deactivate(constraint: staticConstraint)
|
||||
staticConstraint = nil
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
// Must be a variable to use `hook` property in testing
|
||||
var log = {
|
||||
return Logger()
|
||||
}()
|
||||
@@ -68,7 +69,7 @@ struct Logger {
|
||||
|
||||
hook?(log, level)
|
||||
|
||||
os_log("%@", log: osLog, type: level.osLogType, log)
|
||||
os_log("%{public}@", log: osLog, type: level.osLogType, log)
|
||||
}
|
||||
|
||||
private func getPrettyFunction(_ function: String, _ file: String) -> String {
|
||||
|
||||
@@ -50,6 +50,12 @@ public class SurfaceAppearance: NSObject {
|
||||
/// On iOS 10, they are not automatically masked because of a UIVisualEffectView issue. See https://forums.developer.apple.com/thread/50854
|
||||
public var cornerRadius: CGFloat = 0.0
|
||||
|
||||
/// Defines the curve used for rendering the rounded corners of the layer.
|
||||
///
|
||||
/// Defaults to `.circular`.
|
||||
@available(iOS 13.0, *)
|
||||
public lazy var cornerCurve: CALayerCornerCurve = .circular
|
||||
|
||||
/// An array of shadows used to create drop shadows underneath a surface view.
|
||||
public var shadows: [Shadow] = [Shadow()]
|
||||
|
||||
@@ -311,8 +317,8 @@ public class SurfaceView: UIView {
|
||||
|
||||
containerView.backgroundColor = appearance.backgroundColor
|
||||
|
||||
updateShadow()
|
||||
updateCornerRadius()
|
||||
updateShadow()
|
||||
updateBorder()
|
||||
|
||||
grabberHandle.layer.cornerRadius = grabberHandleSize.height / 2
|
||||
@@ -336,10 +342,9 @@ public class SurfaceView: UIView {
|
||||
shadowLayer.frame = layer.bounds
|
||||
|
||||
let spread = shadow.spread
|
||||
let shadowPath = UIBezierPath(roundedRect: containerView.frame.insetBy(dx: -spread,
|
||||
dy: -spread),
|
||||
byRoundingCorners: [.allCorners],
|
||||
cornerRadii: CGSize(width: appearance.cornerRadius, height: 0))
|
||||
let shadowRect = containerView.frame.insetBy(dx: -spread, dy: -spread)
|
||||
let shadowPath = UIBezierPath.path(roundedRect: shadowRect,
|
||||
appearance: appearance)
|
||||
shadowLayer.shadowPath = shadowPath.cgPath
|
||||
shadowLayer.shadowColor = shadow.color.cgColor
|
||||
shadowLayer.shadowOffset = shadow.offset
|
||||
@@ -348,16 +353,16 @@ public class SurfaceView: UIView {
|
||||
shadowLayer.shadowOpacity = shadow.opacity
|
||||
|
||||
let mask = CAShapeLayer()
|
||||
let path = UIBezierPath(roundedRect: containerView.frame,
|
||||
byRoundingCorners: [.allCorners],
|
||||
cornerRadii: CGSize(width: appearance.cornerRadius, height: 0))
|
||||
let path = UIBezierPath.path(roundedRect: containerView.frame,
|
||||
appearance: appearance)
|
||||
let size = window?.bounds.size ?? CGSize(width: 1000.0, height: 1000.0)
|
||||
path.append(UIBezierPath(rect: layer.bounds.insetBy(dx: -size.width,
|
||||
dy: -size.height)))
|
||||
mask.fillRule = .evenOdd
|
||||
mask.path = path.cgPath
|
||||
if #available(iOS 13.0, *) {
|
||||
mask.cornerCurve = containerView.layer.cornerCurve
|
||||
containerView.layer.cornerCurve = appearance.cornerCurve
|
||||
mask.cornerCurve = appearance.cornerCurve
|
||||
}
|
||||
shadowLayer.mask = mask
|
||||
}
|
||||
|
||||
@@ -201,3 +201,70 @@ extension UIEdgeInsets {
|
||||
return self.top + self.bottom
|
||||
}
|
||||
}
|
||||
|
||||
extension UIBezierPath {
|
||||
static func path(roundedRect rect: CGRect, appearance: SurfaceAppearance) -> UIBezierPath {
|
||||
let cornerRadius = appearance.cornerRadius;
|
||||
if #available(iOS 13.0, *) {
|
||||
if appearance.cornerCurve == .circular {
|
||||
let path = UIBezierPath()
|
||||
let start = CGPoint(x: rect.minX + cornerRadius, y: rect.minY)
|
||||
|
||||
path.move(to: start)
|
||||
|
||||
path.addLine(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.minY))
|
||||
if cornerRadius > 0 {
|
||||
path .addArc(withCenter: CGPoint(x: rect.maxX - cornerRadius,
|
||||
y: rect.minY + cornerRadius),
|
||||
radius: cornerRadius,
|
||||
startAngle: -0.5 * .pi,
|
||||
endAngle: 0,
|
||||
clockwise: true)
|
||||
}
|
||||
|
||||
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - cornerRadius))
|
||||
|
||||
if cornerRadius > 0 {
|
||||
path.addArc(withCenter: CGPoint(x: rect.maxX - cornerRadius,
|
||||
y: rect.maxY - cornerRadius),
|
||||
radius: cornerRadius,
|
||||
startAngle: 0,
|
||||
endAngle: .pi * 0.5,
|
||||
clockwise: true)
|
||||
}
|
||||
|
||||
path.addLine(to: CGPoint(x: rect.minX + cornerRadius, y: rect.maxY))
|
||||
|
||||
if cornerRadius > 0 {
|
||||
path.addArc(withCenter: CGPoint(x: rect.minX + cornerRadius,
|
||||
y: rect.maxY - cornerRadius),
|
||||
radius: cornerRadius,
|
||||
startAngle: .pi * 0.5,
|
||||
endAngle: .pi,
|
||||
clockwise: true)
|
||||
}
|
||||
|
||||
path.addLine(to: CGPoint(x: rect.minX, y: rect.minY + cornerRadius))
|
||||
|
||||
if cornerRadius > 0 {
|
||||
path.addArc(withCenter: CGPoint(x: rect.minX + cornerRadius,
|
||||
y: rect.minY + cornerRadius),
|
||||
radius: cornerRadius,
|
||||
startAngle: .pi,
|
||||
endAngle: .pi * 1.5,
|
||||
clockwise: true)
|
||||
}
|
||||
|
||||
path.addLine(to: start)
|
||||
|
||||
path.close()
|
||||
|
||||
return path
|
||||
}
|
||||
}
|
||||
return UIBezierPath(roundedRect: rect,
|
||||
byRoundingCorners: [.allCorners],
|
||||
cornerRadii: CGSize(width: cornerRadius,
|
||||
height: cornerRadius))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user