Compare commits

..

16 Commits

Author SHA1 Message Date
Shin Yamamoto 3d6c0220e0 Version 2.2.0 2021-01-22 07:45:12 +09:00
Shin Yamamoto ecbd318186 Add a note for log variable 2021-01-22 07:45:12 +09:00
Shin Yamamoto 34809bd8ea Show debug logs in console.app 2021-01-22 07:45:12 +09:00
Shin Yamamoto aa5fbe9e94 Move the main ci to github actions (#437)
Because of the migration from travis-ci.org to travis-ci.com. As a
bonus, ci builds on github actions are much faster.

This repo still needs travis-ci for testing on iOS 10/11/12 simulators
on Xcode11.x. So this doesn't remove .travis.yml
2021-01-22 07:44:30 +09:00
nickcheng 9fbb7df15a Fixed the crash when ownerVC is nil. (#436)
`ownerVC` should be checked in the completion as well.
2021-01-20 23:10:43 +09:00
Shin Yamamoto d55f9a0abf Revise the swizzling prop to make it nonnull 2021-01-15 21:54:21 +09:00
Shin Yamamoto a932f3b782 Stop moving a panel while the tracking table view is editing (#431)
To fix https://github.com/SCENEE/FloatingPanel/issues/427
2021-01-11 22:46:34 +09:00
Shin Yamamoto 9ea95d69a1 Fix an issue where not dragging a panel by priority of Layout.interactionConstraint (#428)
I applied `.defaultHigh` priority which might be safe to prevent
an ambiguous layout error. But that causes this issue, so I have to
use `.required` priority.

See more detail [#424
issue](https://github.com/SCENEE/FloatingPanel/issues/424).
2021-01-11 20:13:04 +09:00
Shin Yamamoto e783b92905 Fix a crash by the move animator (#423)
* Prevent a possible memory leak at Core.move(from:to:animated:completion:)
* Replace ``self`` with `self`.
* Change `LayoutAdapter.vc` property to an unowned reference
* Prevent a crash at LayoutAdapter.surfaceLocation called in `NumericSpringAnimator.update` closure.
2021-01-09 12:20:53 +09:00
Shin Yamamoto d380fdeb13 Address the grabber area detection in scroll tracking (#407)
Allows to expand the panel while touching in the grabber area initially.
2021-01-09 11:59:02 +09:00
Fumito Nakazawa 987bf79121 Fix swiftformat (#426) 2021-01-08 22:20:38 +09:00
Federico Zanetello be0ebd0fae Add cornerCurve option to SurfaceAppearance (#417)
* Add cornerCurve option to SurfaceAppearance
* Add cornerCurve usage in Maps example

Co-authored-by: Shin Yamamoto <shin@scenee.com>
2021-01-05 18:20:58 +09:00
Benjamin Otto a6538b7a2a Add optional removalInteractionVelocityThreshold value to Behavior (#425)
Added an optional variable `removalInteractionVelocityThreshold` to `Behavior`, which let’s the user configure the velocity threshold for the default `floatingPanel:shouldRemoveAt:with` implementation.
2021-01-04 23:09:33 +09:00
Shin Yamamoto 40194d91c0 Add a description for the backdropAlpha(for:) API (#416) 2020-12-24 23:53:42 +09:00
Federico Zanetello c8b2b33de0 fix example typo (#418) 2020-12-22 18:22:19 +09:00
Shin Yamamoto 7114f545ff Merge pull request #414 from SCENEE/release-2.1.0
Prepare version 2.1.0
2020-12-12 12:43:52 +09:00
14 changed files with 241 additions and 92 deletions
+81
View File
@@ -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
View File
@@ -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
View File
@@ -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
+6 -3
View File
@@ -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 -1
View File
@@ -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.
+18
View File
@@ -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
+12
View File
@@ -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)
+3 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+3 -4
View File
@@ -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
+2 -1
View File
@@ -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 {
+14 -9
View File
@@ -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
}
+67
View File
@@ -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))
}
}