Compare commits

..

59 Commits

Author SHA1 Message Date
Shin Yamamoto a0ba3af012 Version 2.6.2 2023-06-03 10:23:03 +09:00
Shin Yamamoto 6082dba6a7 ci: add circleci config for testing on ios13/14 2023-06-03 10:12:13 +09:00
Shin Yamamoto 43327e8123 ci: add timeout-minutes 2023-06-03 10:12:13 +09:00
Shin Yamamoto 0a5bc19d0f Remove the host app for the unit testing 2023-06-03 10:12:13 +09:00
Shin Yamamoto dd52e1eaee Revise an illegible variable name 2023-04-16 10:14:54 +09:00
Shin Yamamoto 22aa055843 Revise the short descriptions 2023-04-16 10:09:39 +09:00
Shin Yamamoto 448fc5cbb4 Stop changing UIScrollView.bounces when locking or unlocking a scroll view (#584)
UIScrollView would unexpectedly change its scroll offset after updating the bounces property when dealing with small scrollable content. This fixes issue #524, "Scrolling jumps when tableView content is small".
2023-04-08 10:30:11 +09:00
Shin Yamamoto b4fe3b408c Remove unused testing targets in the Samples project 2023-04-08 10:11:41 +09:00
Shin Yamamoto 6ecc7924ba Update badges in README 2023-04-06 21:50:49 +09:00
Shin Yamamoto 0b82902233 Update README for the API documentation on GitHub Pages 2023-03-25 11:57:24 +09:00
Shin Yamamoto 597dbd4145 Revise some sentences in README 2023-03-25 11:04:55 +09:00
Shin Yamamoto 1ee949ff1b Merge pull request #583 from scenee/release/2.6.1
Release 2.6.1
2023-03-04 10:46:11 +09:00
Shin Yamamoto 2a29cb5b3e Version 2.6.1 2023-03-04 09:43:23 +09:00
Shin Yamamoto 208ab665da ci: add Maps-SwiftUI example build 2023-03-04 09:42:40 +09:00
Shin Yamamoto 3e20314cfa Update DebugTableViewController with followScrollViewBouncing() API 2023-03-02 21:53:24 +09:00
Shin Yamamoto 5b8e9a54d9 Update trackingScrollViewDidScroll() with doc comments 2023-03-02 21:53:24 +09:00
Vlad d3c30b35d9 track scroll view bouncing (#525) 2023-02-25 11:11:50 +09:00
Shin Yamamoto 8bb3795931 Take care of an invalid bounding rectangle of PassthroughView in a screen rotation 2023-02-25 10:51:19 +09:00
Shin Yamamoto 9383cd001d ci: replace iOS 15 envs on testing
Because testing on iOS 15.4 is buggy.
2023-02-18 12:15:26 +09:00
Shin Yamamoto 6d5f770066 ci: replace macos-10.15 with macos-11
macos-10.15 will be completely removed by 2023-03-31.
https://github.com/actions/runner-images/issues/5583#issuecomment-1426004850
2023-02-18 11:54:19 +09:00
Shin Yamamoto ce2cafed5b Workaround: fix a laggy animation in Samples app
The AutoLayout rendering engine has been changed since iOS 16.
As a result, "Show Detail Panel" in Samples.apps has a laggy animation
when pulling it down to the bottom of the screen. The issue hadn't
occurred until iOS 15. "On Safe Area View" causes this issue. I could
fix it by removing the constraint of its view to the top of the safe area.

As a workaround, I've replaced its top constraint with one of a
constraint to the top of the superview.
2023-02-18 11:46:41 +09:00
Shin Yamamoto 68352218ac ci: upgrade actions/checkout to suppress Node.js 12 warnings 2023-02-18 11:10:35 +09:00
Shin Yamamoto e3736e4214 Merge pull request #582 from scenee/release/2.6.0
Release 2.6.0
2023-02-18 11:06:19 +09:00
Shin Yamamoto d6bbf92339 Version 2.6.0 2023-02-18 10:27:53 +09:00
Shin Yamamoto 0e833aee3c Fix SettingsViewController in Samples app 2023-02-17 09:51:22 +09:00
Shin Yamamoto 5949bc88ea Fix typo 2023-02-06 20:46:25 +09:00
Shin Yamamoto 30bd261a10 Update the swift version in the pod spec 2023-01-30 20:47:47 +09:00
Shin Yamamoto f92279484f Fix the buggy scroll tracking
This commit sets the initial scroll offset to the pinning offset.
The previous implementation, which set it to the current content offset,
leads various scroll tracking bugs. This reproduction is one of issues.

Using 'Scroll tracking(UITableView)' in Samples app.
1. Bounce the scroll content at the top most anchor.
2. Pull down the panel in bouncing at a minus content offset. (the
   scroll content stops at the minus offset.)
3. Pull up it

The previous implementation was implemented for #526/#527. But now the
issue hasn't been reproduced in v2.5.6.
2023-01-21 11:21:34 +09:00
Shin Yamamoto 2205d1186d Enable to restrict the content size in FloatingPanelAdaptiveLayoutAnchor (#518)
* Introduce FloatingPanelLayoutContentBoundingGuide property for FloatingPanelAdaptiveLayoutAnchor
* Revise doc comments
* Clean up code
* Update the minimum deployment target of Samples app to iOS 11
2023-01-15 10:30:25 +09:00
Shin Yamamoto cd0948a9a4 Update a doc comment 2023-01-15 08:52:29 +09:00
Shin Yamamoto 6589aff4ac ci: add xcode 14.1 env (#577) 2023-01-13 09:12:17 +09:00
Shin Yamamoto d34c16b1a5 Refactor BackdropView.dismissalTapGestureRecognizer
* Added the interface and implementation comment
* Changed the property as non-optional
2023-01-07 09:14:04 +09:00
Shin Yamamoto 05813253f9 Merge pull request #571 from scenee/release/2.5.5
Release 2.5.5
2022-10-17 20:26:36 +09:00
Shin Yamamoto 4ab7d26030 Version 2.5.5 2022-10-15 19:39:31 +09:00
Shin Yamamoto 162545d7e9 Add test_initial_surface_position() 2022-10-15 09:30:44 +09:00
Shin Yamamoto 4904ea19cb Fix the stiff animation of "show detail panel" panel.
When a panel moves to tip position, the moving animation has been stiff.
This issue has happened since Xcode 14.
2022-10-15 09:19:01 +09:00
Shin Yamamoto b941f91556 Fix the backdrop alpha when the view size or its size class changes (#573)
This fixes #572 to change the backdrop alpha when the view size or 
its size class changes.

The main change is that `true` is passed as a
`forceLayout` parameter into `viewWillTransition(to:with:)` callbacks.

Because it's necessary for the backdrop alpha's update when the view
size or its size class changes.

This also fixes a regression at `9c45c31` commit.

```diff
-        layoutAdapter.activateLayout(for: state, forceLayout: true)
+        layoutAdapter.activateLayout(for: state, forceLayout: forceLayout)
```

The behavior before the above change indicates that the method has
worked well even when `forceLayout` is set to `true` in their callbacks.

Additional improvements: 

* Format `activateLayout(forceLayout:contentInsetAdjustmentBehavior:)`
* Add `_floor` function for `test_updateBackdropAlpha()`
2022-10-15 09:06:14 +09:00
Shin Yamamoto f917316135 Remove the libswiftCoreGraphics.dylib workaround from Maps-SwiftUI.app
Because the deployment target is above iOS 14.
2022-10-10 11:15:58 +09:00
ll_shioi_atsumori 6235a19588 workaround: add libswiftCoreGraphics.tbd to some sample apps (#567)
This fixed the following error occurs and crashes when running an app built with xcode14 on iOS11.
```
dyld: Library not loaded: /usr/lib/swift/libswiftCoreGraphics.dylib
  Referenced from: /private/var/containers/Bundle/Application/0B28F8D6-D8CE-400B-98B7-052EAD3FB923/xxxxxx.app/Frameworks/FloatingPanel.framework/FloatingPanel
  Reason: image not found
```

These are the related forum threads.
- https://developer.apple.com/forums/thread/714629
- https://developer.apple.com/forums/thread/714795

Co-authored-by: atsunori.shioi <astunori.shioi@play.jp>
2022-10-10 11:01:11 +09:00
Shin Yamamoto c47fc3d1d5 Better to declare FloatingPanelLayout.anchors as constants if possible 2022-10-05 22:03:59 +09:00
Shin Yamamoto e6d285c6df Fix the initial position value of the surface view
'Show Top Positioned Panel' in Samples app was broken.
2022-10-05 22:03:50 +09:00
Shin Yamamoto a26a6beab2 Merge pull request #565 from scenee/release/2.5.4
Release 2.5.4
2022-09-11 08:44:13 +09:00
Shin Yamamoto b00a05b9ed Version 2.5.4 2022-09-08 12:42:23 +09:00
Shin Yamamoto bfbb7ad004 Remove the CI job with Xcode 13.1 and iOS 15.0
This job is unstable because of its environment.
2022-09-08 12:42:14 +09:00
Shin Yamamoto 4dd52b1790 Replace PR #551 change using fatalError() (#563)
This resolves issue #561. Instead of continuing to work a modal presentation transition even if a panel state is not .hidden, this library calls fatalError as a programmer error on the occasion.
2022-09-07 20:35:50 +09:00
Shin Yamamoto aef914e0fd Remove unused '.swiftformat' file 2022-07-30 11:04:29 +09:00
Shin Yamamoto 2ef35f58a3 Merge pull request #556 from scenee/iss-536
Fix the view index of FloatingPanelView for SwiftUI
2022-07-30 11:04:11 +09:00
Shin Yamamoto 8aaf8b7b25 Fix the view index of FloatingPanelView for SwiftUI
Resolve #536
2022-07-30 09:28:33 +09:00
Shin Yamamoto c330b59a9f Merge pull request #550 from scenee/release/2.5.3
Release 2.5.3
2022-06-14 21:14:09 +09:00
Shin Yamamoto 639e4221e8 Version 2.5.3 2022-06-13 21:06:44 +09:00
Shin Yamamoto 1ff3c28de8 Ensure the initial state is hidden in presenting it as a modality (#551) 2022-06-13 21:06:24 +09:00
Sean Hernandez 89092676e3 Fix floating panel content view constraints (#549)
Resolved Maps-SwiftUI Example Animation Stuttering (#546)
2022-06-13 20:00:12 +09:00
Anton Siliuk bacc871dd2 Fix cornerCurve property availability compilation fail (#548)
With Xcode 14 Beta 1 library fails to compile with following error.

> Stored properties cannot be marked potentially unavailable with `@available`

Because CALayerCornerCurve is not marked with any @available annotation it is possible to declare private optional storage of it.
2022-06-13 19:54:54 +09:00
Shin Yamamoto 0bc9812c23 Update GitHub Actions workflow using macOS-12 (#538)
* Updated 'ci' workflow for Xcode 13.3.1
* Modernized and simplified the workflow using 'strategy.matrix'
* 'arm64-apple-ios15.4' target is not included in swiftpm because its build gets an build error.
2022-05-31 21:42:36 +09:00
Shin Yamamoto a4d98971b3 Fix a warning in SamplesObjC 2022-04-20 21:16:45 +09:00
little-huang 1b0ec64489 fix: add missing import UIKit (#534)
Co-authored-by: 黄宝成 <huangbc@publink.cn>
2022-04-20 20:29:28 +09:00
Shin Yamamoto 345e894007 Stop scrolling if the initial location of a pan gesture is in the grabber area 2022-02-21 21:16:39 +09:00
Shin Yamamoto 1fb1708df9 Fix the grabber area detecion 2022-02-19 08:08:56 +09:00
Shin Yamamoto b9edbf766e Merge pull request #528 from scenee/release/2.5.2
Release 2.5.2
2022-02-10 17:07:24 +09:00
50 changed files with 1107 additions and 1083 deletions
+21
View File
@@ -0,0 +1,21 @@
version: 2.1
jobs:
test-ios14_5-iPhone_12_Pro:
macos:
xcode: 13.4.1
steps:
- checkout
- run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=14.5,name=iPhone 12 Pro'
test-ios13_7-iPhone_11_Pro:
macos:
xcode: 12.5.1
steps:
- checkout
- run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=13.7,name=iPhone 11 Pro'
workflows:
test:
jobs:
- test-ios14_5-iPhone_12_Pro
- test-ios13_7-iPhone_11_Pro
+80 -69
View File
@@ -10,94 +10,105 @@ on:
jobs:
build:
runs-on: macOS-11
runs-on: ${{ matrix.runsOn }}
env:
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
strategy:
fail-fast: false
matrix:
include:
- swift: "5.7"
xcode: "14.1"
runsOn: macos-12
- swift: "5.6"
xcode: "13.4.1"
runsOn: macos-12
- swift: "5.5"
xcode: "13.2.1"
runsOn: macos-11
- swift: "5.4"
xcode: "12.5.1"
runsOn: macos-11
- swift: "5.3"
xcode: "12.4"
runsOn: macos-11
- swift: "5.2"
xcode: "11.7"
runsOn: macos-11
steps:
- uses: actions/checkout@v1
- name: "Swift 5.4"
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.4 clean build
env:
DEVELOPER_DIR: /Applications/Xcode_12.5.1.app/Contents/Developer
- name: "Swift 5.5"
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.5 clean build
env:
DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer
- uses: actions/checkout@v3
- name: Building in Swift ${{ matrix.swift }}
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=${{ matrix.swift }} clean build
build_compat:
runs-on: macOS-10.15
test:
runs-on: ${{ matrix.runsOn }}
env:
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
strategy:
fail-fast: false
matrix:
include:
- os: "16.1"
xcode: "14.1"
sim: "iPhone 14 Pro"
runsOn: macos-12
- os: "15.5"
xcode: "13.4.1"
sim: "iPhone 13 Pro"
runsOn: macos-12
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.4.app/Contents/Developer
testing:
runs-on: macOS-11
steps:
- uses: actions/checkout@v1
- name: "Testing in iOS 14.5"
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=14.5,name=iPhone 12 Pro'
env:
DEVELOPER_DIR: /Applications/Xcode_12.5.1.app/Contents/Developer
- name: "Testing in iOS 15.0"
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=15.0,name=iPhone 13 Pro'
env:
DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer
testing_compat:
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.4"
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=14.4,name=iPhone 12 Pro'
env:
DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer
- uses: actions/checkout@v3
- name: Testing in iOS ${{ matrix.os }}
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=${{ matrix.os }},name=${{ matrix.sim }}'
timeout-minutes: 20
example:
runs-on: macOS-11
runs-on: macos-12
env:
DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer
DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer
strategy:
fail-fast: false
matrix:
include:
- example: "Maps"
- example: "Maps-SwiftUI"
- example: "Stocks"
- example: "Samples"
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
- uses: actions/checkout@v3
- name: Building ${{ matrix.example }}
run: xcodebuild -workspace FloatingPanel.xcworkspace -scheme ${{ matrix.example }} -sdk iphonesimulator clean build
swiftpm:
runs-on: macOS-11
runs-on: macos-12
env:
DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer
DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer
strategy:
fail-fast: false
matrix:
include:
# 15.7
- target: "x86_64-apple-ios15.7-simulator"
- target: "arm64-apple-ios15.7-simulator"
# 16.1
- target: "x86_64-apple-ios16.1-simulator"
- target: "arm64-apple-ios16.1-simulator"
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"
- uses: actions/checkout@v3
- name: "Swift Package Manager build"
run: swift build -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" -Xswiftc "-target" -Xswiftc "${{ matrix.target }}"
carthage:
runs-on: macOS-11
runs-on: macos-11
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: "Carthage build"
run: carthage build --use-xcframeworks --no-skip-current
cocoapods:
runs-on: macOS-11
runs-on: macos-12
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: "CocoaPods: pod lib lint"
run: pod lib lint --allow-warnings
- name: "CocoaPods: pod spec lint"
-3
View File
@@ -1,3 +0,0 @@
--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,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
@@ -96,8 +96,20 @@ struct FloatingPanelView<Content: View, FloatingPanelContent: View>: UIViewContr
ignoresKeyboard: true
)
hostingViewController.view.backgroundColor = nil
fpc.set(contentViewController: hostingViewController)
fpc.addPanel(toParent: parentViewController, at: 1, animated: false)
let contentViewController = UIViewController()
contentViewController.view.addSubview(hostingViewController.view)
fpc.set(contentViewController: contentViewController)
fpc.addPanel(toParent: parentViewController, animated: false)
hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false
let bottomConstraint = hostingViewController.view.bottomAnchor.constraint(equalTo: contentViewController.view.bottomAnchor)
bottomConstraint.priority = .defaultHigh
NSLayoutConstraint.activate([
hostingViewController.view.topAnchor.constraint(equalTo: contentViewController.view.topAnchor),
hostingViewController.view.leadingAnchor.constraint(equalTo: contentViewController.view.leadingAnchor),
hostingViewController.view.trailingAnchor.constraint(equalTo: contentViewController.view.trailingAnchor),
bottomConstraint
])
}
func updateIfNeeded() {
@@ -1,6 +1,7 @@
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
import FloatingPanel
import UIKit
final class SearchPanelPhoneDelegate: FloatingPanelControllerDelegate {
func floatingPanelWillBeginDragging(_ vc: FloatingPanelController) {
@@ -18,6 +18,7 @@
54B51134216C3D860033A6F3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54B51132216C3D860033A6F3 /* LaunchScreen.storyboard */; };
54E26CB624A989090066C720 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E26CB524A989090066C720 /* Utils.swift */; };
54E26CB824A98E310066C720 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E26CB724A98E310066C720 /* DetailViewController.swift */; };
5D82A6A728D18422006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6A628D1841E006A44BA /* libswiftCoreGraphics.tbd */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -47,6 +48,7 @@
54B51135216C3D860033A6F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
54E26CB524A989090066C720 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
54E26CB724A98E310066C720 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = "<group>"; };
5D82A6A628D1841E006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -55,6 +57,7 @@
buildActionMask = 2147483647;
files = (
543844BD23D2BE2000D5EDE4 /* MapKit.framework in Frameworks */,
5D82A6A728D18422006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
549D23D2233C77D5008EF4D7 /* FloatingPanel.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -65,6 +68,7 @@
543844BB23D2BE1F00D5EDE4 /* Frameworks */ = {
isa = PBXGroup;
children = (
5D82A6A628D1841E006A44BA /* libswiftCoreGraphics.tbd */,
543844BC23D2BE2000D5EDE4 /* MapKit.framework */,
);
name = Frameworks;
@@ -336,6 +340,10 @@
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/swift",
);
PRODUCT_BUNDLE_IDENTIFIER = example.Maps;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -355,6 +363,10 @@
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/swift",
);
PRODUCT_BUNDLE_IDENTIFIER = example.Maps;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
+18 -28
View File
@@ -210,12 +210,10 @@ class SearchPanelPhoneDelegate: NSObject, FloatingPanelControllerDelegate, UIGes
class SearchPanelLandscapeLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .tip
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
]
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
if #available(iOS 11.0, *) {
return [
@@ -244,11 +242,9 @@ class DetailPanelPhoneDelegate: NSObject, FloatingPanelControllerDelegate, UIGes
class DetailPanelPhoneLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
]
let initialState: FloatingPanelState = .full
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.0
@@ -297,13 +293,11 @@ class SearchPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestu
class SearchPanelPadLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .top
let initialState: FloatingPanelState = .tip
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.tip: FloatingPanelLayoutAnchor(absoluteInset: 80.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 200.0, edge: .top, referenceGuide: .superview),
.full: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.tip: FloatingPanelLayoutAnchor(absoluteInset: 80.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 200.0, edge: .top, referenceGuide: .superview),
.full: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
]
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.0
}
@@ -357,11 +351,9 @@ class DetailPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestu
class DetailPanelPadLeftLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .left
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .left, referenceGuide: .superview)
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .left, referenceGuide: .superview)
]
let initialState: FloatingPanelState = .full
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.0
@@ -370,11 +362,9 @@ class DetailPanelPadLeftLayout: FloatingPanelLayout {
class DetailPanelPadRightLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .right
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .right, referenceGuide: .superview)
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .right, referenceGuide: .superview)
]
let initialState: FloatingPanelState = .full
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.0
@@ -18,13 +18,12 @@
5442E24A25FC53C100A26F43 /* DebugTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5442E24925FC53C100A26F43 /* DebugTextViewController.swift */; };
5442E25225FC541700A26F43 /* NestedScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5442E25125FC541700A26F43 /* NestedScrollViewController.swift */; };
54496C59263A7E5A0031E0C8 /* UseCaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54496C58263A7E5A0031E0C8 /* UseCaseController.swift */; };
544BC56826CC918200D0A436 /* AdaptiveLayoutTestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 544BC56726CC918200D0A436 /* AdaptiveLayoutTestViewController.swift */; };
545DB9EE21511E6300CA77B8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9ED21511E6300CA77B8 /* AppDelegate.swift */; };
545DB9F021511E6300CA77B8 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9EF21511E6300CA77B8 /* MainViewController.swift */; };
545DB9F321511E6300CA77B8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F121511E6300CA77B8 /* Main.storyboard */; };
545DB9F521511E6400CA77B8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F421511E6400CA77B8 /* Assets.xcassets */; };
545DB9F821511E6400CA77B8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F621511E6400CA77B8 /* LaunchScreen.storyboard */; };
545DBA0321511E6400CA77B8 /* SampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DBA0221511E6400CA77B8 /* SampleTests.swift */; };
545DBA0E21511E6400CA77B8 /* SampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DBA0D21511E6400CA77B8 /* SampleUITests.swift */; };
546341A125C6415100CA0596 /* UseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546341A025C6415100CA0596 /* UseCase.swift */; };
546341AC25C6426500CA0596 /* CustomState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546341AB25C6426500CA0596 /* CustomState.swift */; };
549D23CB233C7779008EF4D7 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23CA233C7779008EF4D7 /* FloatingPanel.framework */; };
@@ -33,25 +32,9 @@
54CDC5D8215BBE23007D205C /* SupplementaryViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D7215BBE23007D205C /* SupplementaryViews.swift */; };
54EAD35B263A75EB006A36EA /* PanelLayouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EAD35A263A75EB006A36EA /* PanelLayouts.swift */; };
54EAD365263A765F006A36EA /* PagePanelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EAD364263A765F006A36EA /* PagePanelController.swift */; };
5D82A6AD28D1843C006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6AC28D18438006A44BA /* libswiftCoreGraphics.tbd */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
545DB9FF21511E6400CA77B8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 545DB9E221511E6300CA77B8 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 545DB9E921511E6300CA77B8;
remoteInfo = FloatingModalSample;
};
545DBA0A21511E6400CA77B8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 545DB9E221511E6300CA77B8 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 545DB9E921511E6300CA77B8;
remoteInfo = FloatingModalSample;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
549D23CD233C7779008EF4D7 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
@@ -78,6 +61,7 @@
5442E24925FC53C100A26F43 /* DebugTextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugTextViewController.swift; sourceTree = "<group>"; };
5442E25125FC541700A26F43 /* NestedScrollViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedScrollViewController.swift; sourceTree = "<group>"; };
54496C58263A7E5A0031E0C8 /* UseCaseController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UseCaseController.swift; sourceTree = "<group>"; };
544BC56726CC918200D0A436 /* AdaptiveLayoutTestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveLayoutTestViewController.swift; sourceTree = "<group>"; };
545DB9EA21511E6300CA77B8 /* Samples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Samples.app; sourceTree = BUILT_PRODUCTS_DIR; };
545DB9ED21511E6300CA77B8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
545DB9EF21511E6300CA77B8 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
@@ -85,12 +69,6 @@
545DB9F421511E6400CA77B8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
545DB9F721511E6400CA77B8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
545DB9F921511E6400CA77B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
545DB9FE21511E6400CA77B8 /* SamplesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SamplesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
545DBA0221511E6400CA77B8 /* SampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleTests.swift; sourceTree = "<group>"; };
545DBA0421511E6400CA77B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
545DBA0921511E6400CA77B8 /* SamplesUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SamplesUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
545DBA0D21511E6400CA77B8 /* SampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleUITests.swift; sourceTree = "<group>"; };
545DBA0F21511E6400CA77B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
546341A025C6415100CA0596 /* UseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UseCase.swift; sourceTree = "<group>"; };
546341AB25C6426500CA0596 /* CustomState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomState.swift; sourceTree = "<group>"; };
549D23CA233C7779008EF4D7 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -98,6 +76,7 @@
54CDC5D7215BBE23007D205C /* SupplementaryViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupplementaryViews.swift; sourceTree = "<group>"; };
54EAD35A263A75EB006A36EA /* PanelLayouts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PanelLayouts.swift; sourceTree = "<group>"; };
54EAD364263A765F006A36EA /* PagePanelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagePanelController.swift; sourceTree = "<group>"; };
5D82A6AC28D18438006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -105,24 +84,11 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5D82A6AD28D1843C006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
549D23CB233C7779008EF4D7 /* FloatingPanel.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
545DB9FB21511E6400CA77B8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
545DBA0621511E6400CA77B8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -139,6 +105,7 @@
5442E22725FC51E200A26F43 /* MultiPanelController.swift */,
5442E22B25FC521F00A26F43 /* SettingsViewController.swift */,
5442E22F25FC525200A26F43 /* TabBarViewController.swift */,
544BC56726CC918200D0A436 /* AdaptiveLayoutTestViewController.swift */,
);
path = ContentViewControllers;
sourceTree = "<group>";
@@ -148,9 +115,8 @@
children = (
549D23CA233C7779008EF4D7 /* FloatingPanel.framework */,
545DB9EC21511E6300CA77B8 /* Sources */,
545DBA0121511E6400CA77B8 /* Tests */,
545DBA0C21511E6400CA77B8 /* UITests */,
545DB9EB21511E6300CA77B8 /* Products */,
5D82A6AB28D18438006A44BA /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -158,8 +124,6 @@
isa = PBXGroup;
children = (
545DB9EA21511E6300CA77B8 /* Samples.app */,
545DB9FE21511E6400CA77B8 /* SamplesTests.xctest */,
545DBA0921511E6400CA77B8 /* SamplesUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -183,24 +147,6 @@
path = Sources;
sourceTree = "<group>";
};
545DBA0121511E6400CA77B8 /* Tests */ = {
isa = PBXGroup;
children = (
545DBA0221511E6400CA77B8 /* SampleTests.swift */,
545DBA0421511E6400CA77B8 /* Info.plist */,
);
path = Tests;
sourceTree = "<group>";
};
545DBA0C21511E6400CA77B8 /* UITests */ = {
isa = PBXGroup;
children = (
545DBA0D21511E6400CA77B8 /* SampleUITests.swift */,
545DBA0F21511E6400CA77B8 /* Info.plist */,
);
path = UITests;
sourceTree = "<group>";
};
546341AA25C6421000CA0596 /* UseCases */ = {
isa = PBXGroup;
children = (
@@ -211,6 +157,14 @@
path = UseCases;
sourceTree = "<group>";
};
5D82A6AB28D18438006A44BA /* Frameworks */ = {
isa = PBXGroup;
children = (
5D82A6AC28D18438006A44BA /* libswiftCoreGraphics.tbd */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -232,42 +186,6 @@
productReference = 545DB9EA21511E6300CA77B8 /* Samples.app */;
productType = "com.apple.product-type.application";
};
545DB9FD21511E6400CA77B8 /* SamplesTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 545DBA1521511E6400CA77B8 /* Build configuration list for PBXNativeTarget "SamplesTests" */;
buildPhases = (
545DB9FA21511E6400CA77B8 /* Sources */,
545DB9FB21511E6400CA77B8 /* Frameworks */,
545DB9FC21511E6400CA77B8 /* Resources */,
);
buildRules = (
);
dependencies = (
545DBA0021511E6400CA77B8 /* PBXTargetDependency */,
);
name = SamplesTests;
productName = FloatingModalSampleTests;
productReference = 545DB9FE21511E6400CA77B8 /* SamplesTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
545DBA0821511E6400CA77B8 /* SamplesUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 545DBA1821511E6400CA77B8 /* Build configuration list for PBXNativeTarget "SamplesUITests" */;
buildPhases = (
545DBA0521511E6400CA77B8 /* Sources */,
545DBA0621511E6400CA77B8 /* Frameworks */,
545DBA0721511E6400CA77B8 /* Resources */,
);
buildRules = (
);
dependencies = (
545DBA0B21511E6400CA77B8 /* PBXTargetDependency */,
);
name = SamplesUITests;
productName = FloatingModalSampleUITests;
productReference = 545DBA0921511E6400CA77B8 /* SamplesUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -281,14 +199,6 @@
545DB9E921511E6300CA77B8 = {
CreatedOnToolsVersion = 10.0;
};
545DB9FD21511E6400CA77B8 = {
CreatedOnToolsVersion = 10.0;
TestTargetID = 545DB9E921511E6300CA77B8;
};
545DBA0821511E6400CA77B8 = {
CreatedOnToolsVersion = 10.0;
TestTargetID = 545DB9E921511E6300CA77B8;
};
};
};
buildConfigurationList = 545DB9E521511E6300CA77B8 /* Build configuration list for PBXProject "Samples" */;
@@ -305,8 +215,6 @@
projectRoot = "";
targets = (
545DB9E921511E6300CA77B8 /* Samples */,
545DB9FD21511E6400CA77B8 /* SamplesTests */,
545DBA0821511E6400CA77B8 /* SamplesUITests */,
);
};
/* End PBXProject section */
@@ -322,20 +230,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
545DB9FC21511E6400CA77B8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
545DBA0721511E6400CA77B8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -347,6 +241,7 @@
54496C59263A7E5A0031E0C8 /* UseCaseController.swift in Sources */,
54CDC5D8215BBE23007D205C /* SupplementaryViews.swift in Sources */,
54B51116216AFE5F0033A6F3 /* Extensions.swift in Sources */,
544BC56826CC918200D0A436 /* AdaptiveLayoutTestViewController.swift in Sources */,
5442E24A25FC53C100A26F43 /* DebugTextViewController.swift in Sources */,
546341AC25C6426500CA0596 /* CustomState.swift in Sources */,
5442E23A25FC52CD00A26F43 /* ModalViewController.swift in Sources */,
@@ -365,37 +260,8 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
545DB9FA21511E6400CA77B8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
545DBA0321511E6400CA77B8 /* SampleTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
545DBA0521511E6400CA77B8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
545DBA0E21511E6400CA77B8 /* SampleUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
545DBA0021511E6400CA77B8 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 545DB9E921511E6300CA77B8 /* Samples */;
targetProxy = 545DB9FF21511E6400CA77B8 /* PBXContainerItemProxy */;
};
545DBA0B21511E6400CA77B8 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 545DB9E921511E6300CA77B8 /* Samples */;
targetProxy = 545DBA0A21511E6400CA77B8 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
545DB9F121511E6300CA77B8 /* Main.storyboard */ = {
isa = PBXVariantGroup;
@@ -541,11 +407,15 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Sources/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/swift",
);
PRODUCT_BUNDLE_IDENTIFIER = example.Samples;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -560,11 +430,15 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Sources/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/swift",
);
PRODUCT_BUNDLE_IDENTIFIER = example.Samples;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -572,90 +446,6 @@
};
name = Release;
};
545DBA1621511E6400CA77B8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = Tests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalSampleTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Samples.app/Samples";
};
name = Debug;
};
545DBA1721511E6400CA77B8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = Tests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalSampleTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Samples.app/Samples";
};
name = Release;
};
545DBA1921511E6400CA77B8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = J3D7L9FHSS;
INFOPLIST_FILE = UITests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalSampleUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = FloatingModalSample;
};
name = Debug;
};
545DBA1A21511E6400CA77B8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = J3D7L9FHSS;
INFOPLIST_FILE = UITests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalSampleUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = FloatingModalSample;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -677,24 +467,6 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
545DBA1521511E6400CA77B8 /* Build configuration list for PBXNativeTarget "SamplesTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
545DBA1621511E6400CA77B8 /* Debug */,
545DBA1721511E6400CA77B8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
545DBA1821511E6400CA77B8 /* Build configuration list for PBXNativeTarget "SamplesUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
545DBA1921511E6400CA77B8 /* Debug */,
545DBA1A21511E6400CA77B8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 545DB9E221511E6300CA77B8 /* Project object */;
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="RoN-h0-uBD">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="RoN-h0-uBD">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="Stack View standard spacing" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
@@ -15,7 +15,7 @@
<objects>
<navigationController storyboardIdentifier="RootNavigationController" id="RoN-h0-uBD" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="hNW-5m-Omi">
<rect key="frame" x="0.0" y="44" width="375" height="96"/>
<rect key="frame" x="0.0" y="48" width="414" height="96"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
@@ -31,22 +31,22 @@
<objects>
<viewController id="jF4-A0-Eq6" customClass="MainViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Smh-Bd-AAc">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="7IS-PU-x0P">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="M0G-C8-hAO" style="IBUITableViewCellStyleDefault" id="ySY-oA-g81">
<rect key="frame" x="0.0" y="28" width="600" height="43.5"/>
<rect key="frame" x="0.0" y="50" width="414" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ySY-oA-g81" id="sXB-nH-2g2">
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="M0G-C8-hAO">
<rect key="frame" x="16" y="0.0" width="568" height="43.5"/>
<rect key="frame" x="20" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
@@ -87,7 +87,7 @@
<objects>
<viewController storyboardIdentifier="SettingsViewController" id="C1X-9Z-TyQ" customClass="SettingsViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="af9-Zr-Ppc">
<rect key="frame" x="0.0" y="0.0" width="375" height="197.33000000000001"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="197.5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillProportionally" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="n93-ZL-fmC">
@@ -125,7 +125,7 @@
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="js8-Qv-lUC">
<rect key="frame" x="262" y="0.5" width="51" height="31"/>
<connections>
<action selector="toggleLargeTitle:" destination="C1X-9Z-TyQ" eventType="valueChanged" id="FJS-Ty-mCY"/>
<action selector="toggleLargeTitle:" destination="C1X-9Z-TyQ" eventType="valueChanged" id="CxM-wn-r09"/>
</connections>
</switch>
</subviews>
@@ -142,7 +142,7 @@
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="s6b-j9-8Kw">
<rect key="frame" x="262" y="0.5" width="51" height="31"/>
<connections>
<action selector="toggleTranslucent:" destination="C1X-9Z-TyQ" eventType="valueChanged" id="nL4-3L-9hh"/>
<action selector="toggleTranslucent:" destination="C1X-9Z-TyQ" eventType="valueChanged" id="w7r-AV-RqX"/>
</connections>
</switch>
</subviews>
@@ -163,8 +163,8 @@
</view>
<size key="freeformSize" width="375" height="197.33000000000001"/>
<connections>
<outlet property="largeTitlesSwicth" destination="js8-Qv-lUC" id="FOm-6k-ffi"/>
<outlet property="translucentSwicth" destination="s6b-j9-8Kw" id="jmf-WH-bzZ"/>
<outlet property="largeTitlesSwitch" destination="js8-Qv-lUC" id="szl-pU-uRE"/>
<outlet property="translucentSwitch" destination="s6b-j9-8Kw" id="8yK-Du-jkM"/>
<outlet property="versionLabel" destination="WmC-Tq-NDN" id="Woh-kK-U0m"/>
</connections>
</viewController>
@@ -177,11 +177,11 @@
<objects>
<viewController id="RpE-lI-27a" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="JER-jz-KSq">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IvG-yp-yzI">
<rect key="frame" x="20" y="0.0" width="39" height="30"/>
<rect key="frame" x="20" y="48" width="39" height="30"/>
<state key="normal" title="Close"/>
<connections>
<action selector="closeWithSender:" destination="RpE-lI-27a" eventType="touchUpInside" id="hj3-Xv-6Gq"/>
@@ -208,11 +208,11 @@
<objects>
<viewController id="pOk-Zm-vD9" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="85d-ub-G8k">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NbG-e8-HdI">
<rect key="frame" x="20" y="0.0" width="39" height="30"/>
<rect key="frame" x="20" y="48" width="39" height="30"/>
<state key="normal" title="Close"/>
<connections>
<action selector="closeWithSender:" destination="pOk-Zm-vD9" eventType="touchUpInside" id="111-PD-Pop"/>
@@ -239,11 +239,11 @@
<objects>
<viewController id="lto-Zc-Vtp" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="ji9-Ez-N7i">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eFN-tN-4Ct">
<rect key="frame" x="20" y="0.0" width="39" height="30"/>
<rect key="frame" x="20" y="48" width="39" height="30"/>
<state key="normal" title="Close"/>
<connections>
<action selector="closeWithSender:" destination="bYI-y3-Rzb" eventType="touchUpInside" id="YL4-GP-ZEZ"/>
@@ -383,22 +383,22 @@
<objects>
<viewController storyboardIdentifier="ModalViewController" id="bYI-y3-Rzb" customClass="ModalViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="qwo-GK-p1U">
<rect key="frame" x="0.0" y="0.0" width="375" height="778"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="720"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vut-mK-Y4t" customClass="SafeAreaView" customModule="Samples" customModuleProvider="target">
<rect key="frame" x="0.0" y="758" width="375" height="0.0"/>
<rect key="frame" x="0.0" y="720" width="375" height="0.0"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sbF-Az-7sy">
<rect key="frame" x="20" y="0.0" width="39" height="30"/>
<rect key="frame" x="20" y="48" width="39" height="30"/>
<state key="normal" title="Close"/>
<connections>
<action selector="closeWithSender:" destination="bYI-y3-Rzb" eventType="touchUpInside" id="MSC-ch-YJK"/>
</connections>
</button>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="44" translatesAutoresizingMaskIntoConstraints="NO" id="9p4-06-y2T">
<rect key="frame" x="134.5" y="88" width="106" height="326"/>
<rect key="frame" x="134.5" y="136" width="106" height="326"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="i9x-x5-n1q">
<rect key="frame" x="0.0" y="0.0" width="80" height="30"/>
@@ -602,7 +602,7 @@
<color key="backgroundColor" red="0.0078431372550000003" green="0.72156862749999995" blue="0.45882352939999999" alpha="1" colorSpace="calibratedRGB"/>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="noi-1a-5bZ" customClass="CloseButton" customModule="Samples" customModuleProvider="target">
<rect key="frame" x="319" y="0.0" width="44" height="44"/>
<rect key="frame" x="319" y="48" width="44" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="0jg-5D-A1F"/>
<constraint firstAttribute="width" constant="44" id="1Cq-PA-wgW"/>
@@ -612,7 +612,7 @@
</connections>
</button>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillProportionally" alignment="center" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="qux-uG-4o2">
<rect key="frame" x="8" y="8" width="148" height="31"/>
<rect key="frame" x="8" y="56" width="148" height="31"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="fitToBounds" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7lq-d3-PKi">
<rect key="frame" x="0.0" y="5.5" width="91" height="20.5"/>
@@ -660,19 +660,19 @@
<color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<gestureRecognizers/>
<constraints>
<constraint firstItem="noi-1a-5bZ" firstAttribute="top" secondItem="aOK-7l-cA6" secondAttribute="top" id="EQy-cr-F2Y"/>
<constraint firstItem="Kva-Z7-0qY" firstAttribute="top" secondItem="g7l-kO-y7q" secondAttribute="top" id="A4b-Le-m4I"/>
<constraint firstItem="noi-1a-5bZ" firstAttribute="top" secondItem="aOK-7l-cA6" secondAttribute="top" priority="750" id="EQy-cr-F2Y"/>
<constraint firstItem="tP3-oJ-4EB" firstAttribute="centerX" secondItem="aOK-7l-cA6" secondAttribute="centerX" id="EsD-Vf-dNZ"/>
<constraint firstItem="Kva-Z7-0qY" firstAttribute="top" secondItem="aOK-7l-cA6" secondAttribute="top" id="Fff-HL-4mo"/>
<constraint firstItem="8yw-OC-Ubk" firstAttribute="bottom" secondItem="g7l-kO-y7q" secondAttribute="bottom" id="JOL-wC-w74"/>
<constraint firstItem="8yw-OC-Ubk" firstAttribute="bottom" secondItem="g7l-kO-y7q" secondAttribute="bottom" priority="750" id="JOL-wC-w74"/>
<constraint firstItem="8yw-OC-Ubk" firstAttribute="leading" secondItem="aOK-7l-cA6" secondAttribute="leading" id="RiJ-Hb-OOZ"/>
<constraint firstItem="8yw-OC-Ubk" firstAttribute="trailing" secondItem="aOK-7l-cA6" secondAttribute="trailing" id="Sof-yL-mwK"/>
<constraint firstItem="tP3-oJ-4EB" firstAttribute="top" secondItem="g7l-kO-y7q" secondAttribute="top" constant="88" id="Zhb-Ss-epe"/>
<constraint firstItem="tP3-oJ-4EB" firstAttribute="top" secondItem="g7l-kO-y7q" secondAttribute="top" priority="750" constant="88" id="Zhb-Ss-epe"/>
<constraint firstItem="Kva-Z7-0qY" firstAttribute="trailing" secondItem="aOK-7l-cA6" secondAttribute="trailing" id="kkp-Yo-FQW"/>
<constraint firstItem="aOK-7l-cA6" firstAttribute="trailing" secondItem="noi-1a-5bZ" secondAttribute="trailing" constant="12" id="lv9-Nf-HNB"/>
<constraint firstItem="qux-uG-4o2" firstAttribute="top" secondItem="aOK-7l-cA6" secondAttribute="top" constant="8" id="naa-cf-ZIc"/>
<constraint firstItem="Kva-Z7-0qY" firstAttribute="leading" secondItem="aOK-7l-cA6" secondAttribute="leading" id="oVC-i1-TwS"/>
<constraint firstItem="aOK-7l-cA6" firstAttribute="bottom" secondItem="Kva-Z7-0qY" secondAttribute="bottom" id="rW2-mF-5DR"/>
<constraint firstItem="8yw-OC-Ubk" firstAttribute="top" relation="greaterThanOrEqual" secondItem="tP3-oJ-4EB" secondAttribute="bottom" constant="88" id="vKQ-h9-uKt"/>
<constraint firstItem="aOK-7l-cA6" firstAttribute="bottom" secondItem="Kva-Z7-0qY" secondAttribute="bottom" priority="750" id="rW2-mF-5DR"/>
<constraint firstItem="8yw-OC-Ubk" firstAttribute="top" relation="greaterThanOrEqual" secondItem="tP3-oJ-4EB" secondAttribute="bottom" priority="750" constant="88" id="vKQ-h9-uKt"/>
<constraint firstItem="qux-uG-4o2" firstAttribute="leading" secondItem="g7l-kO-y7q" secondAttribute="leading" constant="8" id="zXb-R9-bMO"/>
</constraints>
<connections>
@@ -782,7 +782,57 @@ Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
</objects>
<point key="canvasLocation" x="-1" y="734"/>
</scene>
<!--Adaptive Layout Test View Controller-->
<scene sceneID="rDI-lU-wEx">
<objects>
<viewController storyboardIdentifier="AdaptiveLayoutTestViewController" id="5nC-6E-bXf" customClass="AdaptiveLayoutTestViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="jXL-Ss-NCJ">
<rect key="frame" x="0.0" y="0.0" width="375" height="778"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="W7W-ET-Wco" customClass="IntrinsicTableView" customModule="Samples" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="778"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="Cell" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="Mqi-zK-WA7">
<rect key="frame" x="0.0" y="50" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Mqi-zK-WA7" id="X46-Fp-6Hr">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="5nC-6E-bXf" id="RHg-aY-HNW"/>
<outlet property="delegate" destination="5nC-6E-bXf" id="0YX-fh-bB8"/>
</connections>
</tableView>
</subviews>
<viewLayoutGuide key="safeArea" id="ZfG-sd-dcQ"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="W7W-ET-Wco" firstAttribute="trailing" secondItem="ZfG-sd-dcQ" secondAttribute="trailing" id="3kP-rg-7c6"/>
<constraint firstAttribute="bottom" secondItem="W7W-ET-Wco" secondAttribute="bottom" id="FdS-X9-D1D"/>
<constraint firstItem="W7W-ET-Wco" firstAttribute="leading" secondItem="ZfG-sd-dcQ" secondAttribute="leading" id="HXa-oO-jag"/>
<constraint firstItem="W7W-ET-Wco" firstAttribute="top" secondItem="jXL-Ss-NCJ" secondAttribute="top" id="gMX-Wq-7G8"/>
</constraints>
</view>
<size key="freeformSize" width="375" height="778"/>
<connections>
<outlet property="tableView" destination="W7W-ET-Wco" id="N54-Fv-2Jq"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="7hJ-XW-9az" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4005" y="734"/>
</scene>
</scenes>
<designables>
<designable name="noi-1a-5bZ">
<size key="intrinsicContentSize" width="30" height="30"/>
</designable>
</designables>
<inferredMetricsTieBreakers>
<segue reference="r1P-2i-NDe"/>
</inferredMetricsTieBreakers>
@@ -0,0 +1,89 @@
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
import UIKit
import FloatingPanel
final class AdaptiveLayoutTestViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
class PanelLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .full
private unowned var targetGuide: UILayoutGuide
init(targetGuide: UILayoutGuide) {
self.targetGuide = targetGuide
}
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelAdaptiveLayoutAnchor(
absoluteOffset: 0.0,
contentLayout: targetGuide,
referenceGuide: .superview,
contentBoundingGuide: .safeArea
),
.half: FloatingPanelAdaptiveLayoutAnchor(
fractionalOffset: 0.5,
contentLayout: targetGuide,
referenceGuide: .superview,
contentBoundingGuide: .safeArea
),
]
}
}
@IBOutlet weak var tableView: IntrinsicTableView!
private let cellID = "Cell"
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableView.automaticDimension
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
}
// MARK: - UITableViewDataSource
func numberOfSections(in tableView: UITableView) -> Int {
1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
50
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
cell.textLabel?.text = "\(indexPath.row)"
return cell
}
// MARK: - UITableViewDelegate
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = .orange
return headerView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
44.0
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
40
}
}
class IntrinsicTableView: UITableView {
override var contentSize:CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
}
}
@@ -18,7 +18,7 @@ class DebugTableViewController: InspectableViewController {
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.alignment = .trailing
stackView.spacing = 10.0
stackView.spacing = 4
return stackView
}()
private lazy var reorderButton: UIButton = {
@@ -28,40 +28,48 @@ class DebugTableViewController: InspectableViewController {
button.addTarget(self, action: #selector(reorderItems), for: .touchUpInside)
return button
}()
private lazy var trackingSwitchWrapper: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .fillProportionally
stackView.alignment = .center
stackView.spacing = 8.0
stackView.addArrangedSubview(trackingLabel)
stackView.addArrangedSubview(trackingSwitch)
return stackView
private lazy var trackingButton: UIButton = {
let button = UIButton()
button.setTitle(Menu.trackScrolling.rawValue, for: .normal)
button.setTitleColor(view.tintColor, for: .normal)
button.addTarget(self, action: #selector(toggleTrackingScroll), for: .touchUpInside)
return button
}()
private lazy var trackingLabel: UILabel = {
let label = UILabel()
label.text = "Tracking"
label.font = UIFont.systemFont(ofSize: 17.0, weight: .regular)
return label
}()
private lazy var trackingSwitch: UISwitch = {
let trackingSwitch = UISwitch()
trackingSwitch.isOn = true
trackingSwitch.addTarget(self, action: #selector(turnTrackingOn), for: .touchUpInside)
return trackingSwitch
private lazy var followingButton: UIButton = {
let button = UIButton()
button.setTitleColor(view.tintColor, for: .normal)
button.addTarget(self, action: #selector(toggleFollowingScroll), for: .touchUpInside)
return button
}()
// MARK: - Properties
var kvoObservers: [NSKeyValueObservation] = []
private var fpc: FloatingPanelController? { parent as? FloatingPanelController }
private lazy var items: [String] = {
let items = (0..<100).map { "Items \($0)" }
return Command.replace(items: items)
}()
private var itemHeight: CGFloat = 66.0
// MARK: Flags
private var tracksScrollView: Bool = false {
didSet {
let title = "\(Menu.trackScrolling.rawValue): \(tracksScrollView ? "on" : "off")"
trackingButton.setTitle(title, for: .normal)
}
}
private var followsScrollViewBouncing: Bool = false {
didSet {
let title = "\(Menu.followScrolling.rawValue): \(followsScrollViewBouncing ? "on" : "off")"
followingButton.setTitle(title, for: .normal)
}
}
enum Menu: String, CaseIterable {
case turnOffTracking = "Tracking"
case trackScrolling = "Tracking"
case followScrolling = "Following"
case reorder = "Reorder"
}
@@ -136,13 +144,19 @@ class DebugTableViewController: InspectableViewController {
switch menu {
case .reorder:
buttonStackView.addArrangedSubview(reorderButton)
case .turnOffTracking:
buttonStackView.addArrangedSubview(trackingSwitchWrapper)
case .trackScrolling:
buttonStackView.addArrangedSubview(trackingButton)
case .followScrolling:
buttonStackView.addArrangedSubview(followingButton)
}
}
// Set titles
tracksScrollView = false
followsScrollViewBouncing = false
}
// MARK: - Menu
@objc
private func reorderItems() {
if reorderButton.titleLabel?.text == Menu.reorder.rawValue {
@@ -155,15 +169,21 @@ class DebugTableViewController: InspectableViewController {
}
@objc
private func turnTrackingOn(_ sender: UISwitch) {
guard let fpc = self.parent as? FloatingPanelController else { return }
if sender.isOn {
private func toggleTrackingScroll() {
tracksScrollView.toggle()
guard let fpc = fpc else { return }
if tracksScrollView {
fpc.track(scrollView: tableView)
} else {
fpc.untrack(scrollView: tableView)
}
}
@objc
private func toggleFollowingScroll() {
followsScrollViewBouncing.toggle()
}
// MARK: - Actions
private func execute(command: Command, sourceView: UIView) {
@@ -250,6 +270,9 @@ extension DebugTableViewController: UITableViewDataSource {
extension DebugTableViewController: UITableViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if followsScrollViewBouncing {
fpc?.followScrollViewBouncing()
}
print("TableView --- ", scrollView.contentOffset, scrollView.contentInset)
}
@@ -5,29 +5,25 @@ import FloatingPanel
final class ImageViewController: UIViewController {
class PanelLayout: FloatingPanelLayout {
weak var targetGuide: UILayoutGuide?
init(targetGuide: UILayoutGuide?) {
private unowned var targetGuide: UILayoutGuide
init(targetGuide: UILayoutGuide) {
self.targetGuide = targetGuide
}
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .full
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
if #available(iOS 11.0, *), let targetGuide = targetGuide {
return [
.full: FloatingPanelAdaptiveLayoutAnchor(absoluteOffset: 0,
contentLayout: targetGuide,
referenceGuide: .superview),
.half: FloatingPanelAdaptiveLayoutAnchor(fractionalOffset: 0.5,
contentLayout: targetGuide,
referenceGuide: .superview)
]
} else {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 500,
edge: .bottom,
referenceGuide: .superview)
]
}
return [
.full: FloatingPanelAdaptiveLayoutAnchor(
absoluteOffset: 0,
contentLayout: targetGuide,
referenceGuide: .superview
),
.half: FloatingPanelAdaptiveLayoutAnchor(
fractionalOffset: 0.5,
contentLayout: targetGuide,
referenceGuide: .superview
)
]
}
}
@@ -65,14 +65,12 @@ final class ModalViewController: UIViewController, FloatingPanelControllerDelega
}
class ModalSecondLayout: FloatingPanelLayout {
var position: FloatingPanelPosition = .bottom
var initialState: FloatingPanelState { .half }
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 262, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea)
]
}
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .half
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 262, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea)
]
}
}
@@ -57,11 +57,9 @@ final class MultiPanelController: FloatingPanelController, FloatingPanelControll
private final class FirstViewLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .full
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 40.0, edge: .top, referenceGuide: .superview)
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 40.0, edge: .top, referenceGuide: .superview)
]
}
}
@@ -4,8 +4,8 @@ import UIKit
import FloatingPanel
final class SettingsViewController: InspectableViewController {
@IBOutlet weak var largeTitlesSwicth: UISwitch!
@IBOutlet weak var translucentSwicth: UISwitch!
@IBOutlet weak var largeTitlesSwitch: UISwitch!
@IBOutlet weak var translucentSwitch: UISwitch!
@IBOutlet weak var versionLabel: UILabel!
override func viewDidLoad() {
@@ -16,12 +16,12 @@ final class SettingsViewController: InspectableViewController {
super.viewDidLayoutSubviews()
if #available(iOS 11.0, *) {
let prefersLargeTitles = navigationController!.navigationBar.prefersLargeTitles
largeTitlesSwicth.setOn(prefersLargeTitles, animated: false)
largeTitlesSwitch.setOn(prefersLargeTitles, animated: false)
} else {
largeTitlesSwicth.isEnabled = false
largeTitlesSwitch.isEnabled = false
}
let isTranslucent = navigationController!.navigationBar.isTranslucent
translucentSwicth.setOn(isTranslucent, animated: false)
translucentSwitch.setOn(isTranslucent, animated: false)
}
@IBAction func toggleLargeTitle(_ sender: UISwitch) {
@@ -30,7 +30,19 @@ final class SettingsViewController: InspectableViewController {
}
}
@IBAction func toggleTranslucent(_ sender: UISwitch) {
navigationController?.navigationBar.isTranslucent = sender.isOn
// White non-translucent navigation bar, supports dark appearance
if #available(iOS 15, *) {
let appearance = UINavigationBarAppearance()
if sender.isOn {
appearance.configureWithTransparentBackground()
} else {
appearance.configureWithOpaqueBackground()
}
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
} else {
navigationController?.navigationBar.isTranslucent = sender.isOn
}
}
}
@@ -195,14 +195,12 @@ class OneTabBarPanelLayout: FloatingPanelLayout {
}
class TwoTabBarPanelLayout: FloatingPanelLayout {
var initialState: FloatingPanelState { .half }
var position: FloatingPanelPosition { .bottom }
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 261.0, edge: .bottom, referenceGuide: .safeArea)
]
}
let initialState: FloatingPanelState = .half
let position: FloatingPanelPosition = .bottom
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 261.0, edge: .bottom, referenceGuide: .safeArea)
]
}
class TwoTabBarPanelBehavior: FloatingPanelBehavior {
+14
View File
@@ -2,6 +2,20 @@
import UIKit
extension UIView {
func makeBoundsLayoutGuide() -> UILayoutGuide {
let guide = UILayoutGuide()
addLayoutGuide(guide)
NSLayoutConstraint.activate([
guide.topAnchor.constraint(equalTo: topAnchor),
guide.leftAnchor.constraint(equalTo: leftAnchor),
guide.bottomAnchor.constraint(equalTo: bottomAnchor),
guide.rightAnchor.constraint(equalTo: rightAnchor),
])
return guide
}
}
protocol LayoutGuideProvider {
var topAnchor: NSLayoutYAxisAnchor { get }
var bottomAnchor: NSLayoutYAxisAnchor { get }
+14 -26
View File
@@ -23,14 +23,11 @@ extension MainViewController: FloatingPanelLayout {
class TopPositionedPanelLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .top
let initialState: FloatingPanelState = .full
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 88.0, edge: .bottom, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .top, referenceGuide: .safeArea)
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 88.0, edge: .bottom, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .top, referenceGuide: .safeArea)
]
}
class IntrinsicPanelLayout: FloatingPanelBottomLayout {
@@ -45,13 +42,10 @@ class IntrinsicPanelLayout: FloatingPanelBottomLayout {
class RemovablePanelLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .half
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 130.0, edge: .bottom, referenceGuide: .safeArea)
]
}
]
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.3
@@ -61,13 +55,10 @@ class RemovablePanelLayout: FloatingPanelLayout {
class RemovablePanelLandscapeLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .full
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .bottom, referenceGuide: .safeArea)
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .bottom, referenceGuide: .safeArea)
]
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.3
@@ -77,12 +68,9 @@ class RemovablePanelLandscapeLayout: FloatingPanelLayout {
class ModalPanelLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .full
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .safeArea),
]
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.3
@@ -50,7 +50,9 @@ extension UseCase {
case .showCustomStatePanel: return "Show Panel with Custom state"
}
}
}
extension UseCase {
private enum Content {
case storyboard(String)
case viewController(UIViewController)
@@ -76,7 +78,7 @@ extension UseCase {
case .showNavigationController: return .storyboard("RootNavigationController") // Storyboard only
case .showTopPositionedPanel: return .viewController(DebugTableViewController())
case .showAdaptivePanel: return .storyboard(String(describing: ImageViewController.self))
case .showAdaptivePanelWithCustomGuide: return .storyboard(String(describing: ImageViewController.self))
case .showAdaptivePanelWithCustomGuide: return .storyboard(String(describing: AdaptiveLayoutTestViewController.self))
case .showCustomStatePanel: return .viewController(DebugTableViewController())
}
}
@@ -86,6 +88,7 @@ extension UseCase {
case .storyboard(let id):
return storyboard.instantiateViewController(withIdentifier: id)
case .viewController(let vc):
vc.loadViewIfNeeded()
return vc
}
}
@@ -212,28 +212,40 @@ extension UseCaseController {
addMain(panel: fpc)
case .showTopPositionedPanel: // For debug
let fpc = FloatingPanelController()
let fpc = FloatingPanelController(delegate: self)
let contentVC = UIViewController()
contentVC.view.backgroundColor = .red
fpc.set(contentViewController: contentVC)
addMain(panel: fpc)
case .showAdaptivePanel, .showAdaptivePanelWithCustomGuide:
case .showAdaptivePanel:
let fpc = FloatingPanelController()
fpc.isRemovalInteractionEnabled = true
fpc.set(contentViewController: contentVC)
fpc.ext_trackScrollView(in: contentVC)
if case let contentVC as ImageViewController = contentVC {
if #available(iOS 11.0, *) {
let mode: ImageViewController.Mode = (useCase == .showAdaptivePanelWithCustomGuide) ? .withHeaderFooter : .onlyImage
let layoutGuide = contentVC.layoutGuideFor(mode: mode)
fpc.layout = ImageViewController.PanelLayout(targetGuide: layoutGuide)
} else {
fpc.layout = ImageViewController.PanelLayout(targetGuide: nil)
}
let mode: ImageViewController.Mode = (useCase == .showAdaptivePanelWithCustomGuide) ? .withHeaderFooter : .onlyImage
let layoutGuide = contentVC.layoutGuideFor(mode: mode)
fpc.layout = ImageViewController.PanelLayout(targetGuide: layoutGuide)
}
addMain(panel: fpc)
case .showAdaptivePanelWithCustomGuide:
let fpc = FloatingPanelController()
fpc.isRemovalInteractionEnabled = true
fpc.contentInsetAdjustmentBehavior = .always
fpc.surfaceView.appearance = {
let appearance = SurfaceAppearance()
appearance.cornerRadius = 6.0
return appearance
}()
fpc.set(contentViewController: contentVC)
fpc.ext_trackScrollView(in: contentVC)
fpc.layout = AdaptiveLayoutTestViewController.PanelLayout(targetGuide: contentVC.view.makeBoundsLayoutGuide())
addMain(panel: fpc)
case .showCustomStatePanel:
let fpc = FloatingPanelController()
fpc.delegate = self
@@ -283,7 +295,7 @@ extension UseCaseController {
}
}
@objc
@objc
private func handleSurface(tapGesture: UITapGestureRecognizer) {
switch mainPanelVC.state {
case .full:
@@ -377,6 +389,9 @@ private extension FloatingPanelController {
case let contentVC as ImageViewController:
track(scrollView: contentVC.scrollView)
case let contentVC as AdaptiveLayoutTestViewController:
track(scrollView: contentVC.tableView)
default:
break
}
-22
View File
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
-28
View File
@@ -1,28 +0,0 @@
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
import XCTest
@testable import FloatingPanelSample
class SampleTests: XCTestCase {
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
-22
View File
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
@@ -1,28 +0,0 @@
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
import XCTest
class SampleUITests: XCTestCase {
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
XCUIApplication().launch()
// In UI tests its important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() {
// Use recording to get started writing UI tests.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
}
@@ -15,6 +15,7 @@
545BA71421BA3217007F7846 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 545BA71321BA3217007F7846 /* main.m */; };
545BA72621BA3BAF007F7846 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 545BA72521BA3BAF007F7846 /* FloatingPanel.framework */; };
545BA72721BA3BAF007F7846 /* FloatingPanel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 545BA72521BA3BAF007F7846 /* FloatingPanel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5D82A6B028D18447006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6AF28D18443006A44BA /* libswiftCoreGraphics.tbd */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -44,6 +45,7 @@
545BA71321BA3217007F7846 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
545BA72221BA3867007F7846 /* SamplesObjC-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SamplesObjC-Bridging-Header.h"; sourceTree = "<group>"; };
545BA72521BA3BAF007F7846 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5D82A6AF28D18443006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -51,6 +53,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5D82A6B028D18447006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
545BA72621BA3BAF007F7846 /* FloatingPanel.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -64,6 +67,7 @@
545BA72521BA3BAF007F7846 /* FloatingPanel.framework */,
545BA70321BA3214007F7846 /* SamplesObjC */,
545BA70221BA3214007F7846 /* Products */,
5D82A6AE28D18443006A44BA /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -92,6 +96,14 @@
path = SamplesObjC;
sourceTree = "<group>";
};
5D82A6AE28D18443006A44BA /* Frameworks */ = {
isa = PBXGroup;
children = (
5D82A6AF28D18443006A44BA /* libswiftCoreGraphics.tbd */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -320,6 +332,10 @@
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/swift",
);
PRODUCT_BUNDLE_IDENTIFIER = example.SamplesObjC;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "SamplesObjC/SamplesObjC-Bridging-Header.h";
@@ -342,6 +358,10 @@
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/swift",
);
PRODUCT_BUNDLE_IDENTIFIER = example.SamplesObjC;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "SamplesObjC/SamplesObjC-Bridging-Header.h";
@@ -29,7 +29,6 @@ static FloatingPanelState *_lastQuart;
FloatingPanelController *fpc = [[FloatingPanelController alloc] init];
[fpc setContentViewController:nil];
[fpc trackScrollView:nil];
[fpc setDelegate:self];
[fpc setLayout: [MyFloatingPanelLayout new]];
@@ -14,6 +14,7 @@
548DF95E21705BE10041922A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 548DF95C21705BE10041922A /* LaunchScreen.storyboard */; };
549D23CF233C77CF008EF4D7 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23CE233C77CF008EF4D7 /* FloatingPanel.framework */; };
549D23D0233C77CF008EF4D7 /* FloatingPanel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23CE233C77CF008EF4D7 /* FloatingPanel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5D82A6AA28D18432006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6A928D1842B006A44BA /* libswiftCoreGraphics.tbd */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -39,6 +40,7 @@
548DF95D21705BE10041922A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
548DF95F21705BE10041922A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
549D23CE233C77CF008EF4D7 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5D82A6A928D1842B006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -46,6 +48,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5D82A6AA28D18432006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
549D23CF233C77CF008EF4D7 /* FloatingPanel.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -59,6 +62,7 @@
549D23CE233C77CF008EF4D7 /* FloatingPanel.framework */,
548DF95221705BE00041922A /* Stocks */,
548DF95121705BE00041922A /* Products */,
5D82A6A828D1842A006A44BA /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -83,6 +87,14 @@
path = Stocks;
sourceTree = "<group>";
};
5D82A6A828D1842A006A44BA /* Frameworks */ = {
isa = PBXGroup;
children = (
5D82A6A928D1842B006A44BA /* libswiftCoreGraphics.tbd */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -312,6 +324,10 @@
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/swift",
);
PRODUCT_BUNDLE_IDENTIFIER = example.Stocks;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -331,6 +347,10 @@
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/swift",
);
PRODUCT_BUNDLE_IDENTIFIER = example.Stocks;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -101,14 +101,12 @@ class FloatingPanelStocksLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .tip
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 56.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 262.0, edge: .bottom, referenceGuide: .safeArea),
/* Visible + ToolView */
.tip: FloatingPanelLayoutAnchor(absoluteInset: 85.0 + 44.0, edge: .bottom, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 56.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 262.0, edge: .bottom, referenceGuide: .safeArea),
/* Visible + ToolView */
.tip: FloatingPanelLayoutAnchor(absoluteInset: 85.0 + 44.0, edge: .bottom, referenceGuide: .safeArea),
]
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.0
+2 -2
View File
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "FloatingPanel"
s.version = "2.5.2"
s.version = "2.6.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.
@@ -14,7 +14,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 => s.version.to_s }
s.source_files = "Sources/*.swift"
s.swift_versions = ['5.1', '5.2', '5.3']
s.swift_version = '5.0'
s.framework = "UIKit"
+14 -141
View File
@@ -17,12 +17,10 @@
545DB9E021511AC100CA77B8 /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9DF21511AC100CA77B8 /* Controller.swift */; };
545DBA2B2152383100CA77B8 /* GrabberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DBA2A2152383100CA77B8 /* GrabberView.swift */; };
546055BF2333C4740069F400 /* TestSupports.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542753C722C49A8F00D17955 /* TestSupports.swift */; };
5469F4A224B003EF00537F8A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5469F49F24B003EF00537F8A /* LaunchScreen.storyboard */; };
5469F4A324B003EF00537F8A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4A024B003EF00537F8A /* AppDelegate.swift */; };
5469F4AE24B30D7E00537F8A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4AD24B30D7E00537F8A /* State.swift */; };
5469F4B024B30E1500537F8A /* LayoutAnchoring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4AF24B30E1500537F8A /* LayoutAnchoring.swift */; };
5469F4B224B30F1100537F8A /* Position.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4B124B30F1100537F8A /* Position.swift */; };
5469F4B424B30F3500537F8A /* LayoutReferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4B324B30F3500537F8A /* LayoutReferences.swift */; };
5469F4B424B30F3500537F8A /* LayoutProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4B324B30F3500537F8A /* LayoutProperties.swift */; };
549C371F2361E15E007D8058 /* ExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549C371E2361E15D007D8058 /* ExtensionTests.swift */; };
549E944522CF295D0050AECF /* StateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549E944422CF295D0050AECF /* StateTests.swift */; };
54A6B6B122968B530077F348 /* CoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A6B6B022968B530077F348 /* CoreTests.swift */; };
@@ -34,6 +32,7 @@
54CFBFC5215CD09C006B5735 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CFBFC4215CD09C006B5735 /* Core.swift */; };
54DBA3DC262E938500D75969 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54DBA3DB262E938500D75969 /* Extensions.swift */; };
54E3992727141F5100A8F9ED /* FloatingPanel.docc in Sources */ = {isa = PBXBuildFile; fileRef = 54E3992627141F5100A8F9ED /* FloatingPanel.docc */; };
5D82A6B528D18464006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6B428D18461006A44BA /* libswiftCoreGraphics.tbd */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -44,13 +43,6 @@
remoteGlobalIDString = 545DB9C02151169500CA77B8;
remoteInfo = FloatingModalController;
};
54E740DC218AFE9F005C1A34 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 545DB9B82151169500CA77B8 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 54E740C9218AFD67005C1A34;
remoteInfo = TestingHost;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@@ -67,13 +59,10 @@
545DB9D12151169500CA77B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
545DB9DF21511AC100CA77B8 /* Controller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Controller.swift; sourceTree = "<group>"; };
545DBA2A2152383100CA77B8 /* GrabberView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrabberView.swift; sourceTree = "<group>"; };
5469F49F24B003EF00537F8A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
5469F4A024B003EF00537F8A /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
5469F4A124B003EF00537F8A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5469F4AD24B30D7E00537F8A /* State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = "<group>"; };
5469F4AF24B30E1500537F8A /* LayoutAnchoring.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutAnchoring.swift; sourceTree = "<group>"; };
5469F4B124B30F1100537F8A /* Position.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Position.swift; sourceTree = "<group>"; };
5469F4B324B30F3500537F8A /* LayoutReferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutReferences.swift; sourceTree = "<group>"; };
5469F4B324B30F3500537F8A /* LayoutProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutProperties.swift; sourceTree = "<group>"; };
549C371E2361E15D007D8058 /* ExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionTests.swift; sourceTree = "<group>"; };
549E944422CF295D0050AECF /* StateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateTests.swift; sourceTree = "<group>"; };
54A6B6B022968B530077F348 /* CoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreTests.swift; sourceTree = "<group>"; };
@@ -85,7 +74,7 @@
54CFBFC4215CD09C006B5735 /* Core.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Core.swift; sourceTree = "<group>"; };
54DBA3DB262E938500D75969 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
54E3992627141F5100A8F9ED /* FloatingPanel.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = FloatingPanel.docc; sourceTree = "<group>"; };
54E740CA218AFD67005C1A34 /* FloatingPanelTesting.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FloatingPanelTesting.app; sourceTree = BUILT_PRODUCTS_DIR; };
5D82A6B428D18461006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -93,6 +82,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5D82A6B528D18464006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -104,13 +94,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
54E740C7218AFD67005C1A34 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -120,6 +103,7 @@
545DB9C32151169500CA77B8 /* Sources */,
545DB9CE2151169500CA77B8 /* Tests */,
545DB9C22151169500CA77B8 /* Products */,
5D82A6B328D18460006A44BA /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -128,7 +112,6 @@
children = (
545DB9C12151169500CA77B8 /* FloatingPanel.framework */,
545DB9CA2151169500CA77B8 /* FloatingPanelTests.xctest */,
54E740CA218AFD67005C1A34 /* FloatingPanelTesting.app */,
);
name = Products;
sourceTree = "<group>";
@@ -141,7 +124,7 @@
5469F4B124B30F1100537F8A /* Position.swift */,
54CFBFC4215CD09C006B5735 /* Core.swift */,
54CFBFC2215CD045006B5735 /* Layout.swift */,
5469F4B324B30F3500537F8A /* LayoutReferences.swift */,
5469F4B324B30F3500537F8A /* LayoutProperties.swift */,
5469F4AF24B30E1500537F8A /* LayoutAnchoring.swift */,
5450EEE321646DF500135936 /* Behavior.swift */,
54352E9721A521CA00CBCA08 /* PassthroughView.swift */,
@@ -161,7 +144,6 @@
545DB9CE2151169500CA77B8 /* Tests */ = {
isa = PBXGroup;
children = (
5469F49E24B003EF00537F8A /* TestingApp */,
54A6B6B022968B530077F348 /* CoreTests.swift */,
545DB9CF2151169500CA77B8 /* ControllerTests.swift */,
542753C522C49A6E00D17955 /* LayoutTests.swift */,
@@ -174,14 +156,12 @@
path = Tests;
sourceTree = "<group>";
};
5469F49E24B003EF00537F8A /* TestingApp */ = {
5D82A6B328D18460006A44BA /* Frameworks */ = {
isa = PBXGroup;
children = (
5469F49F24B003EF00537F8A /* LaunchScreen.storyboard */,
5469F4A024B003EF00537F8A /* AppDelegate.swift */,
5469F4A124B003EF00537F8A /* Info.plist */,
5D82A6B428D18461006A44BA /* libswiftCoreGraphics.tbd */,
);
path = TestingApp;
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
@@ -228,30 +208,12 @@
);
dependencies = (
545DB9CD2151169500CA77B8 /* PBXTargetDependency */,
54E740DD218AFE9F005C1A34 /* PBXTargetDependency */,
);
name = FloatingPanelTests;
productName = FloatingModalControllerTests;
productReference = 545DB9CA2151169500CA77B8 /* FloatingPanelTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
54E740C9218AFD67005C1A34 /* TestingApp */ = {
isa = PBXNativeTarget;
buildConfigurationList = 54E740D9218AFD6A005C1A34 /* Build configuration list for PBXNativeTarget "TestingApp" */;
buildPhases = (
54E740C6218AFD67005C1A34 /* Sources */,
54E740C7218AFD67005C1A34 /* Frameworks */,
54E740C8218AFD67005C1A34 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = TestingApp;
productName = TestingHost;
productReference = 54E740CA218AFD67005C1A34 /* FloatingPanelTesting.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -268,10 +230,6 @@
};
545DB9C92151169500CA77B8 = {
CreatedOnToolsVersion = 10.0;
TestTargetID = 54E740C9218AFD67005C1A34;
};
54E740C9218AFD67005C1A34 = {
CreatedOnToolsVersion = 10.1;
};
};
};
@@ -290,7 +248,6 @@
targets = (
545DB9C02151169500CA77B8 /* FloatingPanel */,
545DB9C92151169500CA77B8 /* FloatingPanelTests */,
54E740C9218AFD67005C1A34 /* TestingApp */,
);
};
/* End PBXProject section */
@@ -310,14 +267,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
54E740C8218AFD67005C1A34 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5469F4A224B003EF00537F8A /* LaunchScreen.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -329,7 +278,7 @@
5469F4B024B30E1500537F8A /* LayoutAnchoring.swift in Sources */,
54CDC5D3215B6D5A007D205C /* SurfaceView.swift in Sources */,
54CFBFC3215CD045006B5735 /* Layout.swift in Sources */,
5469F4B424B30F3500537F8A /* LayoutReferences.swift in Sources */,
5469F4B424B30F3500537F8A /* LayoutProperties.swift in Sources */,
54CDC5D5215B6D8D007D205C /* BackdropView.swift in Sources */,
54352E9821A521CA00CBCA08 /* PassthroughView.swift in Sources */,
54CFBFC5215CD09C006B5735 /* Core.swift in Sources */,
@@ -358,14 +307,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
54E740C6218AFD67005C1A34 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5469F4A324B003EF00537F8A /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -374,11 +315,6 @@
target = 545DB9C02151169500CA77B8 /* FloatingPanel */;
targetProxy = 545DB9CC2151169500CA77B8 /* PBXContainerItemProxy */;
};
54E740DD218AFE9F005C1A34 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 54E740C9218AFD67005C1A34 /* TestingApp */;
targetProxy = 54E740DC218AFE9F005C1A34 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
@@ -581,11 +517,10 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalControllerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FloatingPanelTesting.app/FloatingPanelTesting";
};
name = Debug;
};
@@ -602,47 +537,10 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalControllerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FloatingPanelTesting.app/FloatingPanelTesting";
};
name = Release;
};
54E740DA218AFD6A005C1A34 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Tests/TestingApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTesting;
PRODUCT_NAME = FloatingPanelTesting;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
54E740DB218AFD6A005C1A34 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Tests/TestingApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTesting;
PRODUCT_NAME = FloatingPanelTesting;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
@@ -755,25 +653,10 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalControllerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FloatingPanelTesting.app/FloatingPanelTesting";
};
name = Test;
};
54E8AC6A2286CFB6000C5A12 /* Test */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Tests/TestingApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTesting;
PRODUCT_NAME = FloatingPanelTesting;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Test;
};
@@ -810,16 +693,6 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
54E740D9218AFD6A005C1A34 /* Build configuration list for PBXNativeTarget "TestingApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
54E740DA218AFD6A005C1A34 /* Debug */,
54E740DB218AFD6A005C1A34 /* Release */,
54E8AC6A2286CFB6000C5A12 /* Test */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 545DB9B82151169500CA77B8 /* Project object */;
+32 -40
View File
@@ -1,15 +1,15 @@
[![Build Status](https://travis-ci.org/SCENEE/FloatingPanel.svg?branch=master)](https://travis-ci.org/SCENEE/FloatingPanel)
[![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)
[![Swift 5](https://img.shields.io/badge/swift-5-orange.svg?style=flat)](https://swift.org/)
[![Platform](https://img.shields.io/cocoapods/p/FloatingPanel.svg)](https://cocoapods.org/pods/FloatingPanel)
[![Swift 5](https://img.shields.io/badge/Swift-5-orange.svg?style=flat)](https://swift.org/)
[![Version](https://img.shields.io/cocoapods/v/FloatingPanel.svg)](https://cocoapods.org/pods/FloatingPanel)
![GitHub Workflow Status (with branch)](https://img.shields.io/github/actions/workflow/status/scenee/FloatingPanel/ci.yml?branch=master)
[![Carthage compatible](https://img.shields.io/badge/carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
# FloatingPanel
FloatingPanel is a simple and easy-to-use UI component for a new interface introduced in Apple Maps, Shortcuts and Stocks app.
The new interface displays the related contents and utilities in parallel as a user wants.
FloatingPanel is a simple and easy-to-use UI component designed for a user interface featured in Apple Maps, Shortcuts and Stocks app.
The user interface displays related content and utilities alongside the main content.
📝[Here](https://docs.scenee.com/documentation/floatingpanel) is the API references for the latest version powered by [DocC](https://developer.apple.com/documentation/docc).
Please see also [the API reference](https://floatingpanel.github.io/2.6.2/documentation/floatingpanel/) for more details, powered by [DocC](https://developer.apple.com/documentation/docc).
![Maps](https://github.com/SCENEE/FloatingPanel/blob/master/assets/maps.gif)
![Stocks](https://github.com/SCENEE/FloatingPanel/blob/master/assets/stocks.gif)
@@ -75,14 +75,14 @@ The new interface displays the related contents and utilities in parallel as a u
- [x] Removal interaction
- [x] Multi panel support
- [x] Modal presentation
- [x] 4 positioning support(top, left, bottom, right)
- [x] Support for 4 positions (top, left, bottom, right)
- [x] 1 or more magnetic anchors(full, half, tip and more)
- [x] Layout support for all trait environments(i.e. Landscape orientation)
- [x] Common UI elements: surface, backdrop and grabber handle
- [x] Free from common issues of Auto Layout and gesture handling
- [x] Free from common Auto Layout and gesture handling issues
- [x] Compatible with Objective-C
Examples are here.
Examples can be found here:
- [Examples/Maps](https://github.com/SCENEE/FloatingPanel/tree/master/Examples/Maps) like Apple Maps.app.
- [Examples/Stocks](https://github.com/SCENEE/FloatingPanel/tree/master/Examples/Stocks) like Apple Stocks.app.
@@ -91,11 +91,11 @@ Examples are here.
## Requirements
FloatingPanel is written in Swift 5.0+. Compatible with iOS 11.0+.
FloatingPanel is written in Swift 5.0+ and compatible with iOS 11.0+.
The deployment is still iOS 10, but it is recommended to use this library on iOS 11+.
While it still supports iOS 10, it is recommended to use this library on iOS 11+.
:pencil2: You would like to use Swift 4.0. Please use FloatingPanel v1.
:pencil2: If you'd like to use Swift 4.0, please use FloatingPanel v1.
## Installation
@@ -269,13 +269,11 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
class MyFloatingPanelLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .tip
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea),
]
}
```
@@ -321,12 +319,11 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
class LandscapePanelLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .tip
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
]
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
return [
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 8.0),
@@ -345,12 +342,10 @@ class LandscapePanelLayout: FloatingPanelLayout {
class IntrinsicPanelLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .full
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0, referenceGuide: .safeArea),
.half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0, referenceGuide: .safeArea),
.half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
]
...
}
```
@@ -364,14 +359,11 @@ Use `.superview` reference guide in your anchors.
```swift
class MyFullScreenLayout: FloatingPanelLayout {
...
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .superview),
]
}
```
+15 -1
View File
@@ -7,5 +7,19 @@ import UIKit
public class BackdropView: UIView {
/// The gesture recognizer for tap gestures to dismiss a panel.
@objc public var dismissalTapGestureRecognizer: UITapGestureRecognizer!
///
/// By default, this gesture recognizer is disabled as following the default behavior of iOS modalities.
/// To dismiss a panel by tap gestures on the backdrop, `dismissalTapGestureRecognizer.isEnabled` is set to true.
@objc public var dismissalTapGestureRecognizer: UITapGestureRecognizer
init() {
dismissalTapGestureRecognizer = UITapGestureRecognizer()
dismissalTapGestureRecognizer.isEnabled = false
super.init(frame: .zero)
addGestureRecognizer(dismissalTapGestureRecognizer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
+24 -6
View File
@@ -37,9 +37,9 @@ import UIKit
@objc optional
func floatingPanelShouldBeginDragging(_ fpc: FloatingPanelController) -> Bool
/// Called when the user drags the surface or the surface is attracted to a state anchor.
/// Called while the user drags the surface or the surface moves to a state anchor.
@objc optional
func floatingPanelDidMove(_ fpc: FloatingPanelController) // any surface frame changes in dragging
func floatingPanelDidMove(_ fpc: FloatingPanelController)
/// Called on start of dragging (may require some time and or distance to move)
@objc optional
@@ -316,7 +316,7 @@ open class FloatingPanelController: UIViewController {
// Change a layout for the new view size
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: size) {
layout = newLayout
activateLayout(forceLayout: false)
activateLayout(forceLayout: true)
}
if view.translatesAutoresizingMaskIntoConstraints {
@@ -335,7 +335,7 @@ open class FloatingPanelController: UIViewController {
// Change a layout for the new trait collection
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: newCollection) {
self.layout = newLayout
activateLayout(forceLayout: false)
activateLayout(forceLayout: true)
}
}
@@ -401,8 +401,10 @@ open class FloatingPanelController: UIViewController {
}
private func activateLayout(forceLayout: Bool = false) {
floatingPanel.activateLayout(forceLayout: forceLayout,
contentInsetAdjustmentBehavior: contentInsetAdjustmentBehavior)
floatingPanel.activateLayout(
forceLayout: forceLayout,
contentInsetAdjustmentBehavior: contentInsetAdjustmentBehavior
)
}
func remove() {
@@ -439,6 +441,12 @@ open class FloatingPanelController: UIViewController {
// Use `self.view.safeAreaInsets` because `change.newValue` can be nil in particular case when
// is reported in https://github.com/SCENEE/FloatingPanel/issues/330
guard let self = self, change.oldValue != self.view.safeAreaInsets else { return }
// Sometimes the bounding rectangle of the controlled view becomes invalid when the screen is rotated.
// This results in its safeAreaInsets change. In that case, `self.update(safeAreaInsets:)` leads
// an unsatisfied constraints error. So this method should not be called with those bounds.
guard self.view.bounds.height > 0 && self.view.bounds.width > 0 else { return }
self.update(safeAreaInsets: self.view.safeAreaInsets)
}
} else {
@@ -586,6 +594,16 @@ open class FloatingPanelController: UIViewController {
break
}
}
/// [Experimental] Allows the panel to move as its tracking scroll view bounces.
///
/// This method must be called in the delegate method, `UIScrollViewDelegate.scrollViewDidScroll(_:)`,
/// of its tracking scroll view. This method only supports a bottom positioned panel for now.
///
/// - TODO: Support top, left and right positioned panels.
public func followScrollViewBouncing() {
floatingPanel.followScrollViewBouncing()
}
/// Cancel tracking the specify scroll view.
///
+63 -45
View File
@@ -20,13 +20,11 @@ class Core: NSObject, UIGestureRecognizerDelegate {
if let cur = scrollView {
if oldValue == nil {
initialScrollOffset = cur.contentOffset
scrollBounce = cur.bounces
scrollIndictorVisible = cur.showsVerticalScrollIndicator
}
} else {
if let pre = oldValue {
pre.isDirectionalLockEnabled = false
pre.bounces = scrollBounce
pre.showsVerticalScrollIndicator = scrollIndictorVisible
}
}
@@ -64,11 +62,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
// Scroll handling
private var initialScrollOffset: CGPoint = .zero
private var stopScrollDeceleration: Bool = false
private var scrollBounce = false
private var scrollIndictorVisible = false
private var grabberAreaFrame: CGRect {
return surfaceView.grabberAreaFrame
}
// MARK: - Interface
@@ -76,6 +70,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
ownerVC = vc
surfaceView = SurfaceView()
surfaceView.position = layout.position
surfaceView.backgroundColor = .white
backdropView = BackdropView()
@@ -98,11 +93,9 @@ class Core: NSObject, UIGestureRecognizerDelegate {
panGestureRecognizer.addTarget(self, action: #selector(handle(panGesture:)))
panGestureRecognizer.delegate = self
// Set tap-to-dismiss in the backdrop view
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleBackdrop(tapGesture:)))
tapGesture.isEnabled = false
backdropView.dismissalTapGestureRecognizer = tapGesture
backdropView.addGestureRecognizer(tapGesture)
// Set the tap-to-dismiss action of the backdrop view.
// It's disabled by default. See also BackdropView.dismissalTapGestureRecognizer.
backdropView.dismissalTapGestureRecognizer.addTarget(self, action: #selector(handleBackdrop(tapGesture:)))
}
deinit {
@@ -199,8 +192,10 @@ class Core: NSObject, UIGestureRecognizerDelegate {
// MARK: - Layout update
func activateLayout(forceLayout: Bool = false,
contentInsetAdjustmentBehavior: FloatingPanelController.ContentInsetAdjustmentBehavior) {
func activateLayout(
forceLayout: Bool = false,
contentInsetAdjustmentBehavior: FloatingPanelController.ContentInsetAdjustmentBehavior
) {
layoutAdapter.prepareLayout()
// preserve the current content offset if contentInsetAdjustmentBehavior is `.always`
@@ -210,7 +205,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
}
layoutAdapter.updateStaticConstraint()
layoutAdapter.activateLayout(for: state, forceLayout: true)
layoutAdapter.activateLayout(for: state, forceLayout: forceLayout)
// Update the backdrop alpha only when called in `Controller.show(animated:completion:)`
// Because that prevents a backdrop flicking just before presenting a panel(#466).
@@ -277,7 +272,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
is UIRotationGestureRecognizer,
is UIScreenEdgePanGestureRecognizer,
is UIPinchGestureRecognizer:
if grabberAreaFrame.contains(gestureRecognizer.location(in: gestureRecognizer.view)) {
if surfaceView.grabberAreaContains(gestureRecognizer.location(in: surfaceView)) {
return true
}
// all gestures of the tracking scroll view should be recognized in parallel
@@ -308,7 +303,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
return true
}
if grabberAreaFrame.contains(gestureRecognizer.location(in: gestureRecognizer.view)) {
if surfaceView.grabberAreaContains(gestureRecognizer.location(in: surfaceView)) {
return true
}
@@ -336,7 +331,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
scrollGestureRecognizers.contains(otherGestureRecognizer) {
switch otherGestureRecognizer {
case scrollView.panGestureRecognizer:
if grabberAreaFrame.contains(gestureRecognizer.location(in: gestureRecognizer.view)) {
if surfaceView.grabberAreaContains(gestureRecognizer.location(in: surfaceView)) {
return false
}
return allowScrollPanGesture(for: scrollView)
@@ -364,7 +359,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
// Should begin the pan gesture without waiting the dismiss gesture of a sheet modal.
return false
}
if grabberAreaFrame.contains(gestureRecognizer.location(in: gestureRecognizer.view)) {
if surfaceView.grabberAreaContains(gestureRecognizer.location(in: surfaceView)) {
return false
}
// Do not begin the pan gesture until these gestures fail
@@ -390,11 +385,11 @@ class Core: NSObject, UIGestureRecognizerDelegate {
let velocity = value(of: panGesture.velocity(in: panGesture.view))
let location = panGesture.location(in: surfaceView)
let belowEdgeMost = 0 > layoutAdapter.offsetFromMostExpandedAnchor + (1.0 / surfaceView.fp_displayScale)
let insideMostExpandedAnchor = 0 > layoutAdapter.offsetFromMostExpandedAnchor + (1.0 / surfaceView.fp_displayScale)
log.debug("""
scroll gesture(\(state):\(panGesture.state)) -- \
belowTop = \(belowEdgeMost), \
inside expanded anchor = \(insideMostExpandedAnchor), \
interactionInProgress = \(interactionInProgress), \
scroll offset = \(value(of: scrollView.contentOffset)), \
location = \(value(of: location)), velocity = \(velocity)
@@ -402,14 +397,14 @@ class Core: NSObject, UIGestureRecognizerDelegate {
let offsetDiff = value(of: scrollView.contentOffset - contentOffsetForPinning(of: scrollView))
if belowEdgeMost {
if insideMostExpandedAnchor {
// Scroll offset pinning
if state == layoutAdapter.mostExpandedState {
if interactionInProgress {
log.debug("settle offset --", value(of: initialScrollOffset))
stopScrolling(at: initialScrollOffset)
} else {
if grabberAreaFrame.contains(location) {
if surfaceView.grabberAreaContains(location) {
// Preserve the current content offset in moving from full.
stopScrolling(at: initialScrollOffset)
}
@@ -452,7 +447,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
}
if state == layoutAdapter.mostExpandedState {
// Adjust a small gap of the scroll offset just after swiping down starts in the grabber area.
if grabberAreaFrame.contains(location), grabberAreaFrame.contains(initialLocation) {
if surfaceView.grabberAreaContains(location), surfaceView.grabberAreaContains(initialLocation) {
stopScrolling(at: initialScrollOffset)
}
}
@@ -477,7 +472,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
}
}
// Adjust a small gap of the scroll offset just before swiping down starts in the grabber area,
if grabberAreaFrame.contains(location), grabberAreaFrame.contains(initialLocation) {
if surfaceView.grabberAreaContains(location), surfaceView.grabberAreaContains(initialLocation) {
stopScrolling(at: initialScrollOffset)
}
}
@@ -524,7 +519,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
// doesn't pass through .changed state after an interruptible animator is interrupted.
let diff = translation - .leastNonzeroMagnitude
layoutAdapter.updateInteractiveEdgeConstraint(diff: value(of: diff),
overflow: true,
scrollingContent: true,
allowsRubberBanding: behaviorAdapter.allowsRubberBanding(for:))
}
panningEnd(with: translation, velocity: velocity)
@@ -585,19 +580,19 @@ class Core: NSObject, UIGestureRecognizerDelegate {
}
// When the current point is within grabber area but the initial point is not, do scroll.
if grabberAreaFrame.contains(point), !grabberAreaFrame.contains(initialLocation) {
if surfaceView.grabberAreaContains(point), !surfaceView.grabberAreaContains(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) {
if surfaceView.grabberAreaContains(initialLocation), !surfaceView.frame.contains(point) {
return false
}
let scrollViewFrame = scrollView.convert(scrollView.bounds, to: surfaceView)
guard
scrollViewFrame.contains(initialLocation), // When the initial point not in scrollView, don't scroll.
!grabberAreaFrame.contains(point) // When point within grabber area, don't scroll.
!surfaceView.grabberAreaContains(point) // When point within grabber area, don't scroll.
else {
return false
}
@@ -640,7 +635,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
guard let scrollView = scrollView else { return }
if state == layoutAdapter.mostExpandedState {
if grabberAreaFrame.contains(location) {
if surfaceView.grabberAreaContains(location) {
initialScrollOffset = scrollView.contentOffset
}
} else {
@@ -653,10 +648,9 @@ class Core: NSObject, UIGestureRecognizerDelegate {
let pre = value(of: layoutAdapter.surfaceLocation)
let diff = value(of: translation - initialTranslation)
let next = pre + diff
let overflow = shouldOverflow(from: pre, to: next)
layoutAdapter.updateInteractiveEdgeConstraint(diff: diff,
overflow: overflow,
scrollingContent: shouldScrollingContentInMoving(from: pre, to: next),
allowsRubberBanding: behaviorAdapter.allowsRubberBanding(for:))
let cur = value(of: layoutAdapter.surfaceLocation)
@@ -670,32 +664,36 @@ class Core: NSObject, UIGestureRecognizerDelegate {
}
}
private func shouldOverflow(from pre: CGFloat, to next: CGFloat) -> Bool {
private func shouldScrollingContentInMoving(from pre: CGFloat, to next: CGFloat) -> Bool {
// Don't allow scrolling if the initial panning location is in the grabber area.
if surfaceView.grabberAreaContains(initialLocation) {
return false
}
if let scrollView = scrollView, scrollView.panGestureRecognizer.state == .changed {
switch layoutAdapter.position {
case .top:
if pre > .zero, pre < next,
scrollView.contentSize.height > scrollView.bounds.height || scrollView.alwaysBounceVertical {
return false
return true
}
case .left:
if pre > .zero, pre < next,
scrollView.contentSize.width > scrollView.bounds.width || scrollView.alwaysBounceHorizontal {
return false
return true
}
case .bottom:
if pre > .zero, pre > next,
scrollView.contentSize.height > scrollView.bounds.height || scrollView.alwaysBounceVertical {
return false
return true
}
case .right:
if pre > .zero, pre > next,
scrollView.contentSize.width > scrollView.bounds.width || scrollView.alwaysBounceHorizontal {
return false
return true
}
}
}
return true
return false
}
private func panningEnd(with translation: CGPoint, velocity: CGPoint) {
@@ -798,19 +796,24 @@ class Core: NSObject, UIGestureRecognizerDelegate {
initialSurfaceLocation = layoutAdapter.surfaceLocation
if state == layoutAdapter.mostExpandedState, let scrollView = scrollView {
if grabberAreaFrame.contains(location) {
if surfaceView.grabberAreaContains(location) {
initialScrollOffset = scrollView.contentOffset
} else {
initialScrollOffset = scrollView.contentOffset
let offsetDiff = scrollView.contentOffset - contentOffsetForPinning(of: scrollView)
let pinningOffset = contentOffsetForPinning(of: scrollView)
// `scrollView.contentOffset` can be a value in [-30, 0) at this time by `allowScrollPanGesture(for:)`.
// Therefore the initial scroll offset must be reset to the pinning offset. Otherwise, the following
// `Fit the surface bounds` logic don't working and also the scroll content offset can be invalid.
initialScrollOffset = pinningOffset
// Fit the surface bounds to a scroll offset content by startInteraction(at:offset:)
let offsetDiff = scrollView.contentOffset - pinningOffset
switch layoutAdapter.position {
case .top, .left:
// Fit the surface bounds to a scroll offset content by startInteraction(at:offset:)
if value(of: offsetDiff) > 0 {
offset = -offsetDiff
}
case .bottom, .right:
// Fit the surface bounds to a scroll offset content by startInteraction(at:offset:)
if value(of: offsetDiff) < 0 {
offset = -offsetDiff
}
@@ -1002,6 +1005,22 @@ class Core: NSObject, UIGestureRecognizerDelegate {
// MARK: - ScrollView handling
func followScrollViewBouncing() {
guard let scrollView = scrollView else {
return
}
let contentOffset = scrollView.contentOffset.y
guard contentOffset < 0, layoutAdapter.position == .bottom, state == layoutAdapter.mostExpandedState else {
if surfaceView.transform != .identity {
surfaceView.transform = .identity
scrollView.transform = .identity
}
return
}
surfaceView.transform = CGAffineTransform(translationX: 0, y: -contentOffset)
scrollView.transform = CGAffineTransform(translationX: 0, y: contentOffset)
}
private func lockScrollView() {
guard let scrollView = scrollView else { return }
@@ -1011,11 +1030,11 @@ class Core: NSObject, UIGestureRecognizerDelegate {
}
log.debug("lock scroll view")
scrollBounce = scrollView.bounces
scrollIndictorVisible = scrollView.showsVerticalScrollIndicator
// Must not modify the UIScrollView.bounces property here. If you reset it to unlock the tracking scroll view,
// UIScrollView may unexpectedly alter the scroll offset when dealing with small scrollable content.
scrollView.isDirectionalLockEnabled = true
scrollView.bounces = false
scrollView.showsVerticalScrollIndicator = false
}
@@ -1024,7 +1043,6 @@ class Core: NSObject, UIGestureRecognizerDelegate {
log.debug("unlock scroll view")
scrollView.isDirectionalLockEnabled = false
scrollView.bounces = scrollBounce
scrollView.showsVerticalScrollIndicator = scrollIndictorVisible
}
+1 -1
View File
@@ -157,7 +157,7 @@ extension UIGestureRecognizer.State: CustomDebugStringConvertible {
extension UIScrollView {
var isLocked: Bool {
return !showsVerticalScrollIndicator && !bounces && isDirectionalLockEnabled
return !showsVerticalScrollIndicator && isDirectionalLockEnabled
}
var fp_contentInset: UIEdgeInsets {
if #available(iOS 11.0, *) {
+5 -3
View File
@@ -1,11 +1,12 @@
# ``FloatingPanel``
The new interface displays the related contents and utilities in parallel as a user wants.
Create a user interface to display the related content and utilities alongside the main content.
## Overview
FloatingPanel is a simple and easy-to-use UI component for a new interface introduced in Apple Maps, Shortcuts and Stocks app.
The new interface displays the related contents and utilities in parallel as a user wants.
FloatingPanel is a simple and easy-to-use UI component designed for a user interface featured in the Apple Maps, Shortcuts and Stocks app.
The user interface displays related content and utilities alongside the main content.
## Topics
@@ -44,6 +45,7 @@ The new interface displays the related contents and utilities in parallel as a u
- ``FloatingPanelPosition``
- ``FloatingPanelReferenceEdge``
- ``FloatingPanelLayoutReferenceGuide``
- ``FloatingPanelLayoutContentBoundingGuide``
### Behaviors
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.5.2</string>
<string>2.6.2</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
+37 -7
View File
@@ -92,6 +92,9 @@ class LayoutAdapter {
private var staticConstraint: NSLayoutConstraint?
/// A layout constraint to limit the content size in ``FloatingPanelAdaptiveLayoutAnchor``.
private var contentBoundingConstraint: NSLayoutConstraint?
private var anchorStates: Set<FloatingPanelState> {
return Set(layout.anchors.keys)
}
@@ -330,12 +333,27 @@ class LayoutAdapter {
if anchor.referenceGuide == .safeArea {
referenceBoundsLength += position.inset(safeAreaInsets)
}
return dimension - diff
let maxPosition: CGFloat = {
if let maxBounds = anchor.contentBoundingGuide.maxBounds(vc) {
return layout.position.mainLocation(maxBounds.origin)
+ layout.position.mainDimension(maxBounds.size)
} else {
return .infinity
}
}()
return min(dimension - diff, maxPosition)
case .bottom, .right:
if anchor.referenceGuide == .safeArea {
referenceBoundsLength -= position.inset(safeAreaInsets)
}
return referenceBoundsLength - dimension + diff
let minPosition: CGFloat = {
if let maxBounds = anchor.contentBoundingGuide.maxBounds(vc) {
return layout.position.mainLocation(maxBounds.origin)
} else {
return -(.infinity)
}
}()
return max(referenceBoundsLength - dimension + diff, minPosition)
}
case let anchor as FloatingPanelLayoutAnchor:
let referenceBounds = anchor.referenceGuide == .safeArea ? bounds.inset(by: safeAreaInsets) : bounds
@@ -629,8 +647,9 @@ class LayoutAdapter {
// The method is separated from prepareLayout(to:) for the rotation support
// It must be called in FloatingPanelController.traitCollectionDidChange(_:)
func updateStaticConstraint() {
NSLayoutConstraint.deactivate(constraint: staticConstraint)
NSLayoutConstraint.deactivate([staticConstraint, contentBoundingConstraint].compactMap{ $0 })
staticConstraint = nil
contentBoundingConstraint = nil
if vc.contentMode == .fitToBounds {
surfaceView.containerOverflow = 0
@@ -654,7 +673,18 @@ class LayoutAdapter {
constant = 0.0
}
let baseAnchor = position.mainDimensionAnchor(anchor.contentLayoutGuide)
staticConstraint = surfaceAnchor.constraint(equalTo: baseAnchor, constant: constant)
if let boundingLayoutGuide = anchor.contentBoundingGuide.layoutGuide(vc) {
if anchor.isAbsolute {
contentBoundingConstraint = baseAnchor.constraint(lessThanOrEqualTo: position.mainDimensionAnchor(boundingLayoutGuide),
constant: anchor.offset)
} else {
contentBoundingConstraint = baseAnchor.constraint(lessThanOrEqualTo: position.mainDimensionAnchor(boundingLayoutGuide),
multiplier: anchor.offset)
}
staticConstraint = surfaceAnchor.constraint(lessThanOrEqualTo: baseAnchor, constant: constant)
} else {
staticConstraint = surfaceAnchor.constraint(equalTo: baseAnchor, constant: constant)
}
default:
switch position {
case .top, .left:
@@ -673,12 +703,12 @@ class LayoutAdapter {
staticConstraint?.identifier = "FloatingPanel-static-width"
}
NSLayoutConstraint.activate(constraint: staticConstraint)
NSLayoutConstraint.activate([staticConstraint, contentBoundingConstraint].compactMap{ $0 })
surfaceView.containerOverflow = position.mainDimension(vc.view.bounds.size)
}
func updateInteractiveEdgeConstraint(diff: CGFloat, overflow: Bool, allowsRubberBanding: (UIRectEdge) -> Bool) {
func updateInteractiveEdgeConstraint(diff: CGFloat, scrollingContent: Bool, allowsRubberBanding: (UIRectEdge) -> Bool) {
defer {
log.debug("update surface location = \(surfaceLocation)")
}
@@ -701,7 +731,7 @@ class LayoutAdapter {
const = maxConst + rubberBandEffect(for: buffer, base: base)
}
if overflow == false {
if scrollingContent {
const = min(max(const, minConst), maxConst)
}
+66 -10
View File
@@ -15,6 +15,11 @@ import UIKit
///
/// The inset is an amount to inset a panel from an edge of the reference guide. The edge refers to a panel
/// positioning.
///
/// - Parameters:
/// - absoluteOffset: An absolute offset to attach the panel from the edge.
/// - edge: Specify the edge of ``FloatingPanelController``'s view. This is the staring point of the offset.
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
@objc public init(absoluteInset: CGFloat, edge: FloatingPanelReferenceEdge, referenceGuide: FloatingPanelLayoutReferenceGuide) {
self.inset = absoluteInset
self.referenceGuide = referenceGuide
@@ -27,6 +32,11 @@ import UIKit
/// The inset is an amount to inset a panel from the edge of the specified reference guide. The value is
/// a floating-point number in the range 0.0 to 1.0, where 0.0 represents zero distance from the edge and
/// 1.0 represents a distance to the opposite edge.
///
/// - Parameters:
/// - fractionalOffset: A fractional value of the size of ``FloatingPanelController``'s view to attach the panel from the edge.
/// - edge: Specify the edge of ``FloatingPanelController``'s view. This is the staring point of the offset.
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
@objc public init(fractionalInset: CGFloat, edge: FloatingPanelReferenceEdge, referenceGuide: FloatingPanelLayoutReferenceGuide) {
self.inset = fractionalInset
self.referenceGuide = referenceGuide
@@ -49,7 +59,7 @@ public extension FloatingPanelLayoutAnchor {
case .left:
return layoutConstraints(layoutGuide, for: vc.surfaceView.rightAnchor)
case .bottom:
return layoutConstraints(layoutGuide, for: vc.surfaceView.topAnchor)
return layoutConstraints(layoutGuide, for: vc.surfaceView.topAnchor)
case .right:
return layoutConstraints(layoutGuide, for: vc.surfaceView.leftAnchor)
}
@@ -101,6 +111,10 @@ public extension FloatingPanelLayoutAnchor {
///
/// The offset is an amount to offset a position of panel that displays the entire content from an edge of
/// the reference guide. The edge refers to a panel positioning.
///
/// - Parameters:
/// - absoluteOffset: An absolute offset from the content size in the main dimension(i.e. y axis for a bottom panel) to attach the panel.
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
@objc public init(absoluteOffset offset: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
self.offset = offset
self.referenceGuide = referenceGuide
@@ -111,6 +125,10 @@ public extension FloatingPanelLayoutAnchor {
///
/// The offset value is a floating-point number in the range 0.0 to 1.0, where 0.0 represents the full content
/// is displayed and 0.5 represents the half of content is displayed.
///
/// - Parameters:
/// - fractionalOffset: A fractional offset of the content size in the main dimension(i.e. y axis for a bottom panel) to attach the panel.
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
@objc public init(fractionalOffset offset: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
self.offset = offset
self.referenceGuide = referenceGuide
@@ -144,37 +162,72 @@ public extension FloatingPanelIntrinsicLayoutAnchor {
/// An object that defines how to settles a panel with a layout guide of a content view.
@objc final public class FloatingPanelAdaptiveLayoutAnchor: NSObject, FloatingPanelLayoutAnchoring /*, NSCopying */ {
/// Returns a layout anchor with the specified offset by an absolute value, layout guide to display content and reference guide for a panel.
/// Returns a layout anchor with the specified offset by an absolute value to display a panel with its intrinsic content size.
///
/// The offset is an amount to offset a position of panel that displays the entire content of the specified guide from an edge of
/// the reference guide. The edge refers to a panel positioning.
@objc public init(absoluteOffset offset: CGFloat, contentLayout: UILayoutGuide, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
///
/// ``contentBoundingGuide`` restricts the content size which a panel displays. For example, given ``referenceGuide`` is `.superview` and ``contentBoundingGuide`` is `.safeArea` for a bottom positioned panel, the panel content is laid out inside the superview of the view of FloatingPanelController(not its safe area), but its content size is limited to its safe area size. Normally both of ``referenceGuide`` and ``contentBoundingGuide`` are specified with the same rectangle area.
///
/// - Parameters:
/// - absoluteOffset: An absolute offset from the content size in the main dimension(i.e. y axis for a bottom panel) to attach the panel.
/// - contentLayout: The content layout guide to calculate the content size in the panel.
/// - referenceGuide: The rectangular area to lay out the content of a panel. If it's set to `.safeArea`, the panel content displays inside the safe area of its ``FloatingPanelController``'s view. This argument doesn't limit its content size.
/// - contentBoundingGuide: The rectangular area to restrict the content size of a panel in the main dimension(i.e. y axis is the main dimension for a bottom panel).
///
/// - Warning: If ``contentBoundingGuide`` is set to none, the panel may expand out of the screen size, depending on the intrinsic size of its content.
@objc public init(
absoluteOffset offset: CGFloat,
contentLayout: UILayoutGuide,
referenceGuide: FloatingPanelLayoutReferenceGuide,
contentBoundingGuide: FloatingPanelLayoutContentBoundingGuide = .none
) {
self.offset = offset
self.contentLayoutGuide = contentLayout
self.referenceGuide = referenceGuide
self.contentBoundingGuide = contentBoundingGuide
self.isAbsolute = true
}
/// Returns a layout anchor with the specified offset by a fractional value, layout guide to display content and reference guide for a panel.
/// Returns a layout anchor with the specified offset by a fractional value to display a panel with its intrinsic content size.
///
/// The offset value is a floating-point number in the range 0.0 to 1.0, where 0.0 represents the full content
/// is displayed and 0.5 represents the half of content is displayed.
@objc public init(fractionalOffset offset: CGFloat, contentLayout: UILayoutGuide, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
///
/// ``contentBoundingGuide`` restricts the content size which a panel displays. For example, given ``referenceGuide`` is `.superview` and ``contentBoundingGuide`` is `.safeArea` for a bottom positioned panel, the panel content is laid out inside the superview of the view of FloatingPanelController(not its safe area), but its content size is limited to its safe area size. Normally both of ``referenceGuide`` and ``contentBoundingGuide`` are specified with the same rectangle area.
///
/// - Parameters:
/// - fractionalOffset: A fractional offset of the content size in the main dimension(i.e. y axis for a bottom panel) to attach the panel.
/// - contentLayout: The content layout guide to calculate the content size in the panel.
/// - referenceGuide: The rectangular area to lay out the content of a panel. If it's set to `.safeArea`, the panel content displays inside the safe area of its ``FloatingPanelController``'s view. This argument doesn't limit its content size.
/// - contentBoundingGuide: The rectangular area to restrict the content size of a panel in the main dimension(i.e. y axis is the main dimension for a bottom panel).
///
/// - Warning: If ``contentBoundingGuide`` is set to none, the panel may expand out of the screen size, depending on the intrinsic size of its content.
@objc public init(
fractionalOffset offset: CGFloat,
contentLayout: UILayoutGuide,
referenceGuide: FloatingPanelLayoutReferenceGuide,
contentBoundingGuide: FloatingPanelLayoutContentBoundingGuide = .none
) {
self.offset = offset
self.contentLayoutGuide = contentLayout
self.referenceGuide = referenceGuide
self.contentBoundingGuide = contentBoundingGuide
self.isAbsolute = false
}
fileprivate let offset: CGFloat
fileprivate let isAbsolute: Bool
let offset: CGFloat
let isAbsolute: Bool
let contentLayoutGuide: UILayoutGuide
@objc public let referenceGuide: FloatingPanelLayoutReferenceGuide
@objc public let contentBoundingGuide: FloatingPanelLayoutContentBoundingGuide
}
public extension FloatingPanelAdaptiveLayoutAnchor {
func layoutConstraints(_ vc: FloatingPanelController, for position: FloatingPanelPosition) -> [NSLayoutConstraint] {
var constraints = [NSLayoutConstraint]()
let layoutGuide = referenceGuide.layoutGuide(vc: vc)
let offsetConstraint: NSLayoutConstraint
let offsetAnchor: NSLayoutDimension
switch position {
case .top:
@@ -187,10 +240,13 @@ public extension FloatingPanelAdaptiveLayoutAnchor {
offsetAnchor = vc.surfaceView.leftAnchor.anchorWithOffset(to: layoutGuide.rightAnchor)
}
if isAbsolute {
return [offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), constant: -offset)]
offsetConstraint = offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), constant: -offset)
} else {
return [offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), multiplier: (1 - offset))]
offsetConstraint = offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), multiplier: (1 - offset))
}
constraints.append(offsetConstraint)
return constraints
}
}
@@ -27,7 +27,7 @@ extension FloatingPanelReferenceEdge {
}
}
/// Constants that specify a layout guide to lay out a panel.
/// A representation to specify a rectangular area to lay out a panel.
@objc public enum FloatingPanelLayoutReferenceGuide: Int {
case superview = 0
case safeArea = 1
@@ -43,3 +43,34 @@ extension FloatingPanelLayoutReferenceGuide {
}
}
}
/// A representation to specify a bounding box which limit the content size of a panel.
@objc public enum FloatingPanelLayoutContentBoundingGuide: Int {
case none = 0
case superview = 1
case safeArea = 2
}
extension FloatingPanelLayoutContentBoundingGuide {
func layoutGuide(_ fpc: FloatingPanelController) -> LayoutGuideProvider? {
switch self {
case .superview:
return fpc.view
case .safeArea:
return fpc.fp_safeAreaLayoutGuide
case .none:
return nil
}
}
func maxBounds(_ fpc: FloatingPanelController) -> CGRect? {
switch self {
case .superview:
return fpc.view.bounds
case .safeArea:
return fpc.view.bounds.inset(by: fpc.fp_safeAreaInsets)
case .none:
return nil
}
}
}
+24 -1
View File
@@ -54,7 +54,12 @@ public class SurfaceAppearance: NSObject {
///
/// Defaults to `.circular`.
@available(iOS 13.0, *)
public lazy var cornerCurve: CALayerCornerCurve = .circular
public var cornerCurve: CALayerCornerCurve {
get { _cornerCurve ?? .circular }
set { _cornerCurve = newValue }
}
private var _cornerCurve: CALayerCornerCurve?
/// An array of shadows used to create drop shadows underneath a surface view.
public var shadows: [Shadow] = [Shadow()]
@@ -442,4 +447,22 @@ public class SurfaceView: UIView {
func hasStackView() -> Bool {
return contentView?.subviews.reduce(false) { $0 || ($1 is UIStackView) } ?? false
}
func grabberAreaContains(_ location: CGPoint) -> Bool {
// Sometimes a dragging finger's location is out of surface frame.
let cappedLocation: CGPoint
// Because the maximum width / height is out of bounds in CGRect.contains(_:)
let adjustment = 1 / fp_displayScale
switch position {
case .top:
cappedLocation = CGPoint(x: location.x, y: min(location.y, bounds.height - adjustment))
case .left:
cappedLocation = CGPoint(x: min(location.x, bounds.width - adjustment), y: location.y)
case .bottom:
cappedLocation = CGPoint(x: location.x, y: max(location.y, 0))
case .right:
cappedLocation = CGPoint(x: max(location.x, 0), y: location.y)
}
return grabberAreaFrame.contains(cappedLocation)
}
}
+4 -1
View File
@@ -104,7 +104,10 @@ class ModalPresentTransition: NSObject, UIViewControllerAnimatedTransitioning {
fpc?.suspendTransitionAnimator(false)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
return fpc.transitionAnimator!
guard let transitionAnimator = fpc.transitionAnimator else {
fatalError("The panel state must be `hidden` but it is `\(fpc.state)`")
}
return transitionAnimator
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
+7 -5
View File
@@ -22,7 +22,11 @@ class ControllerTests: XCTestCase {
}
func test_addPanel() {
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else { fatalError() }
let rootVC = UIViewController()
rootVC.loadViewIfNeeded()
rootVC.view.bounds = .init(origin: .zero, size: .init(width: 390, height: 844))
let fpc = FloatingPanelController()
fpc.addPanel(toParent: rootVC)
XCTAssertEqual(fpc.surfaceLocation.y, fpc.surfaceLocation(for: .half).y)
@@ -336,8 +340,7 @@ private class MyZombieViewController: UIViewController, FloatingPanelLayout, Flo
return .half
}
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: UIScreen.main.bounds.height == 667.0 ? 18.0 : 16.0,
edge: .top,
referenceGuide: .superview),
@@ -347,6 +350,5 @@ private class MyZombieViewController: UIViewController, FloatingPanelLayout, Flo
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0,
edge: .bottom,
referenceGuide: .superview),
]
}
]
}
+91 -52
View File
@@ -12,27 +12,22 @@ class CoreTests: XCTestCase {
let contentVC1 = UITableViewController(nibName: nil, bundle: nil)
XCTAssertEqual(contentVC1.tableView.showsVerticalScrollIndicator, true)
XCTAssertEqual(contentVC1.tableView.bounces, true)
fpc.set(contentViewController: contentVC1)
fpc.track(scrollView: contentVC1.tableView)
fpc.showForTest()
XCTAssertEqual(fpc.state, .half)
XCTAssertEqual(contentVC1.tableView.showsVerticalScrollIndicator, false)
XCTAssertEqual(contentVC1.tableView.bounces, false)
fpc.move(to: .full, animated: false)
XCTAssertEqual(contentVC1.tableView.showsVerticalScrollIndicator, true)
XCTAssertEqual(contentVC1.tableView.bounces, true)
fpc.move(to: .tip, animated: false)
XCTAssertEqual(contentVC1.tableView.showsVerticalScrollIndicator, false)
XCTAssertEqual(contentVC1.tableView.bounces, false)
let exp1 = expectation(description: "move to full with animation")
fpc.move(to: .full, animated: true) {
XCTAssertEqual(contentVC1.tableView.showsVerticalScrollIndicator, true)
XCTAssertEqual(contentVC1.tableView.bounces, true)
exp1.fulfill()
}
wait(for: [exp1], timeout: 1.0)
@@ -40,7 +35,6 @@ class CoreTests: XCTestCase {
let exp2 = expectation(description: "move to tip with animation")
fpc.move(to: .tip, animated: false) {
XCTAssertEqual(contentVC1.tableView.showsVerticalScrollIndicator, false)
XCTAssertEqual(contentVC1.tableView.bounces, false)
exp2.fulfill()
}
wait(for: [exp2], timeout: 1.0)
@@ -48,22 +42,19 @@ class CoreTests: XCTestCase {
// Reset the content vc
let contentVC2 = UITableViewController(nibName: nil, bundle: nil)
XCTAssertEqual(contentVC2.tableView.showsVerticalScrollIndicator, true)
XCTAssertEqual(contentVC2.tableView.bounces, true)
fpc.set(contentViewController: contentVC2)
fpc.track(scrollView: contentVC2.tableView)
fpc.show(animated: false, completion: nil)
XCTAssertEqual(fpc.state, .half)
XCTAssertEqual(contentVC2.tableView.showsVerticalScrollIndicator, false)
XCTAssertEqual(contentVC2.tableView.bounces, false)
}
func test_getBackdropAlpha_1positions() {
class FloatingPanelLayout1Positions: FloatingPanelLayout {
let initialState: FloatingPanelState = .full
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] =
[.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
}
let delegate = FloatingPanelTestDelegate()
@@ -106,12 +97,10 @@ class CoreTests: XCTestCase {
class FloatingPanelLayout2Positions: FloatingPanelLayout {
let initialState: FloatingPanelState = .half
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
]
}
let delegate = FloatingPanelTestDelegate()
@@ -198,26 +187,41 @@ class CoreTests: XCTestCase {
}
}
}
let fpc = FloatingPanelController()
class BackdropTestLayout2: FloatingPanelTestLayout {
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.0
}
}
class TestDelegate: FloatingPanelControllerDelegate {
var layout: FloatingPanelLayout = BackdropTestLayout2()
func floatingPanel(_ fpc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout { layout }
func floatingPanel(_ fpc: FloatingPanelController, layoutFor size: CGSize) -> FloatingPanelLayout { layout }
}
func _floor(_ alpha: CGFloat) -> CGFloat {
return floor(fpc.backdropView.alpha * 1e+06) / 1e+06
}
let delegate = TestDelegate()
let fpc = FloatingPanelController(delegate: delegate)
fpc.layout = BackdropTestLayout()
fpc.showForTest()
fpc.move(to: .full, animated: false)
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
fpc.move(to: .half, animated: false)
XCTAssertEqual(fpc.backdropView.alpha, 0.0)
fpc.move(to: .tip, animated: false)
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
let exp1 = expectation(description: "move to full with animation")
fpc.move(to: .full, animated: true) {
exp1.fulfill()
}
wait(for: [exp1], timeout: 1.0)
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
let exp2 = expectation(description: "move to half with animation")
fpc.move(to: .half, animated: true) {
@@ -226,23 +230,66 @@ class CoreTests: XCTestCase {
wait(for: [exp2], timeout: 1.0)
XCTAssertEqual(fpc.backdropView.alpha, 0.0)
// Test a content mode change of FloatingPanelController
let exp3 = expectation(description: "move to tip with animation")
fpc.move(to: .tip, animated: true) {
exp3.fulfill()
}
fpc.contentMode = .fitToBounds
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must not affect the backdrop alpha by changing the content mode
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must not affect the backdrop alpha by changing the content mode
wait(for: [exp3], timeout: 1.0)
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
// Test a size class change of FloatingPanelController.view
fpc.move(to: .full, animated: false)
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
fpc.willTransition(to: UITraitCollection(horizontalSizeClass: .regular), with: MockTransitionCoordinator())
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must update the alpha by BackdropTestLayout2 in TestDelegate.
// Test a view size change of FloatingPanelController.view
fpc.move(to: .full, animated: false)
delegate.layout = BackdropTestLayout()
fpc.invalidateLayout()
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
delegate.layout = BackdropTestLayout2()
fpc.viewWillTransition(to: CGSize.zero, with: MockTransitionCoordinator())
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must update the alpha by BackdropTestLayout2 in TestDelegate.
}
func test_initial_surface_position() {
class FloatingPanelTestDelegate: FloatingPanelControllerDelegate {
class Layout: FloatingPanelLayout {
let initialState: FloatingPanelState = .full
let position: FloatingPanelPosition = .top
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring]
= [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .bottom, referenceGuide: .superview)]
}
func floatingPanel(_ fpc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
Layout()
}
}
do {
let delegate = FloatingPanelTestDelegate()
let fpc = FloatingPanelController(delegate: delegate)
XCTAssertEqual(fpc.surfaceView.position, .top)
}
do {
let fpc = FloatingPanelController()
fpc.layout = FloatingPanelTestDelegate.Layout()
XCTAssertEqual(fpc.surfaceView.position, .top)
}
}
func test_targetPosition_1positions() {
class FloatingPanelLayout1Positions: FloatingPanelLayout {
let initialState: FloatingPanelState = .full
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring]
= [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
}
let delegate = FloatingPanelTestDelegate()
@@ -269,12 +316,10 @@ class CoreTests: XCTestCase {
class FloatingPanelLayout2Positions: FloatingPanelLayout {
let initialState: FloatingPanelState = .half
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
]
}
let delegate = FloatingPanelTestDelegate()
@@ -326,12 +371,10 @@ class CoreTests: XCTestCase {
class FloatingPanelLayout2Positions: FloatingPanelLayout {
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let delegate = FloatingPanelTestDelegate()
@@ -689,13 +732,11 @@ class CoreTests: XCTestCase {
class FloatingPanelLayout3PositionsWithHidden: FloatingPanelLayout {
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let delegate = FloatingPanelTestDelegate()
let fpc = FloatingPanelController(delegate: delegate)
@@ -721,13 +762,11 @@ class CoreTests: XCTestCase {
class FloatingPanelLayout3Positions: FloatingPanelLayout {
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let delegate = FloatingPanelTestDelegate()
+40 -50
View File
@@ -17,23 +17,19 @@ class LayoutTests: XCTestCase {
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.leastExpandedState, .tip)
class FloatingPanelLayoutWithHidden: FloatingPanelLayout {
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview)
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview)
]
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .bottom
}
class FloatingPanelLayout2Positions: FloatingPanelLayout {
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
]
let initialState: FloatingPanelState = .tip
let position: FloatingPanelPosition = .bottom
}
@@ -138,31 +134,31 @@ class LayoutTests: XCTestCase {
var next: CGFloat
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos + 100.0)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, tipPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, tipPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, tipPos + 100.0)
@@ -190,31 +186,31 @@ class LayoutTests: XCTestCase {
var next: CGFloat
pre = fpc.surfaceLocation.y
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, pre)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, tipPos + 100.0)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos + 100,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos + 100,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos + 100.0)
@@ -224,12 +220,10 @@ class LayoutTests: XCTestCase {
func test_updateInteractiveEdgeConstraintWithHidden() {
class FloatingPanelLayout2Positions: FloatingPanelLayout {
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .bottom
}
@@ -245,19 +239,19 @@ class LayoutTests: XCTestCase {
var next: CGFloat
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos - 100.0)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: hiddenPos - fullPos + 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, hiddenPos + 100.0)
@@ -267,12 +261,10 @@ class LayoutTests: XCTestCase {
func test_updateInteractiveEdgeConstraintWithHidden_bottomEdge() {
class FloatingPanelLayout2Positions: FloatingPanelLayout {
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
[
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .top, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .top, referenceGuide: .superview),
]
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .top
}
@@ -288,19 +280,19 @@ class LayoutTests: XCTestCase {
var next: CGFloat
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos + 100.0)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: hiddenPos - fullPos + 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, hiddenPos + 100.0)
@@ -310,12 +302,10 @@ class LayoutTests: XCTestCase {
func test_updateInteractiveTopConstraintWithMinusInsets() {
class FloatingPanelLayoutMinusInsets: FloatingPanelLayout {
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
[
.full: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .bottom, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .bottom, referenceGuide: .safeArea),
]
let initialState: FloatingPanelState = .full
let position: FloatingPanelPosition = .bottom
}
@@ -328,19 +318,19 @@ class LayoutTests: XCTestCase {
var next: CGFloat
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos - 100)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, tipPos + 100)
+84
View File
@@ -228,4 +228,88 @@ class SurfaceViewTests: XCTestCase {
XCTAssert(surface.containerView.layer.borderColor == UIColor.red.cgColor)
XCTAssert(surface.containerView.layer.borderWidth == 3.0)
}
func test_surfaceView_grabberArea() {
let sv = SurfaceView()
sv.bounds = .init(x: 0, y: 0, width: 375, height: 500)
sv.grabberAreaOffset = 44.0
// Top
do {
sv.position = .top
XCTAssertEqual(sv.grabberAreaFrame,
.init(x: 0,
y: sv.bounds.height - sv.grabberAreaOffset,
width: sv.bounds.width,
height: sv.grabberAreaOffset))
}
// Bottom
do {
sv.position = .bottom
XCTAssertEqual(sv.grabberAreaFrame,
.init(x: 0,
y: 0,
width: sv.bounds.width,
height: sv.grabberAreaOffset))
}
// Left
do {
sv.position = .left
XCTAssertEqual(sv.grabberAreaFrame,
.init(x: sv.bounds.width - sv.grabberAreaOffset,
y: 0,
width: sv.grabberAreaOffset,
height: sv.bounds.height))
}
// Right
do {
sv.position = .right
XCTAssertEqual(sv.grabberAreaFrame,
.init(x: 0,
y: 0,
width: sv.grabberAreaOffset,
height: sv.bounds.height))
}
}
func test_surfaceView_grabberAreaDetection() {
let sv = SurfaceView()
sv.bounds = .init(x: 0, y: 0, width: 375, height: 500)
// Top
do {
sv.position = .top
XCTAssertTrue(sv.grabberAreaContains(.init(x: 0, y: sv.bounds.height)))
XCTAssertTrue(sv.grabberAreaContains(.init(x: sv.bounds.width / 2, y: sv.bounds.height)))
XCTAssertTrue(sv.grabberAreaContains(.init(x: sv.bounds.width / 2, y: sv.bounds.height + 2)))
XCTAssertFalse(sv.grabberAreaContains(.init(x: -2, y: sv.bounds.height)))
XCTAssertFalse(sv.grabberAreaContains(.init(x: -2, y: -2)))
}
// Bottom
do {
sv.position = .bottom
XCTAssertTrue(sv.grabberAreaContains(.init(x: 0, y: 0)))
XCTAssertTrue(sv.grabberAreaContains(.init(x: sv.bounds.width / 2, y: 0)))
XCTAssertTrue(sv.grabberAreaContains(.init(x: sv.bounds.width / 2, y: -2)))
XCTAssertFalse(sv.grabberAreaContains(.init(x: -2, y: 0)))
XCTAssertFalse(sv.grabberAreaContains(.init(x: -2, y: -2)))
}
// Left
do {
sv.position = .left
XCTAssertTrue(sv.grabberAreaContains(.init(x: sv.bounds.width, y: 0)))
XCTAssertTrue(sv.grabberAreaContains(.init(x: sv.bounds.width, y: sv.bounds.height / 2)))
XCTAssertTrue(sv.grabberAreaContains(.init(x: sv.bounds.width + 2, y: sv.bounds.height / 2)))
XCTAssertFalse(sv.grabberAreaContains(.init(x: sv.bounds.width, y: -2)))
XCTAssertFalse(sv.grabberAreaContains(.init(x: -2, y: -2)))
}
// Right
do {
sv.position = .right
XCTAssertTrue(sv.grabberAreaContains(.init(x: 0, y: 0)))
XCTAssertTrue(sv.grabberAreaContains(.init(x: 0, y: sv.bounds.height / 2)))
XCTAssertTrue(sv.grabberAreaContains(.init(x: -2, y: sv.bounds.height / 2)))
XCTAssertFalse(sv.grabberAreaContains(.init(x: 0, y: -2)))
XCTAssertFalse(sv.grabberAreaContains(.init(x: -2, y: -2)))
}
}
}
+22
View File
@@ -77,3 +77,25 @@ class FloatingPanelProjectableBehavior: FloatingPanelBehavior {
return true
}
}
class MockTransitionCoordinator: NSObject, UIViewControllerTransitionCoordinator {
func animate(alongsideTransition animation: ((UIViewControllerTransitionCoordinatorContext) -> Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Void)? = nil) -> Bool { true }
func animateAlongsideTransition(in view: UIView?, animation: ((UIViewControllerTransitionCoordinatorContext) -> Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Void)? = nil) -> Bool { true }
func notifyWhenInteractionEnds(_ handler: @escaping (UIViewControllerTransitionCoordinatorContext) -> Void) {}
func notifyWhenInteractionChanges(_ handler: @escaping (UIViewControllerTransitionCoordinatorContext) -> Void) {}
var isAnimated: Bool = false
var presentationStyle: UIModalPresentationStyle = .fullScreen
var initiallyInteractive: Bool = false
var isInterruptible: Bool = false
var isInteractive: Bool = false
var isCancelled: Bool = false
var transitionDuration: TimeInterval = 0.25
var percentComplete: CGFloat = 0
var completionVelocity: CGFloat = 0
var completionCurve: UIView.AnimationCurve = .easeInOut
func viewController(forKey key: UITransitionContextViewControllerKey) -> UIViewController? { nil }
func view(forKey key: UITransitionContextViewKey) -> UIView? { nil }
var containerView: UIView { UIView() }
var targetTransform: CGAffineTransform = .identity
}
-19
View File
@@ -1,19 +0,0 @@
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
let rootVC = UIViewController(nibName: nil, bundle: nil)
rootVC.view.backgroundColor = .gray
let window = UIWindow()
window.rootViewController = rootVC
window.makeKeyAndVisible()
self.window = window
return true
}
}
-43
View File
@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
-48
View File
@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Copyright © 2019 scenee. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd">
<rect key="frame" x="0.0" y="626.5" width="375" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="TestingApp" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
<rect key="frame" x="0.0" y="202" width="375" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="obG-Y5-kRd" secondAttribute="centerX" id="5cz-MP-9tL"/>
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
<constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" constant="20" symbolic="YES" id="SfN-ll-jLj"/>
<constraint firstAttribute="bottom" secondItem="obG-Y5-kRd" secondAttribute="bottom" constant="20" id="Y44-ml-fuU"/>
<constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
<constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" constant="20" symbolic="YES" id="x7j-FC-K8j"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>