Compare commits
185
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ab6007892 | ||
|
|
b6e8928b1a | ||
|
|
3a3d53424c | ||
|
|
71f419a3cd | ||
|
|
0e27410460 | ||
|
|
29185a47bd | ||
|
|
6821b26706 | ||
|
|
5bdbe0f0ea | ||
|
|
afe2a9bced | ||
|
|
c0d88af234 | ||
|
|
3b4c1bd51c | ||
|
|
3c9f556533 | ||
|
|
7f1a74825d | ||
|
|
22d46c5260 | ||
|
|
466aaf21dd | ||
|
|
56e71ac580 | ||
|
|
f45b6aaa3f | ||
|
|
e473c3c440 | ||
|
|
8f2be39bf4 | ||
|
|
92c10830ff | ||
|
|
dcb89f58c3 | ||
|
|
d39c4b54d1 | ||
|
|
504182ceae | ||
|
|
bc1cfe444b | ||
|
|
0e0f773df7 | ||
|
|
be2be99537 | ||
|
|
afcf1ced36 | ||
|
|
5b33d3d5ff | ||
|
|
dbef6a691a | ||
|
|
dd238884bf | ||
|
|
046ed3df5b | ||
|
|
4996ce1a84 | ||
|
|
b8f7ff825d | ||
|
|
6fb9a9b3a2 | ||
|
|
34ebb3bf19 | ||
|
|
dbf665526d | ||
|
|
6c7f529eff | ||
|
|
c414d3a2a6 | ||
|
|
8eba647d75 | ||
|
|
c3568067b7 | ||
|
|
72580f089d | ||
|
|
c508ec892d | ||
|
|
ff2d4a48f1 | ||
|
|
62364eb6d5 | ||
|
|
ce5469a69d | ||
|
|
93c31fd71d | ||
|
|
461f637818 | ||
|
|
6b3b18b8ed | ||
|
|
b5ca468397 | ||
|
|
80956bfac6 | ||
|
|
5d382c440f | ||
|
|
d28c939a4c | ||
|
|
8bd02145cf | ||
|
|
debeca1fb2 | ||
|
|
9da54f9fc1 | ||
|
|
a13d053867 | ||
|
|
eda7201fe8 | ||
|
|
b1edef49a4 | ||
|
|
a2db94a8c4 | ||
|
|
57495cff84 | ||
|
|
eff5cde844 | ||
|
|
c365eadf1e | ||
|
|
5d02681b05 | ||
|
|
6e17ff734a | ||
|
|
02ed923e7b | ||
|
|
421335d98c | ||
|
|
2618f49556 | ||
|
|
328116600f | ||
|
|
a10b1426cd | ||
|
|
5468856a93 | ||
|
|
8f3a7de321 | ||
|
|
2de1fb9ac8 | ||
|
|
27a2d81a71 | ||
|
|
85ed3a6ce3 | ||
|
|
b34f1093de | ||
|
|
de1dbe70de | ||
|
|
c593c646ca | ||
|
|
0cb5307a61 | ||
|
|
8ab3b7986c | ||
|
|
bbdf3e7c6f | ||
|
|
bdb756b665 | ||
|
|
6611ec83a2 | ||
|
|
a917d6a626 | ||
|
|
7511ce577d | ||
|
|
e7d0a72440 | ||
|
|
44923ef66e | ||
|
|
2760bc7298 | ||
|
|
d428e96b03 | ||
|
|
67495961e5 | ||
|
|
7b88703e43 | ||
|
|
c40e66ef3d | ||
|
|
53fb131d0e | ||
|
|
37969f6cb3 | ||
|
|
a0ba3af012 | ||
|
|
6082dba6a7 | ||
|
|
43327e8123 | ||
|
|
0a5bc19d0f | ||
|
|
dd52e1eaee | ||
|
|
22aa055843 | ||
|
|
448fc5cbb4 | ||
|
|
b4fe3b408c | ||
|
|
6ecc7924ba | ||
|
|
0b82902233 | ||
|
|
597dbd4145 | ||
|
|
1ee949ff1b | ||
|
|
2a29cb5b3e | ||
|
|
208ab665da | ||
|
|
3e20314cfa | ||
|
|
5b8e9a54d9 | ||
|
|
d3c30b35d9 | ||
|
|
8bb3795931 | ||
|
|
9383cd001d | ||
|
|
6d5f770066 | ||
|
|
ce2cafed5b | ||
|
|
68352218ac | ||
|
|
e3736e4214 | ||
|
|
d6bbf92339 | ||
|
|
0e833aee3c | ||
|
|
5949bc88ea | ||
|
|
30bd261a10 | ||
|
|
f92279484f | ||
|
|
2205d1186d | ||
|
|
cd0948a9a4 | ||
|
|
6589aff4ac | ||
|
|
d34c16b1a5 | ||
|
|
05813253f9 | ||
|
|
4ab7d26030 | ||
|
|
162545d7e9 | ||
|
|
4904ea19cb | ||
|
|
b941f91556 | ||
|
|
f917316135 | ||
|
|
6235a19588 | ||
|
|
c47fc3d1d5 | ||
|
|
e6d285c6df | ||
|
|
a26a6beab2 | ||
|
|
b00a05b9ed | ||
|
|
bfbb7ad004 | ||
|
|
4dd52b1790 | ||
|
|
aef914e0fd | ||
|
|
2ef35f58a3 | ||
|
|
8aaf8b7b25 | ||
|
|
c330b59a9f | ||
|
|
639e4221e8 | ||
|
|
1ff3c28de8 | ||
|
|
89092676e3 | ||
|
|
bacc871dd2 | ||
|
|
0bc9812c23 | ||
|
|
a4d98971b3 | ||
|
|
1b0ec64489 | ||
|
|
345e894007 | ||
|
|
1fb1708df9 | ||
|
|
b9edbf766e | ||
|
|
fe30e60235 | ||
|
|
6e7c31110f | ||
|
|
e39f634d34 | ||
|
|
5851fd77c4 | ||
|
|
0fbbbc8d99 | ||
|
|
69078366de | ||
|
|
084b589a10 | ||
|
|
07ae324586 | ||
|
|
819c87c530 | ||
|
|
3bb9c4fd0f | ||
|
|
910a304849 | ||
|
|
2ecdbbae52 | ||
|
|
fe2e3173b8 | ||
|
|
21c6e3c715 | ||
|
|
29beea0ff9 | ||
|
|
1e16d9c1fb | ||
|
|
31d7a2a301 | ||
|
|
5f1d49b0bd | ||
|
|
ee66f218ca | ||
|
|
3a0831a334 | ||
|
|
add2f9ba4f | ||
|
|
ca6a8d5c16 | ||
|
|
84eba1aefa | ||
|
|
289ea4c971 | ||
|
|
821b03376c | ||
|
|
19fe1cffef | ||
|
|
b8fcb50874 | ||
|
|
9c45c31190 | ||
|
|
895790f697 | ||
|
|
43c7e8c2a0 | ||
|
|
904a66115c | ||
|
|
d0932e8e37 | ||
|
|
22009013eb |
@@ -0,0 +1,14 @@
|
|||||||
|
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'
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
test:
|
||||||
|
jobs:
|
||||||
|
- test-ios14_5-iPhone_12_Pro
|
||||||
+131
-48
@@ -4,77 +4,160 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- next
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: macOS-10.15
|
runs-on: ${{ matrix.runs-on }}
|
||||||
|
env:
|
||||||
|
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- swift: "5.10"
|
||||||
|
xcode: "15.4"
|
||||||
|
runs-on: macos-14
|
||||||
|
- swift: "5.9"
|
||||||
|
xcode: "15.2"
|
||||||
|
runs-on: macos-13
|
||||||
|
- swift: "5.8"
|
||||||
|
xcode: "14.3.1"
|
||||||
|
runs-on: macos-13
|
||||||
|
- swift: "5.7"
|
||||||
|
xcode: "14.1"
|
||||||
|
runs-on: macos-12
|
||||||
|
- swift: "5.6"
|
||||||
|
xcode: "13.4.1"
|
||||||
|
runs-on: macos-12
|
||||||
|
- swift: "5.5"
|
||||||
|
xcode: "13.2.1"
|
||||||
|
runs-on: macos-12
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: "Swift 5.1"
|
- name: Building in Swift ${{ matrix.swift }}
|
||||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.1 clean build
|
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=${{ matrix.swift }} clean build
|
||||||
env:
|
|
||||||
DEVELOPER_DIR: /Applications/Xcode_11.3.1.app/Contents/Developer
|
|
||||||
- name: "Swift 5.2"
|
|
||||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.2 clean build
|
|
||||||
env:
|
|
||||||
DEVELOPER_DIR: /Applications/Xcode_11.7.app/Contents/Developer
|
|
||||||
- name: "Swift 5.3"
|
|
||||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.3 clean build
|
|
||||||
env:
|
|
||||||
DEVELOPER_DIR: /Applications/Xcode_12.3.app/Contents/Developer
|
|
||||||
|
|
||||||
testing:
|
test:
|
||||||
runs-on: macOS-10.15
|
runs-on: ${{ matrix.runs-on }}
|
||||||
|
env:
|
||||||
|
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: "17.5"
|
||||||
|
xcode: "15.4"
|
||||||
|
sim: "iPhone 15 Pro"
|
||||||
|
parallel: NO # Stop random test job failures
|
||||||
|
runs-on: macos-14
|
||||||
|
- os: "16.4"
|
||||||
|
xcode: "14.3.1"
|
||||||
|
sim: "iPhone 14 Pro"
|
||||||
|
parallel: NO # Stop random test job failures
|
||||||
|
runs-on: macos-13
|
||||||
|
- os: "15.5"
|
||||||
|
xcode: "13.4.1"
|
||||||
|
sim: "iPhone 13 Pro"
|
||||||
|
parallel: NO # Stop random test job failures
|
||||||
|
runs-on: macos-12
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: "Testing in iOS 13.7"
|
- name: Testing in iOS ${{ matrix.os }}
|
||||||
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=13.7,name=iPhone 11 Pro'
|
run: |
|
||||||
env:
|
xcodebuild clean test \
|
||||||
DEVELOPER_DIR: /Applications/Xcode_11.7.app/Contents/Developer
|
-workspace FloatingPanel.xcworkspace \
|
||||||
- name: "Testing in iOS 14.3"
|
-scheme FloatingPanel \
|
||||||
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=14.3,name=iPhone 12 Pro'
|
-destination 'platform=iOS Simulator,OS=${{ matrix.os }},name=${{ matrix.sim }}' \
|
||||||
env:
|
-parallel-testing-enabled '${{ matrix.parallel }}'
|
||||||
DEVELOPER_DIR: /Applications/Xcode_12.3.app/Contents/Developer
|
timeout-minutes: 20
|
||||||
|
|
||||||
example:
|
example:
|
||||||
runs-on: macOS-10.15
|
runs-on: macos-14
|
||||||
env:
|
env:
|
||||||
DEVELOPER_DIR: /Applications/Xcode_12.3.app/Contents/Developer
|
DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- example: "Maps"
|
||||||
|
- example: "Maps-SwiftUI"
|
||||||
|
- example: "Stocks"
|
||||||
|
- example: "Samples"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: "Build Maps"
|
- name: Building ${{ matrix.example }}
|
||||||
run: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Maps -sdk iphonesimulator clean build
|
run: |
|
||||||
- name: "Build Stocks"
|
xcodebuild clean build \
|
||||||
run: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Stocks -sdk iphonesimulator clean build
|
-workspace FloatingPanel.xcworkspace \
|
||||||
- name: "Build Samples"
|
-scheme ${{ matrix.example }} \
|
||||||
run: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Samples -sdk iphonesimulator clean build
|
-sdk iphonesimulator
|
||||||
|
|
||||||
swiftpm:
|
swiftpm:
|
||||||
runs-on: macOS-10.15
|
runs-on: macos-14
|
||||||
env:
|
env:
|
||||||
DEVELOPER_DIR: /Applications/Xcode_12.3.app/Contents/Developer
|
DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
platform: [iphoneos, iphonesimulator]
|
||||||
|
arch: [x86_64, arm64]
|
||||||
|
exclude:
|
||||||
|
- platform: iphoneos
|
||||||
|
arch: x86_64
|
||||||
|
include:
|
||||||
|
# 17.2
|
||||||
|
- platform: iphoneos
|
||||||
|
sys: "ios17.2"
|
||||||
|
- platform: iphonesimulator
|
||||||
|
sys: "ios17.2-simulator"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: "Swift Package build"
|
- name: "Swift Package Manager build"
|
||||||
run: swift build -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" -Xswiftc "-target" -Xswiftc "x86_64-apple-ios14.3-simulator"
|
run: |
|
||||||
|
xcrun swift build \
|
||||||
|
--sdk "$(xcrun --sdk ${{ matrix.platform }} --show-sdk-path)" \
|
||||||
|
-Xswiftc "-target" -Xswiftc "${{ matrix.arch }}-apple-${{ matrix.sys }}"
|
||||||
|
|
||||||
carthage:
|
swiftpm_old:
|
||||||
runs-on: macOS-10.15
|
runs-on: ${{ matrix.runs-on }}
|
||||||
env:
|
env:
|
||||||
# Carthage doesn't fix issues on Xcode 12: https://github.com/Carthage/Carthage/releases/tag/0.36.0
|
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||||
DEVELOPER_DIR: /Applications/Xcode_11.7.app/Contents/Developer
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
# 16.4
|
||||||
|
- target: "x86_64-apple-ios16.4-simulator"
|
||||||
|
xcode: "14.3.1"
|
||||||
|
runs-on: macos-13
|
||||||
|
- target: "arm64-apple-ios16.4-simulator"
|
||||||
|
xcode: "14.3.1"
|
||||||
|
runs-on: macos-13
|
||||||
|
# 15.7
|
||||||
|
- target: "x86_64-apple-ios15.7-simulator"
|
||||||
|
xcode: "14.1"
|
||||||
|
runs-on: macos-12
|
||||||
|
- target: "arm64-apple-ios15.7-simulator"
|
||||||
|
xcode: "14.1"
|
||||||
|
runs-on: macos-12
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: "Carthage build"
|
- name: "Swift Package Manager build"
|
||||||
run: carthage build --no-skip-current
|
run: |
|
||||||
|
swift build \
|
||||||
|
-Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" \
|
||||||
|
-Xswiftc "-target" -Xswiftc "${{ matrix.target }}"
|
||||||
|
|
||||||
cocoapods:
|
cocoapods:
|
||||||
runs-on: macOS-10.15
|
runs-on: macos-14
|
||||||
|
env:
|
||||||
|
DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: "CocoaPods: pod lib lint"
|
- name: "CocoaPods: pod lib lint"
|
||||||
run: pod lib lint --allow-warnings
|
run: pod lib lint --allow-warnings
|
||||||
- name: "CocoaPods: pod spec lint"
|
- name: "CocoaPods: pod spec lint"
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
version: 1
|
||||||
|
builder:
|
||||||
|
configs:
|
||||||
|
- documentation_targets: [FloatingPanel]
|
||||||
|
platform: ios
|
||||||
@@ -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
|
|
||||||
|
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
language: objective-c
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- LANG=en_US.UTF-8
|
|
||||||
- LC_ALL=en_US.UTF-8
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
- stage: "Tests"
|
|
||||||
script: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=10.3.1,name=iPhone SE (1st generation)'
|
|
||||||
osx_image: xcode11.6
|
|
||||||
name: "iPhone SE (iOS 10.3)"
|
|
||||||
- script: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=11.4,name=iPhone 7'
|
|
||||||
osx_image: xcode11.6
|
|
||||||
name: "iPhone 7 (iOS 11.4)"
|
|
||||||
- script: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=12.4,name=iPhone X'
|
|
||||||
osx_image: xcode11.6
|
|
||||||
name: "iPhone X (iOS 12.4)"
|
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 55;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
6467E8642699AC5F00565F4F /* SurfaceAppearance+phone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6467E8632699AC5F00565F4F /* SurfaceAppearance+phone.swift */; };
|
||||||
|
6467E86A2699B19D00565F4F /* SearchPanelPhoneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6467E8692699B19D00565F4F /* SearchPanelPhoneDelegate.swift */; };
|
||||||
|
649A122926C14D0900DAB961 /* UIHostingController+ignoreKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649A122826C14D0900DAB961 /* UIHostingController+ignoreKeyboard.swift */; };
|
||||||
|
649A122D26C168CF00DAB961 /* View+floatingPanelGrabberHandlePadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649A122C26C168CF00DAB961 /* View+floatingPanelGrabberHandlePadding.swift */; };
|
||||||
|
64A5B7232691323900BCAA05 /* MapsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A5B7222691323900BCAA05 /* MapsApp.swift */; };
|
||||||
|
64A5B7252691323900BCAA05 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A5B7242691323900BCAA05 /* ContentView.swift */; };
|
||||||
|
64A5B734269133DC00BCAA05 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 64A5B733269133DC00BCAA05 /* FloatingPanel.framework */; };
|
||||||
|
64A5B735269133DC00BCAA05 /* FloatingPanel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 64A5B733269133DC00BCAA05 /* FloatingPanel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
|
64A5B738269134CA00BCAA05 /* VisualEffectBlur.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A5B737269134CA00BCAA05 /* VisualEffectBlur.swift */; };
|
||||||
|
64A5B73C2691469900BCAA05 /* FloatingPanelContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A5B73B2691469900BCAA05 /* FloatingPanelContentView.swift */; };
|
||||||
|
64A5B73E269147DC00BCAA05 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A5B73D269147DC00BCAA05 /* SearchBar.swift */; };
|
||||||
|
64A5B7402691532400BCAA05 /* ResultsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A5B73F2691532400BCAA05 /* ResultsList.swift */; };
|
||||||
|
64A5B7422691541A00BCAA05 /* HostingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A5B7412691541A00BCAA05 /* HostingCell.swift */; };
|
||||||
|
64F7E83126AD70EB00A0E0F7 /* View+floatingPanelContentInsetAdjustmentBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F7E82C26AD70EB00A0E0F7 /* View+floatingPanelContentInsetAdjustmentBehavior.swift */; };
|
||||||
|
64F7E83226AD70EB00A0E0F7 /* View+floatingPanelContentMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F7E82D26AD70EB00A0E0F7 /* View+floatingPanelContentMode.swift */; };
|
||||||
|
64F7E83326AD70EB00A0E0F7 /* FloatingPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F7E82E26AD70EB00A0E0F7 /* FloatingPanelView.swift */; };
|
||||||
|
64F7E83426AD70EB00A0E0F7 /* View+floatingPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F7E82F26AD70EB00A0E0F7 /* View+floatingPanel.swift */; };
|
||||||
|
64F7E83526AD70EB00A0E0F7 /* View+floatingPanelSurfaceAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F7E83026AD70EB00A0E0F7 /* View+floatingPanelSurfaceAppearance.swift */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
64A5B736269133DC00BCAA05 /* Embed Frameworks */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
64A5B735269133DC00BCAA05 /* FloatingPanel.framework in Embed Frameworks */,
|
||||||
|
);
|
||||||
|
name = "Embed Frameworks";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
6467E8632699AC5F00565F4F /* SurfaceAppearance+phone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SurfaceAppearance+phone.swift"; sourceTree = "<group>"; };
|
||||||
|
6467E8692699B19D00565F4F /* SearchPanelPhoneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchPanelPhoneDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
649A122826C14D0900DAB961 /* UIHostingController+ignoreKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIHostingController+ignoreKeyboard.swift"; sourceTree = "<group>"; };
|
||||||
|
649A122C26C168CF00DAB961 /* View+floatingPanelGrabberHandlePadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+floatingPanelGrabberHandlePadding.swift"; sourceTree = "<group>"; };
|
||||||
|
64A5B71F2691323900BCAA05 /* Maps-SwiftUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Maps-SwiftUI.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
64A5B7222691323900BCAA05 /* MapsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapsApp.swift; sourceTree = "<group>"; };
|
||||||
|
64A5B7242691323900BCAA05 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||||
|
64A5B733269133DC00BCAA05 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
64A5B737269134CA00BCAA05 /* VisualEffectBlur.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualEffectBlur.swift; sourceTree = "<group>"; };
|
||||||
|
64A5B73B2691469900BCAA05 /* FloatingPanelContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelContentView.swift; sourceTree = "<group>"; };
|
||||||
|
64A5B73D269147DC00BCAA05 /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = "<group>"; };
|
||||||
|
64A5B73F2691532400BCAA05 /* ResultsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultsList.swift; sourceTree = "<group>"; };
|
||||||
|
64A5B7412691541A00BCAA05 /* HostingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostingCell.swift; sourceTree = "<group>"; };
|
||||||
|
64F7E82C26AD70EB00A0E0F7 /* View+floatingPanelContentInsetAdjustmentBehavior.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+floatingPanelContentInsetAdjustmentBehavior.swift"; sourceTree = "<group>"; };
|
||||||
|
64F7E82D26AD70EB00A0E0F7 /* View+floatingPanelContentMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+floatingPanelContentMode.swift"; sourceTree = "<group>"; };
|
||||||
|
64F7E82E26AD70EB00A0E0F7 /* FloatingPanelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingPanelView.swift; sourceTree = "<group>"; };
|
||||||
|
64F7E82F26AD70EB00A0E0F7 /* View+floatingPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+floatingPanel.swift"; sourceTree = "<group>"; };
|
||||||
|
64F7E83026AD70EB00A0E0F7 /* View+floatingPanelSurfaceAppearance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+floatingPanelSurfaceAppearance.swift"; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
64A5B71C2691323900BCAA05 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
64A5B734269133DC00BCAA05 /* FloatingPanel.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
64A5B7162691323900BCAA05 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
64A5B7212691323900BCAA05 /* Maps */,
|
||||||
|
64A5B7202691323900BCAA05 /* Products */,
|
||||||
|
64A5B732269133DC00BCAA05 /* Frameworks */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
64A5B7202691323900BCAA05 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
64A5B71F2691323900BCAA05 /* Maps-SwiftUI.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
64A5B7212691323900BCAA05 /* Maps */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
64F7E82B26AD70EB00A0E0F7 /* FloatingPanel */,
|
||||||
|
64BE55702691BBB0006D98BD /* Representable */,
|
||||||
|
64A5B7222691323900BCAA05 /* MapsApp.swift */,
|
||||||
|
64A5B7242691323900BCAA05 /* ContentView.swift */,
|
||||||
|
64A5B73B2691469900BCAA05 /* FloatingPanelContentView.swift */,
|
||||||
|
6467E8692699B19D00565F4F /* SearchPanelPhoneDelegate.swift */,
|
||||||
|
6467E8632699AC5F00565F4F /* SurfaceAppearance+phone.swift */,
|
||||||
|
64A5B7412691541A00BCAA05 /* HostingCell.swift */,
|
||||||
|
64A5B73F2691532400BCAA05 /* ResultsList.swift */,
|
||||||
|
);
|
||||||
|
path = Maps;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
64A5B732269133DC00BCAA05 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
64A5B733269133DC00BCAA05 /* FloatingPanel.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
64BE55702691BBB0006D98BD /* Representable */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
64A5B73D269147DC00BCAA05 /* SearchBar.swift */,
|
||||||
|
64A5B737269134CA00BCAA05 /* VisualEffectBlur.swift */,
|
||||||
|
);
|
||||||
|
path = Representable;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
64F7E82B26AD70EB00A0E0F7 /* FloatingPanel */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
64F7E82E26AD70EB00A0E0F7 /* FloatingPanelView.swift */,
|
||||||
|
64F7E82F26AD70EB00A0E0F7 /* View+floatingPanel.swift */,
|
||||||
|
64F7E82C26AD70EB00A0E0F7 /* View+floatingPanelContentInsetAdjustmentBehavior.swift */,
|
||||||
|
64F7E82D26AD70EB00A0E0F7 /* View+floatingPanelContentMode.swift */,
|
||||||
|
64F7E83026AD70EB00A0E0F7 /* View+floatingPanelSurfaceAppearance.swift */,
|
||||||
|
649A122C26C168CF00DAB961 /* View+floatingPanelGrabberHandlePadding.swift */,
|
||||||
|
649A122826C14D0900DAB961 /* UIHostingController+ignoreKeyboard.swift */,
|
||||||
|
);
|
||||||
|
path = FloatingPanel;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
64A5B71E2691323900BCAA05 /* Maps-SwiftUI */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 64A5B72D2691323A00BCAA05 /* Build configuration list for PBXNativeTarget "Maps-SwiftUI" */;
|
||||||
|
buildPhases = (
|
||||||
|
64A5B71B2691323900BCAA05 /* Sources */,
|
||||||
|
64A5B71C2691323900BCAA05 /* Frameworks */,
|
||||||
|
64A5B71D2691323900BCAA05 /* Resources */,
|
||||||
|
64A5B736269133DC00BCAA05 /* Embed Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = "Maps-SwiftUI";
|
||||||
|
productName = "Maps-SwiftUI";
|
||||||
|
productReference = 64A5B71F2691323900BCAA05 /* Maps-SwiftUI.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
64A5B7172691323900BCAA05 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = 1;
|
||||||
|
LastSwiftUpdateCheck = 1300;
|
||||||
|
LastUpgradeCheck = 1300;
|
||||||
|
TargetAttributes = {
|
||||||
|
64A5B71E2691323900BCAA05 = {
|
||||||
|
CreatedOnToolsVersion = 13.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 64A5B71A2691323900BCAA05 /* Build configuration list for PBXProject "Maps-SwiftUI" */;
|
||||||
|
compatibilityVersion = "Xcode 13.0";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 64A5B7162691323900BCAA05;
|
||||||
|
productRefGroup = 64A5B7202691323900BCAA05 /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
64A5B71E2691323900BCAA05 /* Maps-SwiftUI */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
64A5B71D2691323900BCAA05 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
64A5B71B2691323900BCAA05 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
6467E86A2699B19D00565F4F /* SearchPanelPhoneDelegate.swift in Sources */,
|
||||||
|
64A5B7422691541A00BCAA05 /* HostingCell.swift in Sources */,
|
||||||
|
64A5B738269134CA00BCAA05 /* VisualEffectBlur.swift in Sources */,
|
||||||
|
64A5B7402691532400BCAA05 /* ResultsList.swift in Sources */,
|
||||||
|
6467E8642699AC5F00565F4F /* SurfaceAppearance+phone.swift in Sources */,
|
||||||
|
64F7E83226AD70EB00A0E0F7 /* View+floatingPanelContentMode.swift in Sources */,
|
||||||
|
64A5B7252691323900BCAA05 /* ContentView.swift in Sources */,
|
||||||
|
64A5B7232691323900BCAA05 /* MapsApp.swift in Sources */,
|
||||||
|
64A5B73C2691469900BCAA05 /* FloatingPanelContentView.swift in Sources */,
|
||||||
|
649A122D26C168CF00DAB961 /* View+floatingPanelGrabberHandlePadding.swift in Sources */,
|
||||||
|
64F7E83326AD70EB00A0E0F7 /* FloatingPanelView.swift in Sources */,
|
||||||
|
64F7E83426AD70EB00A0E0F7 /* View+floatingPanel.swift in Sources */,
|
||||||
|
64F7E83526AD70EB00A0E0F7 /* View+floatingPanelSurfaceAppearance.swift in Sources */,
|
||||||
|
64F7E83126AD70EB00A0E0F7 /* View+floatingPanelContentInsetAdjustmentBehavior.swift in Sources */,
|
||||||
|
64A5B73E269147DC00BCAA05 /* SearchBar.swift in Sources */,
|
||||||
|
649A122926C14D0900DAB961 /* UIHostingController+ignoreKeyboard.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
64A5B72B2691323A00BCAA05 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
64A5B72C2691323A00BCAA05 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
64A5B72E2691323A00BCAA05 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "example.Maps-SwiftUI";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
64A5B72F2691323A00BCAA05 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "exmaple.Maps-SwiftUI";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
64A5B71A2691323900BCAA05 /* Build configuration list for PBXProject "Maps-SwiftUI" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
64A5B72B2691323A00BCAA05 /* Debug */,
|
||||||
|
64A5B72C2691323A00BCAA05 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
64A5B72D2691323A00BCAA05 /* Build configuration list for PBXNativeTarget "Maps-SwiftUI" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
64A5B72E2691323A00BCAA05 /* Debug */,
|
||||||
|
64A5B72F2691323A00BCAA05 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 64A5B7172691323900BCAA05 /* Project object */;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import MapKit
|
||||||
|
|
||||||
|
struct ContentView: View {
|
||||||
|
@State private var region = MKCoordinateRegion(
|
||||||
|
center: CLLocationCoordinate2D(latitude: 37.623198015869235, longitude: -122.43066818432008),
|
||||||
|
span: MKCoordinateSpan(latitudeDelta: 0.4425100023575723, longitudeDelta: 0.28543697435880233)
|
||||||
|
)
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
Map(coordinateRegion: $region)
|
||||||
|
.ignoresSafeArea()
|
||||||
|
statusBarBlur
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var statusBarBlur: some View {
|
||||||
|
GeometryReader { geometry in
|
||||||
|
VisualEffectBlur()
|
||||||
|
.frame(height: geometry.safeAreaInsets.top)
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ContentView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ContentView()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import FloatingPanel
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// A proxy for exposing the methods of the floating panel controller.
|
||||||
|
public struct FloatingPanelProxy {
|
||||||
|
/// The associated floating panel controller.
|
||||||
|
public weak var fpc: FloatingPanelController?
|
||||||
|
|
||||||
|
/// Tracks the specified scroll view to correspond with the scroll.
|
||||||
|
///
|
||||||
|
/// - Parameter scrollView: Specify a scroll view to continuously and
|
||||||
|
/// seamlessly work in concert with interactions of the surface view.
|
||||||
|
public func track(scrollView: UIScrollView) {
|
||||||
|
fpc?.track(scrollView: scrollView)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the floating panel to the specified position.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - floatingPanelState: The state to move to.
|
||||||
|
/// - animated: `true` to animate the transition to the new state; `false`
|
||||||
|
/// otherwise.
|
||||||
|
public func move(
|
||||||
|
to floatingPanelState: FloatingPanelState,
|
||||||
|
animated: Bool,
|
||||||
|
completion: (() -> Void)? = nil
|
||||||
|
) {
|
||||||
|
fpc?.move(to: floatingPanelState, animated: animated, completion: completion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A view with an associated floating panel.
|
||||||
|
struct FloatingPanelView<Content: View, FloatingPanelContent: View>: UIViewControllerRepresentable {
|
||||||
|
/// A type that conforms to the `FloatingPanelControllerDelegate` protocol.
|
||||||
|
var delegate: FloatingPanelControllerDelegate?
|
||||||
|
|
||||||
|
/// The behavior for determining the adjusted content offsets.
|
||||||
|
@Environment(\.contentInsetAdjustmentBehavior) var contentInsetAdjustmentBehavior
|
||||||
|
|
||||||
|
/// Constants that define how a panel content fills in the surface.
|
||||||
|
@Environment(\.contentMode) var contentMode
|
||||||
|
|
||||||
|
/// The floating panel grabber handle offset.
|
||||||
|
@Environment(\.grabberHandlePadding) var grabberHandlePadding
|
||||||
|
|
||||||
|
/// The floating panel `surfaceView` appearance.
|
||||||
|
@Environment(\.surfaceAppearance) var surfaceAppearance
|
||||||
|
|
||||||
|
/// The view builder that creates the floating panel parent view content.
|
||||||
|
@ViewBuilder var content: Content
|
||||||
|
|
||||||
|
/// The view builder that creates the floating panel content.
|
||||||
|
@ViewBuilder var floatingPanelContent: (FloatingPanelProxy) -> FloatingPanelContent
|
||||||
|
|
||||||
|
public func makeUIViewController(context: Context) -> UIHostingController<Content> {
|
||||||
|
let hostingController = UIHostingController(rootView: content)
|
||||||
|
hostingController.view.backgroundColor = nil
|
||||||
|
// We need to wait for the current runloop cycle to complete before our
|
||||||
|
// view is actually added (into the view hierarchy), otherwise the
|
||||||
|
// environment is not ready yet.
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
context.coordinator.setupFloatingPanel(hostingController)
|
||||||
|
}
|
||||||
|
return hostingController
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateUIViewController(
|
||||||
|
_ uiViewController: UIHostingController<Content>,
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
|
context.coordinator.updateIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeCoordinator() -> Coordinator {
|
||||||
|
Coordinator(parent: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `FloatingPanelView` coordinator.
|
||||||
|
///
|
||||||
|
/// Responsible to setup the view hierarchy and floating panel.
|
||||||
|
final class Coordinator {
|
||||||
|
private let parent: FloatingPanelView<Content, FloatingPanelContent>
|
||||||
|
private lazy var fpc = FloatingPanelController()
|
||||||
|
|
||||||
|
init(parent: FloatingPanelView<Content, FloatingPanelContent>) {
|
||||||
|
self.parent = parent
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupFloatingPanel(_ parentViewController: UIViewController) {
|
||||||
|
updateIfNeeded()
|
||||||
|
let panelContent = parent.floatingPanelContent(FloatingPanelProxy(fpc: fpc))
|
||||||
|
let hostingViewController = UIHostingController(
|
||||||
|
rootView: panelContent,
|
||||||
|
ignoresKeyboard: true
|
||||||
|
)
|
||||||
|
hostingViewController.view.backgroundColor = nil
|
||||||
|
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() {
|
||||||
|
if fpc.contentInsetAdjustmentBehavior != parent.contentInsetAdjustmentBehavior {
|
||||||
|
fpc.contentInsetAdjustmentBehavior = parent.contentInsetAdjustmentBehavior
|
||||||
|
}
|
||||||
|
if fpc.contentMode != parent.contentMode {
|
||||||
|
fpc.contentMode = parent.contentMode
|
||||||
|
}
|
||||||
|
if fpc.delegate !== parent.delegate {
|
||||||
|
fpc.delegate = parent.delegate
|
||||||
|
}
|
||||||
|
if fpc.surfaceView.grabberHandlePadding != parent.grabberHandlePadding {
|
||||||
|
fpc.surfaceView.grabberHandlePadding = parent.grabberHandlePadding
|
||||||
|
}
|
||||||
|
if fpc.surfaceView.appearance != parent.surfaceAppearance {
|
||||||
|
fpc.surfaceView.appearance = parent.surfaceAppearance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// This extension makes sure SwiftUI views are not affected by iOS keyboard.
|
||||||
|
///
|
||||||
|
/// Credits to https://steipete.me/posts/disabling-keyboard-avoidance-in-swiftui-uihostingcontroller/
|
||||||
|
extension UIHostingController {
|
||||||
|
public convenience init(rootView: Content, ignoresKeyboard: Bool) {
|
||||||
|
self.init(rootView: rootView)
|
||||||
|
|
||||||
|
if ignoresKeyboard {
|
||||||
|
guard let viewClass = object_getClass(view) else { return }
|
||||||
|
|
||||||
|
let viewSubclassName = String(
|
||||||
|
cString: class_getName(viewClass)
|
||||||
|
).appending("_IgnoresKeyboard")
|
||||||
|
|
||||||
|
if let viewSubclass = NSClassFromString(viewSubclassName) {
|
||||||
|
object_setClass(view, viewSubclass)
|
||||||
|
} else {
|
||||||
|
guard
|
||||||
|
let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String,
|
||||||
|
let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0)
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
if let method = class_getInstanceMethod(
|
||||||
|
viewClass,
|
||||||
|
NSSelectorFromString("keyboardWillShowWithNotification:")
|
||||||
|
) {
|
||||||
|
let keyboardWillShow: @convention(block) (AnyObject, AnyObject) -> Void = { _, _ in }
|
||||||
|
class_addMethod(
|
||||||
|
viewSubclass,
|
||||||
|
NSSelectorFromString("keyboardWillShowWithNotification:"),
|
||||||
|
imp_implementationWithBlock(keyboardWillShow),
|
||||||
|
method_getTypeEncoding(method)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
objc_registerClassPair(viewSubclass)
|
||||||
|
object_setClass(view, viewSubclass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import FloatingPanel
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
/// Presents a floating panel using the given closure as its content.
|
||||||
|
///
|
||||||
|
/// The modifier's content view builder receives a `FloatingPanelProxy`
|
||||||
|
/// instance; you use the proxy's methods to interact with the associated
|
||||||
|
/// `FloatingPanelController`.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - delegate: A type that conforms to the
|
||||||
|
/// `FloatingPanelControllerDelegate` protocol. You have comprehensive
|
||||||
|
/// control over the floating panel behavior when you use a delegate.
|
||||||
|
/// - floatingPanelContent: The floating panel content. This view builder
|
||||||
|
/// receives a `FloatingPanelProxy` instance that you use to interact
|
||||||
|
/// with the `FloatingPanelController`.
|
||||||
|
public func floatingPanel<FloatingPanelContent: View>(
|
||||||
|
delegate: FloatingPanelControllerDelegate? = nil,
|
||||||
|
@ViewBuilder _ floatingPanelContent: @escaping (_: FloatingPanelProxy) -> FloatingPanelContent
|
||||||
|
) -> some View {
|
||||||
|
FloatingPanelView(
|
||||||
|
delegate: delegate,
|
||||||
|
content: { self },
|
||||||
|
floatingPanelContent: floatingPanelContent
|
||||||
|
)
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import FloatingPanel
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ContentInsetKey: EnvironmentKey {
|
||||||
|
static var defaultValue: FloatingPanelController.ContentInsetAdjustmentBehavior = .always
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EnvironmentValues {
|
||||||
|
/// The behavior for determining the adjusted content offsets.
|
||||||
|
var contentInsetAdjustmentBehavior: FloatingPanelController.ContentInsetAdjustmentBehavior {
|
||||||
|
get { self[ContentInsetKey.self] }
|
||||||
|
set { self[ContentInsetKey.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
/// Sets the content inset adjustment behavior for floating panels within
|
||||||
|
/// this view.
|
||||||
|
///
|
||||||
|
/// Use this modifier to set a specific content inset adjustment behavior
|
||||||
|
/// for floating panel instances within a view:
|
||||||
|
///
|
||||||
|
/// MainView()
|
||||||
|
/// .floatingPanel { _ in
|
||||||
|
/// FloatingPanelContent()
|
||||||
|
/// }
|
||||||
|
/// .floatingPanelContentInsetAdjustmentBehavior(.never)
|
||||||
|
///
|
||||||
|
/// - Parameter contentInsetAdjustmentBehavior: The content inset adjustment
|
||||||
|
/// behavior to set.
|
||||||
|
public func floatingPanelContentInsetAdjustmentBehavior(
|
||||||
|
_ contentInsetAdjustmentBehavior: FloatingPanelController.ContentInsetAdjustmentBehavior
|
||||||
|
) -> some View {
|
||||||
|
environment(\.contentInsetAdjustmentBehavior, contentInsetAdjustmentBehavior)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import FloatingPanel
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ContentModeKey: EnvironmentKey {
|
||||||
|
static var defaultValue: FloatingPanelController.ContentMode = .static
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EnvironmentValues {
|
||||||
|
/// Used to determine how the floating panel controller lays out the content
|
||||||
|
/// view when the surface position changes.
|
||||||
|
var contentMode: FloatingPanelController.ContentMode {
|
||||||
|
get { self[ContentModeKey.self] }
|
||||||
|
set { self[ContentModeKey.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
/// Sets the content mode for floating panels within this view.
|
||||||
|
///
|
||||||
|
/// Use this modifier to set a specific content mode for floating panel
|
||||||
|
/// instances within a view:
|
||||||
|
///
|
||||||
|
/// MainView()
|
||||||
|
/// .floatingPanel { _ in
|
||||||
|
/// FloatingPanelContent()
|
||||||
|
/// }
|
||||||
|
/// .floatingPanelContentMode(.static)
|
||||||
|
///
|
||||||
|
/// - Parameter contentMode: The content mode to set.
|
||||||
|
public func floatingPanelContentMode(
|
||||||
|
_ contentMode: FloatingPanelController.ContentMode
|
||||||
|
) -> some View {
|
||||||
|
environment(\.contentMode, contentMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import FloatingPanel
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct GrabberHandlePaddingKey: EnvironmentKey {
|
||||||
|
static var defaultValue: CGFloat = 6.0
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EnvironmentValues {
|
||||||
|
/// The offset of the grabber handle from the interactive edge.
|
||||||
|
var grabberHandlePadding: CGFloat {
|
||||||
|
get { self[GrabberHandlePaddingKey.self] }
|
||||||
|
set { self[GrabberHandlePaddingKey.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
/// Sets the grabber handle padding for floating panels within this view.
|
||||||
|
///
|
||||||
|
/// Use this modifier to set a specific padding to floating panel instances
|
||||||
|
/// within a view:
|
||||||
|
///
|
||||||
|
/// MainView()
|
||||||
|
/// .floatingPanel { _ in
|
||||||
|
/// FloatingPanelContent()
|
||||||
|
/// }
|
||||||
|
/// .floatingPanelGrabberHandlePadding(16)
|
||||||
|
///
|
||||||
|
/// - Parameter padding: The grabber handle padding to set.
|
||||||
|
public func floatingPanelGrabberHandlePadding(
|
||||||
|
_ padding: CGFloat
|
||||||
|
) -> some View {
|
||||||
|
environment(\.grabberHandlePadding, padding)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import FloatingPanel
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SurfaceAppearanceKey: EnvironmentKey {
|
||||||
|
static var defaultValue = SurfaceAppearance()
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EnvironmentValues {
|
||||||
|
/// The appearance of a surface view.
|
||||||
|
var surfaceAppearance: SurfaceAppearance {
|
||||||
|
get { self[SurfaceAppearanceKey.self] }
|
||||||
|
set { self[SurfaceAppearanceKey.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
/// Sets the surface appearance for floating panels within this view.
|
||||||
|
///
|
||||||
|
/// Use this modifier to set a specific surface appearance for floating
|
||||||
|
/// panel instances within a view:
|
||||||
|
///
|
||||||
|
/// MainView()
|
||||||
|
/// .floatingPanel { _ in
|
||||||
|
/// FloatingPanelContent()
|
||||||
|
/// }
|
||||||
|
/// .floatingPanelSurfaceAppearance(.transparent)
|
||||||
|
///
|
||||||
|
/// extension SurfaceAppearance {
|
||||||
|
/// static var transparent: SurfaceAppearance {
|
||||||
|
/// let appearance = SurfaceAppearance()
|
||||||
|
/// appearance.backgroundColor = .clear
|
||||||
|
/// return appearance
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// - Parameter surfaceAppearance: The surface appearance to set.
|
||||||
|
public func floatingPanelSurfaceAppearance(
|
||||||
|
_ surfaceAppearance: SurfaceAppearance
|
||||||
|
) -> some View {
|
||||||
|
environment(\.surfaceAppearance, surfaceAppearance)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct FloatingPanelContentView: View {
|
||||||
|
@State private var searchText = ""
|
||||||
|
@State private var isShowingCancelButton = false
|
||||||
|
var proxy: FloatingPanelProxy
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
searchBar
|
||||||
|
resultsList
|
||||||
|
}
|
||||||
|
// 👇🏻 for the floating panel grabber handle.
|
||||||
|
.padding(.top, 6)
|
||||||
|
.background(
|
||||||
|
VisualEffectBlur(blurStyle: .systemMaterial)
|
||||||
|
// ⚠️ If the `VisualEffectBlur` view receives taps, it's going
|
||||||
|
// to mess up with the whole panel and render it
|
||||||
|
// non-interactive, make sure it never receives any taps.
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
)
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchBar: some View {
|
||||||
|
SearchBar(
|
||||||
|
"Search for a place or address",
|
||||||
|
text: $searchText,
|
||||||
|
isShowingCancelButton: $isShowingCancelButton
|
||||||
|
) { isFocused in
|
||||||
|
proxy.move(to: isFocused ? .full : .half, animated: true)
|
||||||
|
isShowingCancelButton = isFocused
|
||||||
|
} onCancel: {
|
||||||
|
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultsList: some View {
|
||||||
|
ResultsList(onScrollViewCreated: proxy.track(scrollView:))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// A `UITableViewCell` that accepts a SwiftUI view as its content.
|
||||||
|
///
|
||||||
|
/// Credits to https://noahgilmore.com/blog/swiftui-self-sizing-cells/ .
|
||||||
|
public final class HostingCell<Content: View>: UITableViewCell {
|
||||||
|
private let hostingController = UIHostingController<Content?>(
|
||||||
|
rootView: nil,
|
||||||
|
ignoresKeyboard: true
|
||||||
|
)
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
hostingController.view.backgroundColor = nil
|
||||||
|
backgroundColor = .clear
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, unavailable)
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func set(rootView: Content, parentController: UIViewController) {
|
||||||
|
hostingController.rootView = rootView
|
||||||
|
hostingController.view.invalidateIntrinsicContentSize()
|
||||||
|
|
||||||
|
let requiresControllerMove = hostingController.parent != parentController
|
||||||
|
if requiresControllerMove {
|
||||||
|
parentController.addChild(hostingController)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !contentView.subviews.contains(hostingController.view) {
|
||||||
|
contentView.addSubview(hostingController.view)
|
||||||
|
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
contentView.addConstraints([
|
||||||
|
hostingController.view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
||||||
|
hostingController.view.topAnchor.constraint(equalTo: contentView.topAnchor),
|
||||||
|
hostingController.view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
||||||
|
hostingController.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
if requiresControllerMove {
|
||||||
|
hostingController.didMove(toParent: parentController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct MapsApp: App {
|
||||||
|
var body: some Scene {
|
||||||
|
WindowGroup {
|
||||||
|
ContentView()
|
||||||
|
.floatingPanel(delegate: SearchPanelPhoneDelegate()) { proxy in
|
||||||
|
FloatingPanelContentView(proxy: proxy)
|
||||||
|
}
|
||||||
|
.floatingPanelSurfaceAppearance(.phone)
|
||||||
|
.floatingPanelContentMode(.fitToBounds)
|
||||||
|
.floatingPanelContentInsetAdjustmentBehavior(.never)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// UIKit's `UISearchBar`brought to SwiftUI.
|
||||||
|
public struct SearchBar: UIViewRepresentable {
|
||||||
|
var title: String
|
||||||
|
@Binding var text: String
|
||||||
|
@Binding var isShowingCancelButton: Bool
|
||||||
|
var onEditingChanged: (Bool) -> Void
|
||||||
|
var onCancel: () -> Void
|
||||||
|
|
||||||
|
public init(
|
||||||
|
_ title: String = "",
|
||||||
|
text: Binding<String>,
|
||||||
|
isShowingCancelButton: Binding<Bool>,
|
||||||
|
onEditingChanged: @escaping (Bool) -> Void = { _ in },
|
||||||
|
onCancel: @escaping () -> Void
|
||||||
|
) {
|
||||||
|
self.title = title
|
||||||
|
self._text = text
|
||||||
|
self._isShowingCancelButton = isShowingCancelButton
|
||||||
|
self.onEditingChanged = onEditingChanged
|
||||||
|
self.onCancel = onCancel
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
|
||||||
|
let searchBar = UISearchBar(frame: .zero)
|
||||||
|
searchBar.searchBarStyle = .minimal
|
||||||
|
searchBar.isTranslucent = true
|
||||||
|
searchBar.placeholder = title
|
||||||
|
searchBar.delegate = context.coordinator
|
||||||
|
searchBar.autocapitalizationType = .none
|
||||||
|
return searchBar
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) {
|
||||||
|
uiView.text = text
|
||||||
|
uiView.placeholder = title
|
||||||
|
uiView.setShowsCancelButton(isShowingCancelButton, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeCoordinator() -> SearchBar.Coordinator {
|
||||||
|
Coordinator(parent: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Coordinator: NSObject, UISearchBarDelegate {
|
||||||
|
var parent: SearchBar
|
||||||
|
|
||||||
|
init(parent: SearchBar) {
|
||||||
|
self.parent = parent
|
||||||
|
}
|
||||||
|
|
||||||
|
public func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||||
|
parent.text = searchText
|
||||||
|
}
|
||||||
|
|
||||||
|
public func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
|
||||||
|
parent.onEditingChanged(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
||||||
|
searchBar.resignFirstResponder()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
|
||||||
|
parent.onEditingChanged(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
||||||
|
parent.onCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@available(iOS, introduced: 13, deprecated: 15, message: "Use iOS 15 material API.")
|
||||||
|
public struct VisualEffectBlur<Content: View>: UIViewRepresentable {
|
||||||
|
var blurStyle: UIBlurEffect.Style = .systemMaterial
|
||||||
|
var vibrancyStyle: UIVibrancyEffectStyle? = nil
|
||||||
|
@ViewBuilder var content: Content
|
||||||
|
|
||||||
|
public func makeUIView(context: Context) -> UIVisualEffectView {
|
||||||
|
context.coordinator.blurView
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateUIView(_ view: UIVisualEffectView, context: Context) {
|
||||||
|
context.coordinator.update(
|
||||||
|
content: content,
|
||||||
|
blurStyle: blurStyle,
|
||||||
|
vibrancyStyle: vibrancyStyle
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeCoordinator() -> Coordinator {
|
||||||
|
Coordinator(content: content)
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Coordinator {
|
||||||
|
let blurView = UIVisualEffectView()
|
||||||
|
let vibrancyView = UIVisualEffectView()
|
||||||
|
let hostingController: UIHostingController<Content>
|
||||||
|
|
||||||
|
init(content: Content) {
|
||||||
|
hostingController = UIHostingController(rootView: content)
|
||||||
|
hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
hostingController.view.backgroundColor = nil
|
||||||
|
blurView.contentView.addSubview(vibrancyView)
|
||||||
|
|
||||||
|
blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
vibrancyView.contentView.addSubview(hostingController.view)
|
||||||
|
vibrancyView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(content: Content, blurStyle: UIBlurEffect.Style, vibrancyStyle: UIVibrancyEffectStyle?) {
|
||||||
|
hostingController.rootView = content
|
||||||
|
|
||||||
|
let blurEffect = UIBlurEffect(style: blurStyle)
|
||||||
|
blurView.effect = blurEffect
|
||||||
|
|
||||||
|
if let vibrancyStyle = vibrancyStyle {
|
||||||
|
vibrancyView.effect = UIVibrancyEffect(blurEffect: blurEffect, style: vibrancyStyle)
|
||||||
|
} else {
|
||||||
|
vibrancyView.effect = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hostingController.view.setNeedsDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension VisualEffectBlur where Content == EmptyView {
|
||||||
|
init(
|
||||||
|
blurStyle: UIBlurEffect.Style = .systemMaterial,
|
||||||
|
vibrancyStyle: UIVibrancyEffectStyle? = nil
|
||||||
|
) {
|
||||||
|
self.init(blurStyle: blurStyle, vibrancyStyle: vibrancyStyle) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ResultsList: UIViewControllerRepresentable {
|
||||||
|
var onScrollViewCreated: (_ scrollView: UIScrollView) -> Void
|
||||||
|
|
||||||
|
func makeUIViewController(
|
||||||
|
context: Context
|
||||||
|
) -> ResultsTableViewController {
|
||||||
|
let rtvc = ResultsTableViewController()
|
||||||
|
onScrollViewCreated(rtvc.tableView)
|
||||||
|
return rtvc
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(
|
||||||
|
_ uiViewController: ResultsTableViewController,
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class ResultsTableViewController: UITableViewController {
|
||||||
|
private let reuseIdentifier = "HostingCell<ResultListCell>"
|
||||||
|
|
||||||
|
private enum Section: CaseIterable {
|
||||||
|
case main
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct TableViewItem: Hashable {
|
||||||
|
let color: Color
|
||||||
|
let symbolName: String
|
||||||
|
let title: String
|
||||||
|
let description: String
|
||||||
|
}
|
||||||
|
|
||||||
|
private var dataSource: UITableViewDiffableDataSource<Section, TableViewItem>?
|
||||||
|
|
||||||
|
// MARK: Lifecycle
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
tableView.backgroundColor = nil
|
||||||
|
tableView.register(HostingCell<ResultListCell>.self, forCellReuseIdentifier: reuseIdentifier)
|
||||||
|
configureDataSource()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: UITableViewDataSource
|
||||||
|
|
||||||
|
private func configureDataSource() {
|
||||||
|
dataSource = UITableViewDiffableDataSource
|
||||||
|
<Section, TableViewItem>(tableView: tableView) { [weak self] tableView, _, tableItem -> UITableViewCell? in
|
||||||
|
self?.tableView(tableView, cellForTableViewItem: tableItem)
|
||||||
|
}
|
||||||
|
tableView.dataSource = dataSource
|
||||||
|
|
||||||
|
var snapshot = NSDiffableDataSourceSnapshot<Section, TableViewItem>()
|
||||||
|
snapshot.appendSections([.main])
|
||||||
|
let results: [TableViewItem] = (1...100).map {
|
||||||
|
TableViewItem(
|
||||||
|
color: Color(red: 255 / 255.0, green: 94 / 255.0 , blue: 94 / 255.0),
|
||||||
|
symbolName: "heart.fill",
|
||||||
|
title: "Favorites",
|
||||||
|
description: "\($0) Places"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
snapshot.appendItems(results, toSection: .main)
|
||||||
|
dataSource?.apply(snapshot, animatingDifferences: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func tableView(
|
||||||
|
_ tableView: UITableView,
|
||||||
|
cellForTableViewItem tableViewItem: TableViewItem
|
||||||
|
) -> UITableViewCell {
|
||||||
|
let cell: HostingCell<ResultListCell> = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier) as! HostingCell<ResultListCell>
|
||||||
|
setupResultTableViewCell(
|
||||||
|
cell,
|
||||||
|
color: tableViewItem.color,
|
||||||
|
symbolName: tableViewItem.symbolName,
|
||||||
|
title: tableViewItem.title,
|
||||||
|
description: tableViewItem.description
|
||||||
|
)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupResultTableViewCell(
|
||||||
|
_ cell: HostingCell<ResultListCell>,
|
||||||
|
color: Color,
|
||||||
|
symbolName: String,
|
||||||
|
title: String,
|
||||||
|
description: String
|
||||||
|
) {
|
||||||
|
cell.set(
|
||||||
|
rootView: ResultListCell(
|
||||||
|
color: color,
|
||||||
|
symbolName: symbolName,
|
||||||
|
title: title,
|
||||||
|
description: description
|
||||||
|
),
|
||||||
|
parentController: self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: UITableViewDelegate
|
||||||
|
|
||||||
|
override func tableView(
|
||||||
|
_ tableView: UITableView,
|
||||||
|
didSelectRowAt indexPath: IndexPath
|
||||||
|
) {
|
||||||
|
tableView.deselectRow(at: indexPath, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ResultListCell: View {
|
||||||
|
let color: Color
|
||||||
|
let symbolName: String
|
||||||
|
let title: String
|
||||||
|
let description: String
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: symbolName)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.font(.headline)
|
||||||
|
.padding(8)
|
||||||
|
.background(Circle().fill(color))
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text(title)
|
||||||
|
.font(.system(size: 20, weight: .bold))
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
Text(description)
|
||||||
|
.font(.system(size: 13))
|
||||||
|
.foregroundColor(Color(.secondaryLabel))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import FloatingPanel
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
final class SearchPanelPhoneDelegate: FloatingPanelControllerDelegate {
|
||||||
|
func floatingPanelWillBeginDragging(_ vc: FloatingPanelController) {
|
||||||
|
if vc.state == .full {
|
||||||
|
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import FloatingPanel
|
||||||
|
|
||||||
|
extension FloatingPanel.SurfaceAppearance {
|
||||||
|
static var phone: SurfaceAppearance {
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerCurve = .continuous
|
||||||
|
appearance.cornerRadius = 8.0
|
||||||
|
appearance.backgroundColor = .clear
|
||||||
|
return appearance
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,12 +12,13 @@
|
|||||||
549D23D2233C77D5008EF4D7 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23D1233C77D5008EF4D7 /* FloatingPanel.framework */; };
|
549D23D2233C77D5008EF4D7 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23D1233C77D5008EF4D7 /* FloatingPanel.framework */; };
|
||||||
549D23D3233C77D5008EF4D7 /* FloatingPanel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23D1233C77D5008EF4D7 /* FloatingPanel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
549D23D3233C77D5008EF4D7 /* FloatingPanel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23D1233C77D5008EF4D7 /* FloatingPanel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
54B5112A216C3D840033A6F3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B51129216C3D840033A6F3 /* AppDelegate.swift */; };
|
54B5112A216C3D840033A6F3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B51129216C3D840033A6F3 /* AppDelegate.swift */; };
|
||||||
54B5112C216C3D840033A6F3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B5112B216C3D840033A6F3 /* ViewController.swift */; };
|
54B5112C216C3D840033A6F3 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B5112B216C3D840033A6F3 /* MainViewController.swift */; };
|
||||||
54B5112F216C3D840033A6F3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54B5112D216C3D840033A6F3 /* Main.storyboard */; };
|
54B5112F216C3D840033A6F3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54B5112D216C3D840033A6F3 /* Main.storyboard */; };
|
||||||
54B51131216C3D860033A6F3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 54B51130216C3D860033A6F3 /* Assets.xcassets */; };
|
54B51131216C3D860033A6F3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 54B51130216C3D860033A6F3 /* Assets.xcassets */; };
|
||||||
54B51134216C3D860033A6F3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54B51132216C3D860033A6F3 /* LaunchScreen.storyboard */; };
|
54B51134216C3D860033A6F3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54B51132216C3D860033A6F3 /* LaunchScreen.storyboard */; };
|
||||||
54E26CB624A989090066C720 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E26CB524A989090066C720 /* Utils.swift */; };
|
54E26CB624A989090066C720 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E26CB524A989090066C720 /* Utils.swift */; };
|
||||||
54E26CB824A98E310066C720 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E26CB724A98E310066C720 /* DetailViewController.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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
@@ -40,13 +41,14 @@
|
|||||||
549D23D1233C77D5008EF4D7 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
549D23D1233C77D5008EF4D7 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
54B51126216C3D840033A6F3 /* Maps.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Maps.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
54B51126216C3D840033A6F3 /* Maps.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Maps.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
54B51129216C3D840033A6F3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
54B51129216C3D840033A6F3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
54B5112B216C3D840033A6F3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
54B5112B216C3D840033A6F3 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
|
||||||
54B5112E216C3D840033A6F3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
54B5112E216C3D840033A6F3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
54B51130216C3D860033A6F3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
54B51130216C3D860033A6F3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
54B51133216C3D860033A6F3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
54B51133216C3D860033A6F3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
54B51135216C3D860033A6F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -55,6 +57,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
543844BD23D2BE2000D5EDE4 /* MapKit.framework in Frameworks */,
|
543844BD23D2BE2000D5EDE4 /* MapKit.framework in Frameworks */,
|
||||||
|
5D82A6A728D18422006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
|
||||||
549D23D2233C77D5008EF4D7 /* FloatingPanel.framework in Frameworks */,
|
549D23D2233C77D5008EF4D7 /* FloatingPanel.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -65,6 +68,7 @@
|
|||||||
543844BB23D2BE1F00D5EDE4 /* Frameworks */ = {
|
543844BB23D2BE1F00D5EDE4 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
5D82A6A628D1841E006A44BA /* libswiftCoreGraphics.tbd */,
|
||||||
543844BC23D2BE2000D5EDE4 /* MapKit.framework */,
|
543844BC23D2BE2000D5EDE4 /* MapKit.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
@@ -91,14 +95,14 @@
|
|||||||
54B51128216C3D840033A6F3 /* Maps */ = {
|
54B51128216C3D840033A6F3 /* Maps */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
54B51130216C3D860033A6F3 /* Assets.xcassets */,
|
||||||
|
54B51132216C3D860033A6F3 /* LaunchScreen.storyboard */,
|
||||||
|
54B5112D216C3D840033A6F3 /* Main.storyboard */,
|
||||||
54B51129216C3D840033A6F3 /* AppDelegate.swift */,
|
54B51129216C3D840033A6F3 /* AppDelegate.swift */,
|
||||||
54B5112B216C3D840033A6F3 /* ViewController.swift */,
|
54B5112B216C3D840033A6F3 /* MainViewController.swift */,
|
||||||
549A5F58244673FE0025F312 /* SearchViewController.swift */,
|
549A5F58244673FE0025F312 /* SearchViewController.swift */,
|
||||||
54E26CB724A98E310066C720 /* DetailViewController.swift */,
|
54E26CB724A98E310066C720 /* DetailViewController.swift */,
|
||||||
54E26CB524A989090066C720 /* Utils.swift */,
|
54E26CB524A989090066C720 /* Utils.swift */,
|
||||||
54B5112D216C3D840033A6F3 /* Main.storyboard */,
|
|
||||||
54B51130216C3D860033A6F3 /* Assets.xcassets */,
|
|
||||||
54B51132216C3D860033A6F3 /* LaunchScreen.storyboard */,
|
|
||||||
54B51135216C3D860033A6F3 /* Info.plist */,
|
54B51135216C3D860033A6F3 /* Info.plist */,
|
||||||
);
|
);
|
||||||
path = Maps;
|
path = Maps;
|
||||||
@@ -132,7 +136,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 1000;
|
LastSwiftUpdateCheck = 1000;
|
||||||
LastUpgradeCheck = 1000;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = scenee;
|
ORGANIZATIONNAME = scenee;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
54B51125216C3D840033A6F3 = {
|
54B51125216C3D840033A6F3 = {
|
||||||
@@ -177,7 +181,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
549A5F59244673FE0025F312 /* SearchViewController.swift in Sources */,
|
549A5F59244673FE0025F312 /* SearchViewController.swift in Sources */,
|
||||||
54B5112C216C3D840033A6F3 /* ViewController.swift in Sources */,
|
54B5112C216C3D840033A6F3 /* MainViewController.swift in Sources */,
|
||||||
54E26CB824A98E310066C720 /* DetailViewController.swift in Sources */,
|
54E26CB824A98E310066C720 /* DetailViewController.swift in Sources */,
|
||||||
54B5112A216C3D840033A6F3 /* AppDelegate.swift in Sources */,
|
54B5112A216C3D840033A6F3 /* AppDelegate.swift in Sources */,
|
||||||
54E26CB624A989090066C720 /* Utils.swift in Sources */,
|
54E26CB624A989090066C720 /* Utils.swift in Sources */,
|
||||||
@@ -232,6 +236,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -293,6 +298,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -334,7 +340,11 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.Maps;
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SDKROOT)/usr/lib/swift",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = example.Maps;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
@@ -353,7 +363,11 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.Maps;
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SDKROOT)/usr/lib/swift",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = example.Maps;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1000"
|
LastUpgradeVersion = "1300"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -27,8 +27,6 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
@@ -38,8 +36,8 @@
|
|||||||
ReferencedContainer = "container:Maps.xcodeproj">
|
ReferencedContainer = "container:Maps.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
<Testables>
|
||||||
</AdditionalOptions>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@@ -63,8 +61,6 @@
|
|||||||
ReferencedContainer = "container:Maps.xcodeproj">
|
ReferencedContainer = "container:Maps.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
|||||||
@@ -5,9 +5,5 @@ import UIKit
|
|||||||
@UIApplicationMain
|
@UIApplicationMain
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||||
<device id="retina5_9" orientation="portrait" appearance="light"/>
|
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--View Controller-->
|
<!--Main View Controller-->
|
||||||
<scene sceneID="tne-QT-ifu">
|
<scene sceneID="tne-QT-ifu">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="Maps" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="BYZ-38-t0r" customClass="MainViewController" customModule="Maps" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="5Jw-n2-Cpw">
|
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="5Jw-n2-Cpw">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
</mapView>
|
</mapView>
|
||||||
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="d9i-3g-8Ja">
|
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="d9i-3g-8Ja">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="59"/>
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="lMa-xa-AVV">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="lMa-xa-AVV">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="59"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
</view>
|
</view>
|
||||||
<blurEffect style="prominent"/>
|
<blurEffect style="prominent"/>
|
||||||
@@ -54,28 +54,28 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<viewController storyboardIdentifier="SearchViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="0S1-Lk-JgE" customClass="SearchViewController" customModule="Maps" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController storyboardIdentifier="SearchViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="0S1-Lk-JgE" customClass="SearchViewController" customModule="Maps" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="Ncl-E9-yRn">
|
<view key="view" contentMode="scaleToFill" id="Ncl-E9-yRn">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ye3-uU-bq3">
|
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ye3-uU-bq3">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="ED1-gT-FBj">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="ED1-gT-FBj">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<searchBar contentMode="redraw" searchBarStyle="minimal" translatesAutoresizingMaskIntoConstraints="NO" id="Zcj-SE-gb8">
|
<searchBar contentMode="redraw" searchBarStyle="minimal" translatesAutoresizingMaskIntoConstraints="NO" id="Zcj-SE-gb8">
|
||||||
<rect key="frame" x="0.0" y="6" width="375" height="56"/>
|
<rect key="frame" x="0.0" y="6" width="393" height="56"/>
|
||||||
<textInputTraits key="textInputTraits"/>
|
<textInputTraits key="textInputTraits"/>
|
||||||
</searchBar>
|
</searchBar>
|
||||||
<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="D7r-re-InH">
|
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="D7r-re-InH">
|
||||||
<rect key="frame" x="0.0" y="66" width="375" height="746"/>
|
<rect key="frame" x="0.0" y="66" width="393" height="786"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<view key="tableHeaderView" contentMode="scaleToFill" id="u28-LY-hIh" customClass="SearchHeaderView" customModule="Maps" customModuleProvider="target">
|
<view key="tableHeaderView" contentMode="scaleToFill" id="u28-LY-hIh" customClass="SearchHeaderView" customModule="Maps" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="116"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="116"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" baselineRelativeArrangement="YES" translatesAutoresizingMaskIntoConstraints="NO" id="era-8w-yA1">
|
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" baselineRelativeArrangement="YES" translatesAutoresizingMaskIntoConstraints="NO" id="era-8w-yA1">
|
||||||
<rect key="frame" x="24" y="10.666666666666664" width="327" height="97.333333333333343"/>
|
<rect key="frame" x="24" y="10.666666666666664" width="345" height="97.333333333333343"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="auI-1v-Yfk">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="auI-1v-Yfk">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="60" height="97.333333333333329"/>
|
<rect key="frame" x="0.0" y="0.0" width="60" height="97.333333333333329"/>
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="0vd-sD-XKv">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="0vd-sD-XKv">
|
||||||
<rect key="frame" x="89" y="0.0" width="60" height="97.333333333333329"/>
|
<rect key="frame" x="95" y="0.0" width="60" height="97.333333333333329"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="shopping" translatesAutoresizingMaskIntoConstraints="NO" id="xcm-St-HAo">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="shopping" translatesAutoresizingMaskIntoConstraints="NO" id="xcm-St-HAo">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
|
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="Jd8-YL-b5s">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="Jd8-YL-b5s">
|
||||||
<rect key="frame" x="178" y="0.0" width="60" height="97.333333333333329"/>
|
<rect key="frame" x="190" y="0.0" width="60" height="97.333333333333329"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fun" translatesAutoresizingMaskIntoConstraints="NO" id="bMJ-Jn-Gi8">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fun" translatesAutoresizingMaskIntoConstraints="NO" id="bMJ-Jn-Gi8">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
|
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
|
||||||
@@ -136,7 +136,7 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="dTL-e1-Arz">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="dTL-e1-Arz">
|
||||||
<rect key="frame" x="267" y="0.0" width="60" height="97.333333333333329"/>
|
<rect key="frame" x="285" y="0.0" width="60" height="97.333333333333329"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="travel" translatesAutoresizingMaskIntoConstraints="NO" id="8h3-fo-pC3">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="travel" translatesAutoresizingMaskIntoConstraints="NO" id="8h3-fo-pC3">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
|
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
|
||||||
@@ -168,10 +168,10 @@
|
|||||||
</view>
|
</view>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" rowHeight="70" id="LzC-B9-Adb" customClass="SearchCell" customModule="Maps" customModuleProvider="target">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" rowHeight="70" id="LzC-B9-Adb" customClass="SearchCell" customModule="Maps" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="144" width="375" height="70"/>
|
<rect key="frame" x="0.0" y="166" width="393" height="70"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="LzC-B9-Adb" id="evr-60-laS">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="LzC-B9-Adb" id="evr-60-laS">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="70"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="70"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="like" translatesAutoresizingMaskIntoConstraints="NO" id="GEk-yE-lLq">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="like" translatesAutoresizingMaskIntoConstraints="NO" id="GEk-yE-lLq">
|
||||||
@@ -183,16 +183,16 @@
|
|||||||
</constraints>
|
</constraints>
|
||||||
</imageView>
|
</imageView>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="Gfl-Oy-rsy">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="Gfl-Oy-rsy">
|
||||||
<rect key="frame" x="57" y="12" width="303" height="46"/>
|
<rect key="frame" x="57" y="12" width="321" height="46"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Favorites" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Spf-8L-Ne6">
|
<label opaque="NO" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Favorites" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Spf-8L-Ne6">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="303" height="22"/>
|
<rect key="frame" x="0.0" y="0.0" width="321" height="22"/>
|
||||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
|
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0 Places" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gyo-3V-7U8">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0 Places" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gyo-3V-7U8">
|
||||||
<rect key="frame" x="0.0" y="24" width="303" height="22"/>
|
<rect key="frame" x="0.0" y="24" width="321" height="22"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||||
<color key="textColor" red="0.57647058819999997" green="0.57647058819999997" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="textColor" red="0.57647058819999997" green="0.57647058819999997" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
@@ -257,18 +257,18 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<viewController storyboardIdentifier="DetailViewController" id="Tp2-MF-IFz" customClass="DetailViewController" customModule="Maps" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController storyboardIdentifier="DetailViewController" id="Tp2-MF-IFz" customClass="DetailViewController" customModule="Maps" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="FmO-AT-4Y7">
|
<view key="view" contentMode="scaleToFill" id="FmO-AT-4Y7">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<visualEffectView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="c3d-2e-0b1">
|
<visualEffectView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="c3d-2e-0b1">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
<rect key="frame" x="-1" y="-1" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="9fL-a5-0LS">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="9fL-a5-0LS">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<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="kP7-56-wlG">
|
<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="kP7-56-wlG">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
</tableView>
|
</tableView>
|
||||||
</subviews>
|
</subviews>
|
||||||
|
|||||||
+54
-67
@@ -4,7 +4,7 @@ import UIKit
|
|||||||
import MapKit
|
import MapKit
|
||||||
import FloatingPanel
|
import FloatingPanel
|
||||||
|
|
||||||
class ViewController: UIViewController {
|
class MainViewController: UIViewController {
|
||||||
typealias PanelDelegate = FloatingPanelControllerDelegate & UIGestureRecognizerDelegate
|
typealias PanelDelegate = FloatingPanelControllerDelegate & UIGestureRecognizerDelegate
|
||||||
|
|
||||||
// Search Panel
|
// Search Panel
|
||||||
@@ -22,7 +22,9 @@ class ViewController: UIViewController {
|
|||||||
storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
|
storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
|
||||||
|
|
||||||
@IBOutlet weak var mapView: MKMapView!
|
@IBOutlet weak var mapView: MKMapView!
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MainViewController {
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
fpc.contentMode = .fitToBounds
|
fpc.contentMode = .fitToBounds
|
||||||
@@ -40,7 +42,7 @@ class ViewController: UIViewController {
|
|||||||
layoutPanelForPhone()
|
layoutPanelForPhone()
|
||||||
}
|
}
|
||||||
|
|
||||||
setupMapView()
|
setUpMapView()
|
||||||
setUpSearchView()
|
setUpSearchView()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,9 +55,11 @@ class ViewController: UIViewController {
|
|||||||
|
|
||||||
override func viewDidDisappear(_ animated: Bool) {
|
override func viewDidDisappear(_ animated: Bool) {
|
||||||
super.viewDidDisappear(animated)
|
super.viewDidDisappear(animated)
|
||||||
teardownMapView()
|
tearDownMapView()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MainViewController {
|
||||||
func layoutPanelForPad() {
|
func layoutPanelForPad() {
|
||||||
fpc.behavior = SearchPaneliPadBehavior()
|
fpc.behavior = SearchPaneliPadBehavior()
|
||||||
fpc.panGestureRecognizer.delegateProxy = fpcDelegate
|
fpc.panGestureRecognizer.delegateProxy = fpcDelegate
|
||||||
@@ -76,15 +80,14 @@ class ViewController: UIViewController {
|
|||||||
self.didMove(toParent: self)
|
self.didMove(toParent: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fpc.setAppearanceForPad()
|
[fpc, detailFpc].forEach { $0.setAppearanceForPad() }
|
||||||
detailFpc.setAppearanceForPad()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func layoutPanelForPhone() {
|
func layoutPanelForPhone() {
|
||||||
fpc.track(scrollView: searchVC.tableView) // Only track the tabvle view on iPhone
|
fpc.track(scrollView: searchVC.tableView) // Only track the table view on iPhone
|
||||||
fpc.addPanel(toParent: self, animated: true)
|
fpc.addPanel(toParent: self, animated: true)
|
||||||
fpc.setAppearanceForPhone()
|
|
||||||
detailFpc.setAppearanceForPhone()
|
[fpc, detailFpc].forEach { $0.setAppearanceForPhone()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +119,7 @@ extension FloatingPanelController {
|
|||||||
|
|
||||||
// MARK: - UISearchBarDelegate
|
// MARK: - UISearchBarDelegate
|
||||||
|
|
||||||
extension ViewController: UISearchBarDelegate {
|
extension MainViewController: UISearchBarDelegate {
|
||||||
func activate(searchBar: UISearchBar) {
|
func activate(searchBar: UISearchBar) {
|
||||||
searchBar.showsCancelButton = true
|
searchBar.showsCancelButton = true
|
||||||
searchVC.showHeader(animated: true)
|
searchVC.showHeader(animated: true)
|
||||||
@@ -147,12 +150,20 @@ extension ViewController: UISearchBarDelegate {
|
|||||||
// MARK: - iPhone
|
// MARK: - iPhone
|
||||||
|
|
||||||
class SearchPanelPhoneDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
class SearchPanelPhoneDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
||||||
unowned let owner: ViewController
|
unowned let owner: MainViewController
|
||||||
|
|
||||||
init(owner: ViewController) {
|
init(owner: MainViewController) {
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func floatingPanel(
|
||||||
|
_ fpc: FloatingPanelController,
|
||||||
|
shouldAllowToScroll scrollView: UIScrollView,
|
||||||
|
in state: FloatingPanelState
|
||||||
|
) -> Bool {
|
||||||
|
return state == .full || state == .half
|
||||||
|
}
|
||||||
|
|
||||||
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
|
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
|
||||||
switch newCollection.verticalSizeClass {
|
switch newCollection.verticalSizeClass {
|
||||||
case .compact:
|
case .compact:
|
||||||
@@ -201,37 +212,21 @@ class SearchPanelPhoneDelegate: NSObject, FloatingPanelControllerDelegate, UIGes
|
|||||||
if targetState.pointee != .full {
|
if targetState.pointee != .full {
|
||||||
owner.searchVC.hideHeader(animated: true)
|
owner.searchVC.hideHeader(animated: true)
|
||||||
}
|
}
|
||||||
if targetState.pointee == .tip {
|
|
||||||
vc.contentMode = .static
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func floatingPanelDidEndAttracting(_ fpc: FloatingPanelController) {
|
|
||||||
fpc.contentMode = .fitToBounds
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SearchPanelLandscapeLayout: FloatingPanelLayout {
|
class SearchPanelLandscapeLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
let initialState: FloatingPanelState = .tip
|
let initialState: FloatingPanelState = .tip
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
||||||
if #available(iOS 11.0, *) {
|
return [
|
||||||
return [
|
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 8.0),
|
||||||
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 8.0),
|
surfaceView.widthAnchor.constraint(equalToConstant: 291),
|
||||||
surfaceView.widthAnchor.constraint(equalToConstant: 291),
|
]
|
||||||
]
|
|
||||||
} else {
|
|
||||||
return [
|
|
||||||
surfaceView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8.0),
|
|
||||||
surfaceView.widthAnchor.constraint(equalToConstant: 291),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -239,20 +234,18 @@ class SearchPanelLandscapeLayout: FloatingPanelLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DetailPanelPhoneDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
class DetailPanelPhoneDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
||||||
unowned let owner: ViewController
|
unowned let owner: MainViewController
|
||||||
|
|
||||||
init(owner: ViewController) {
|
init(owner: MainViewController) {
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DetailPanelPhoneLayout: FloatingPanelLayout {
|
class DetailPanelPhoneLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -262,9 +255,9 @@ class DetailPanelPhoneLayout: FloatingPanelLayout {
|
|||||||
// MARK: - iPad
|
// MARK: - iPad
|
||||||
|
|
||||||
class SearchPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
class SearchPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
||||||
unowned let owner: ViewController
|
unowned let owner: MainViewController
|
||||||
|
|
||||||
init(owner: ViewController) {
|
init(owner: MainViewController) {
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,13 +294,11 @@ class SearchPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestu
|
|||||||
class SearchPanelPadLayout: FloatingPanelLayout {
|
class SearchPanelPadLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .top
|
let position: FloatingPanelPosition = .top
|
||||||
let initialState: FloatingPanelState = .tip
|
let initialState: FloatingPanelState = .tip
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: 80.0, edge: .top, referenceGuide: .superview),
|
||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 80.0, edge: .top, referenceGuide: .superview),
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 200.0, edge: .top, referenceGuide: .superview),
|
||||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 200.0, edge: .top, referenceGuide: .superview),
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
@@ -329,15 +320,15 @@ class SearchPaneliPadBehavior: FloatingPanelBehavior {
|
|||||||
var momentumProjectionRate: CGFloat {
|
var momentumProjectionRate: CGFloat {
|
||||||
return UIScrollView.DecelerationRate.fast.rawValue
|
return UIScrollView.DecelerationRate.fast.rawValue
|
||||||
}
|
}
|
||||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedTargetPosition: FloatingPanelState) -> Bool {
|
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedState: FloatingPanelState) -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DetailPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
class DetailPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
||||||
unowned let owner: ViewController
|
unowned let owner: MainViewController
|
||||||
|
|
||||||
init(owner: ViewController) {
|
init(owner: MainViewController) {
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,11 +352,9 @@ class DetailPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestu
|
|||||||
|
|
||||||
class DetailPanelPadLeftLayout: FloatingPanelLayout {
|
class DetailPanelPadLeftLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .left
|
let position: FloatingPanelPosition = .left
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .left, referenceGuide: .superview)
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .left, referenceGuide: .superview)
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -374,11 +363,9 @@ class DetailPanelPadLeftLayout: FloatingPanelLayout {
|
|||||||
|
|
||||||
class DetailPanelPadRightLayout: FloatingPanelLayout {
|
class DetailPanelPadRightLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .right
|
let position: FloatingPanelPosition = .right
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .right, referenceGuide: .superview)
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .right, referenceGuide: .superview)
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -387,8 +374,8 @@ class DetailPanelPadRightLayout: FloatingPanelLayout {
|
|||||||
|
|
||||||
// MARK: - MKMapViewDelegate
|
// MARK: - MKMapViewDelegate
|
||||||
|
|
||||||
extension ViewController: MKMapViewDelegate {
|
extension MainViewController: MKMapViewDelegate {
|
||||||
func setupMapView() {
|
func setUpMapView() {
|
||||||
let center = CLLocationCoordinate2D(latitude: 37.623198015869235,
|
let center = CLLocationCoordinate2D(latitude: 37.623198015869235,
|
||||||
longitude: -122.43066818432008)
|
longitude: -122.43066818432008)
|
||||||
let span = MKCoordinateSpan(latitudeDelta: 0.4425100023575723,
|
let span = MKCoordinateSpan(latitudeDelta: 0.4425100023575723,
|
||||||
@@ -400,7 +387,7 @@ extension ViewController: MKMapViewDelegate {
|
|||||||
mapView.delegate = self
|
mapView.delegate = self
|
||||||
}
|
}
|
||||||
|
|
||||||
func teardownMapView() {
|
func tearDownMapView() {
|
||||||
// Prevent a crash
|
// Prevent a crash
|
||||||
mapView.delegate = nil
|
mapView.delegate = nil
|
||||||
mapView = nil
|
mapView = nil
|
||||||
@@ -4,7 +4,7 @@ import UIKit
|
|||||||
|
|
||||||
// MARK: - UITableViewDelegate
|
// MARK: - UITableViewDelegate
|
||||||
|
|
||||||
extension ViewController: UITableViewDelegate {
|
extension MainViewController: UITableViewDelegate {
|
||||||
func setUpSearchView() {
|
func setUpSearchView() {
|
||||||
searchVC.loadViewIfNeeded()
|
searchVC.loadViewIfNeeded()
|
||||||
searchVC.tableView.delegate = self
|
searchVC.tableView.delegate = self
|
||||||
@@ -81,9 +81,6 @@ class SearchViewController: UIViewController, UITableViewDataSource {
|
|||||||
|
|
||||||
var items: [LocationItem] = []
|
var items: [LocationItem] = []
|
||||||
|
|
||||||
// For iOS 10 only
|
|
||||||
private lazy var shadowLayer: CAShapeLayer = CAShapeLayer()
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
tableView.dataSource = self
|
tableView.dataSource = self
|
||||||
@@ -92,29 +89,6 @@ class SearchViewController: UIViewController, UITableViewDataSource {
|
|||||||
hideHeader(animated: false)
|
hideHeader(animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLayoutSubviews() {
|
|
||||||
super.viewDidLayoutSubviews()
|
|
||||||
if #available(iOS 11, *) {
|
|
||||||
} else {
|
|
||||||
// Exmaple: Add rounding corners on iOS 10
|
|
||||||
visualEffectView.layer.cornerRadius = 9.0
|
|
||||||
visualEffectView.clipsToBounds = true
|
|
||||||
|
|
||||||
// Exmaple: Add shadow manually on iOS 10
|
|
||||||
view.layer.insertSublayer(shadowLayer, at: 0)
|
|
||||||
let rect = visualEffectView.frame
|
|
||||||
let path = UIBezierPath(roundedRect: rect,
|
|
||||||
byRoundingCorners: [.topLeft, .topRight],
|
|
||||||
cornerRadii: CGSize(width: 9.0, height: 9.0))
|
|
||||||
shadowLayer.frame = visualEffectView.frame
|
|
||||||
shadowLayer.shadowPath = path.cgPath
|
|
||||||
shadowLayer.shadowColor = UIColor.black.cgColor
|
|
||||||
shadowLayer.shadowOffset = CGSize(width: 0.0, height: 1.0)
|
|
||||||
shadowLayer.shadowOpacity = 0.2
|
|
||||||
shadowLayer.shadowRadius = 3.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return items.count
|
return items.count
|
||||||
}
|
}
|
||||||
@@ -130,16 +104,16 @@ class SearchViewController: UIViewController, UITableViewDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func showHeader(animated: Bool) {
|
func showHeader(animated: Bool) {
|
||||||
changeHeader(height: 116.0, aniamted: animated)
|
changeHeader(height: 116.0, animated: animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hideHeader(animated: Bool) {
|
func hideHeader(animated: Bool) {
|
||||||
changeHeader(height: 0.0, aniamted: animated)
|
changeHeader(height: 0.0, animated: animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func changeHeader(height: CGFloat, aniamted: Bool) {
|
private func changeHeader(height: CGFloat, animated: Bool) {
|
||||||
guard let headerView = tableView.tableHeaderView, headerView.bounds.height != height else { return }
|
guard let headerView = tableView.tableHeaderView, headerView.bounds.height != height else { return }
|
||||||
if aniamted == false {
|
if animated == false {
|
||||||
updateHeader(height: height)
|
updateHeader(height: height)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,40 +18,26 @@
|
|||||||
5442E24A25FC53C100A26F43 /* DebugTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5442E24925FC53C100A26F43 /* DebugTextViewController.swift */; };
|
5442E24A25FC53C100A26F43 /* DebugTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5442E24925FC53C100A26F43 /* DebugTextViewController.swift */; };
|
||||||
5442E25225FC541700A26F43 /* NestedScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5442E25125FC541700A26F43 /* NestedScrollViewController.swift */; };
|
5442E25225FC541700A26F43 /* NestedScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5442E25125FC541700A26F43 /* NestedScrollViewController.swift */; };
|
||||||
54496C59263A7E5A0031E0C8 /* UseCaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54496C58263A7E5A0031E0C8 /* UseCaseController.swift */; };
|
54496C59263A7E5A0031E0C8 /* UseCaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54496C58263A7E5A0031E0C8 /* UseCaseController.swift */; };
|
||||||
|
544BC56826CC918200D0A436 /* TableViewControllerForAdaptiveLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 544BC56726CC918200D0A436 /* TableViewControllerForAdaptiveLayout.swift */; };
|
||||||
545DB9EE21511E6300CA77B8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9ED21511E6300CA77B8 /* AppDelegate.swift */; };
|
545DB9EE21511E6300CA77B8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9ED21511E6300CA77B8 /* AppDelegate.swift */; };
|
||||||
545DB9F021511E6300CA77B8 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9EF21511E6300CA77B8 /* MainViewController.swift */; };
|
545DB9F021511E6300CA77B8 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9EF21511E6300CA77B8 /* MainViewController.swift */; };
|
||||||
545DB9F321511E6300CA77B8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F121511E6300CA77B8 /* Main.storyboard */; };
|
545DB9F321511E6300CA77B8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F121511E6300CA77B8 /* Main.storyboard */; };
|
||||||
545DB9F521511E6400CA77B8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F421511E6400CA77B8 /* Assets.xcassets */; };
|
545DB9F521511E6400CA77B8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F421511E6400CA77B8 /* Assets.xcassets */; };
|
||||||
545DB9F821511E6400CA77B8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F621511E6400CA77B8 /* LaunchScreen.storyboard */; };
|
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 */; };
|
546341A125C6415100CA0596 /* UseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546341A025C6415100CA0596 /* UseCase.swift */; };
|
||||||
546341AC25C6426500CA0596 /* CustomState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546341AB25C6426500CA0596 /* CustomState.swift */; };
|
546341AC25C6426500CA0596 /* CustomState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546341AB25C6426500CA0596 /* CustomState.swift */; };
|
||||||
549D23CB233C7779008EF4D7 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23CA233C7779008EF4D7 /* FloatingPanel.framework */; };
|
549D23CB233C7779008EF4D7 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23CA233C7779008EF4D7 /* FloatingPanel.framework */; };
|
||||||
549D23CC233C7779008EF4D7 /* FloatingPanel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23CA233C7779008EF4D7 /* FloatingPanel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
549D23CC233C7779008EF4D7 /* FloatingPanel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23CA233C7779008EF4D7 /* FloatingPanel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
54B51116216AFE5F0033A6F3 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B51115216AFE5F0033A6F3 /* Extensions.swift */; };
|
54B51116216AFE5F0033A6F3 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B51115216AFE5F0033A6F3 /* Extensions.swift */; };
|
||||||
54CDC5D8215BBE23007D205C /* Components.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D7215BBE23007D205C /* Components.swift */; };
|
54CDC5D8215BBE23007D205C /* SupplementaryViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D7215BBE23007D205C /* SupplementaryViews.swift */; };
|
||||||
54EAD35B263A75EB006A36EA /* Layouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EAD35A263A75EB006A36EA /* Layouts.swift */; };
|
54E58CB72BF8A8D900408EA9 /* CollectionViewControllerForAdaptiveLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E58CB62BF8A8D900408EA9 /* CollectionViewControllerForAdaptiveLayout.swift */; };
|
||||||
|
54EAD35B263A75EB006A36EA /* PanelLayouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EAD35A263A75EB006A36EA /* PanelLayouts.swift */; };
|
||||||
54EAD365263A765F006A36EA /* PagePanelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EAD364263A765F006A36EA /* PagePanelController.swift */; };
|
54EAD365263A765F006A36EA /* PagePanelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EAD364263A765F006A36EA /* PagePanelController.swift */; };
|
||||||
|
54F185822BF4AD0000916F57 /* DebugListCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54F185812BF4AD0000916F57 /* DebugListCollectionViewController.swift */; };
|
||||||
|
54F185842BF4B82E00916F57 /* UnavailableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54F185832BF4B82E00916F57 /* UnavailableViewController.swift */; };
|
||||||
|
5D82A6AD28D1843C006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6AC28D18438006A44BA /* libswiftCoreGraphics.tbd */; };
|
||||||
/* End PBXBuildFile section */
|
/* 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 */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
549D23CD233C7779008EF4D7 /* Embed Frameworks */ = {
|
549D23CD233C7779008EF4D7 /* Embed Frameworks */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
@@ -78,6 +64,7 @@
|
|||||||
5442E24925FC53C100A26F43 /* DebugTextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugTextViewController.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
54496C58263A7E5A0031E0C8 /* UseCaseController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UseCaseController.swift; sourceTree = "<group>"; };
|
||||||
|
544BC56726CC918200D0A436 /* TableViewControllerForAdaptiveLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewControllerForAdaptiveLayout.swift; sourceTree = "<group>"; };
|
||||||
545DB9EA21511E6300CA77B8 /* Samples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Samples.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
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>"; };
|
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>"; };
|
545DB9EF21511E6300CA77B8 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
|
||||||
@@ -85,19 +72,17 @@
|
|||||||
545DB9F421511E6400CA77B8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
549D23CA233C7779008EF4D7 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
54B51115216AFE5F0033A6F3 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
|
54B51115216AFE5F0033A6F3 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
|
||||||
54CDC5D7215BBE23007D205C /* Components.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Components.swift; sourceTree = "<group>"; };
|
54CDC5D7215BBE23007D205C /* SupplementaryViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupplementaryViews.swift; sourceTree = "<group>"; };
|
||||||
54EAD35A263A75EB006A36EA /* Layouts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layouts.swift; sourceTree = "<group>"; };
|
54E58CB62BF8A8D900408EA9 /* CollectionViewControllerForAdaptiveLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewControllerForAdaptiveLayout.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>"; };
|
54EAD364263A765F006A36EA /* PagePanelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagePanelController.swift; sourceTree = "<group>"; };
|
||||||
|
54F185812BF4AD0000916F57 /* DebugListCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugListCollectionViewController.swift; sourceTree = "<group>"; };
|
||||||
|
54F185832BF4B82E00916F57 /* UnavailableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnavailableViewController.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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -105,42 +90,31 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
5D82A6AD28D1843C006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
|
||||||
549D23CB233C7779008EF4D7 /* FloatingPanel.framework in Frameworks */,
|
549D23CB233C7779008EF4D7 /* FloatingPanel.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
545DB9FB21511E6400CA77B8 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
545DBA0621511E6400CA77B8 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
5442E22225FC519700A26F43 /* ViewControllers */ = {
|
5442E22225FC519700A26F43 /* ContentViewControllers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
54E58CB52BF8A88600408EA9 /* AdaptiveLayout */,
|
||||||
5442E23F25FC533800A26F43 /* DebugTableViewController.swift */,
|
5442E23F25FC533800A26F43 /* DebugTableViewController.swift */,
|
||||||
5442E24925FC53C100A26F43 /* DebugTextViewController.swift */,
|
5442E24925FC53C100A26F43 /* DebugTextViewController.swift */,
|
||||||
|
54F185812BF4AD0000916F57 /* DebugListCollectionViewController.swift */,
|
||||||
5442E23325FC528400A26F43 /* DetailViewController.swift */,
|
5442E23325FC528400A26F43 /* DetailViewController.swift */,
|
||||||
5442E24325FC538200A26F43 /* InspectorViewController.swift */,
|
5442E24325FC538200A26F43 /* InspectorViewController.swift */,
|
||||||
5442E22325FC51AF00A26F43 /* ImageViewController.swift */,
|
|
||||||
5442E25125FC541700A26F43 /* NestedScrollViewController.swift */,
|
5442E25125FC541700A26F43 /* NestedScrollViewController.swift */,
|
||||||
5442E23925FC52CD00A26F43 /* ModalViewController.swift */,
|
5442E23925FC52CD00A26F43 /* ModalViewController.swift */,
|
||||||
5442E22725FC51E200A26F43 /* MultiPanelController.swift */,
|
5442E22725FC51E200A26F43 /* MultiPanelController.swift */,
|
||||||
5442E22B25FC521F00A26F43 /* SettingsViewController.swift */,
|
5442E22B25FC521F00A26F43 /* SettingsViewController.swift */,
|
||||||
5442E22F25FC525200A26F43 /* TabBarViewController.swift */,
|
5442E22F25FC525200A26F43 /* TabBarViewController.swift */,
|
||||||
|
54F185832BF4B82E00916F57 /* UnavailableViewController.swift */,
|
||||||
);
|
);
|
||||||
path = ViewControllers;
|
path = ContentViewControllers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
545DB9E121511E6300CA77B8 = {
|
545DB9E121511E6300CA77B8 = {
|
||||||
@@ -148,9 +122,8 @@
|
|||||||
children = (
|
children = (
|
||||||
549D23CA233C7779008EF4D7 /* FloatingPanel.framework */,
|
549D23CA233C7779008EF4D7 /* FloatingPanel.framework */,
|
||||||
545DB9EC21511E6300CA77B8 /* Sources */,
|
545DB9EC21511E6300CA77B8 /* Sources */,
|
||||||
545DBA0121511E6400CA77B8 /* Tests */,
|
|
||||||
545DBA0C21511E6400CA77B8 /* UITests */,
|
|
||||||
545DB9EB21511E6300CA77B8 /* Products */,
|
545DB9EB21511E6300CA77B8 /* Products */,
|
||||||
|
5D82A6AB28D18438006A44BA /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -158,8 +131,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
545DB9EA21511E6300CA77B8 /* Samples.app */,
|
545DB9EA21511E6300CA77B8 /* Samples.app */,
|
||||||
545DB9FE21511E6400CA77B8 /* SamplesTests.xctest */,
|
|
||||||
545DBA0921511E6400CA77B8 /* SamplesUITests.xctest */,
|
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -172,45 +143,45 @@
|
|||||||
545DB9F121511E6300CA77B8 /* Main.storyboard */,
|
545DB9F121511E6300CA77B8 /* Main.storyboard */,
|
||||||
545DB9ED21511E6300CA77B8 /* AppDelegate.swift */,
|
545DB9ED21511E6300CA77B8 /* AppDelegate.swift */,
|
||||||
545DB9EF21511E6300CA77B8 /* MainViewController.swift */,
|
545DB9EF21511E6300CA77B8 /* MainViewController.swift */,
|
||||||
54496C58263A7E5A0031E0C8 /* UseCaseController.swift */,
|
|
||||||
546341AA25C6421000CA0596 /* UseCases */,
|
546341AA25C6421000CA0596 /* UseCases */,
|
||||||
5442E22225FC519700A26F43 /* ViewControllers */,
|
5442E22225FC519700A26F43 /* ContentViewControllers */,
|
||||||
54EAD35A263A75EB006A36EA /* Layouts.swift */,
|
54EAD35A263A75EB006A36EA /* PanelLayouts.swift */,
|
||||||
|
546341AB25C6426500CA0596 /* CustomState.swift */,
|
||||||
|
54CDC5D7215BBE23007D205C /* SupplementaryViews.swift */,
|
||||||
54B51115216AFE5F0033A6F3 /* Extensions.swift */,
|
54B51115216AFE5F0033A6F3 /* Extensions.swift */,
|
||||||
54CDC5D7215BBE23007D205C /* Components.swift */,
|
|
||||||
545DB9F921511E6400CA77B8 /* Info.plist */,
|
545DB9F921511E6400CA77B8 /* Info.plist */,
|
||||||
);
|
);
|
||||||
path = Sources;
|
path = Sources;
|
||||||
sourceTree = "<group>";
|
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 */ = {
|
546341AA25C6421000CA0596 /* UseCases */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
546341A025C6415100CA0596 /* UseCase.swift */,
|
546341A025C6415100CA0596 /* UseCase.swift */,
|
||||||
546341AB25C6426500CA0596 /* CustomState.swift */,
|
54496C58263A7E5A0031E0C8 /* UseCaseController.swift */,
|
||||||
54EAD364263A765F006A36EA /* PagePanelController.swift */,
|
54EAD364263A765F006A36EA /* PagePanelController.swift */,
|
||||||
);
|
);
|
||||||
path = UseCases;
|
path = UseCases;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
54E58CB52BF8A88600408EA9 /* AdaptiveLayout */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5442E22325FC51AF00A26F43 /* ImageViewController.swift */,
|
||||||
|
544BC56726CC918200D0A436 /* TableViewControllerForAdaptiveLayout.swift */,
|
||||||
|
54E58CB62BF8A8D900408EA9 /* CollectionViewControllerForAdaptiveLayout.swift */,
|
||||||
|
);
|
||||||
|
path = AdaptiveLayout;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
5D82A6AB28D18438006A44BA /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5D82A6AC28D18438006A44BA /* libswiftCoreGraphics.tbd */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -218,7 +189,6 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 545DBA1221511E6400CA77B8 /* Build configuration list for PBXNativeTarget "Samples" */;
|
buildConfigurationList = 545DBA1221511E6400CA77B8 /* Build configuration list for PBXNativeTarget "Samples" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
54D7209621D4DB970054A255 /* ShellScript */,
|
|
||||||
545DB9E621511E6300CA77B8 /* Sources */,
|
545DB9E621511E6300CA77B8 /* Sources */,
|
||||||
545DB9E721511E6300CA77B8 /* Frameworks */,
|
545DB9E721511E6300CA77B8 /* Frameworks */,
|
||||||
545DB9E821511E6300CA77B8 /* Resources */,
|
545DB9E821511E6300CA77B8 /* Resources */,
|
||||||
@@ -233,42 +203,6 @@
|
|||||||
productReference = 545DB9EA21511E6300CA77B8 /* Samples.app */;
|
productReference = 545DB9EA21511E6300CA77B8 /* Samples.app */;
|
||||||
productType = "com.apple.product-type.application";
|
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 */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
@@ -276,20 +210,12 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 1000;
|
LastSwiftUpdateCheck = 1000;
|
||||||
LastUpgradeCheck = 1000;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = scenee;
|
ORGANIZATIONNAME = scenee;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
545DB9E921511E6300CA77B8 = {
|
545DB9E921511E6300CA77B8 = {
|
||||||
CreatedOnToolsVersion = 10.0;
|
CreatedOnToolsVersion = 10.0;
|
||||||
};
|
};
|
||||||
545DB9FD21511E6400CA77B8 = {
|
|
||||||
CreatedOnToolsVersion = 10.0;
|
|
||||||
TestTargetID = 545DB9E921511E6300CA77B8;
|
|
||||||
};
|
|
||||||
545DBA0821511E6400CA77B8 = {
|
|
||||||
CreatedOnToolsVersion = 10.0;
|
|
||||||
TestTargetID = 545DB9E921511E6300CA77B8;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 545DB9E521511E6300CA77B8 /* Build configuration list for PBXProject "Samples" */;
|
buildConfigurationList = 545DB9E521511E6300CA77B8 /* Build configuration list for PBXProject "Samples" */;
|
||||||
@@ -306,8 +232,6 @@
|
|||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
545DB9E921511E6300CA77B8 /* Samples */,
|
545DB9E921511E6300CA77B8 /* Samples */,
|
||||||
545DB9FD21511E6400CA77B8 /* SamplesTests */,
|
|
||||||
545DBA0821511E6400CA77B8 /* SamplesUITests */,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@@ -323,100 +247,41 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
545DB9FC21511E6400CA77B8 /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
545DBA0721511E6400CA77B8 /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
|
||||||
54D7209621D4DB970054A255 /* ShellScript */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
outputFileListPaths = (
|
|
||||||
);
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $(git rev-parse --abbrev-ref HEAD)($(git rev-parse --short HEAD))\" \"$SRCROOT/$INFOPLIST_FILE\"\n";
|
|
||||||
};
|
|
||||||
/* End PBXShellScriptBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
545DB9E621511E6300CA77B8 /* Sources */ = {
|
545DB9E621511E6300CA77B8 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5442E23425FC528400A26F43 /* DetailViewController.swift in Sources */,
|
5442E23425FC528400A26F43 /* DetailViewController.swift in Sources */,
|
||||||
|
54E58CB72BF8A8D900408EA9 /* CollectionViewControllerForAdaptiveLayout.swift in Sources */,
|
||||||
54496C59263A7E5A0031E0C8 /* UseCaseController.swift in Sources */,
|
54496C59263A7E5A0031E0C8 /* UseCaseController.swift in Sources */,
|
||||||
54CDC5D8215BBE23007D205C /* Components.swift in Sources */,
|
54CDC5D8215BBE23007D205C /* SupplementaryViews.swift in Sources */,
|
||||||
54B51116216AFE5F0033A6F3 /* Extensions.swift in Sources */,
|
54B51116216AFE5F0033A6F3 /* Extensions.swift in Sources */,
|
||||||
|
544BC56826CC918200D0A436 /* TableViewControllerForAdaptiveLayout.swift in Sources */,
|
||||||
5442E24A25FC53C100A26F43 /* DebugTextViewController.swift in Sources */,
|
5442E24A25FC53C100A26F43 /* DebugTextViewController.swift in Sources */,
|
||||||
546341AC25C6426500CA0596 /* CustomState.swift in Sources */,
|
546341AC25C6426500CA0596 /* CustomState.swift in Sources */,
|
||||||
5442E23A25FC52CD00A26F43 /* ModalViewController.swift in Sources */,
|
5442E23A25FC52CD00A26F43 /* ModalViewController.swift in Sources */,
|
||||||
5442E22425FC51AF00A26F43 /* ImageViewController.swift in Sources */,
|
5442E22425FC51AF00A26F43 /* ImageViewController.swift in Sources */,
|
||||||
5442E24025FC533800A26F43 /* DebugTableViewController.swift in Sources */,
|
5442E24025FC533800A26F43 /* DebugTableViewController.swift in Sources */,
|
||||||
|
54F185822BF4AD0000916F57 /* DebugListCollectionViewController.swift in Sources */,
|
||||||
5442E25225FC541700A26F43 /* NestedScrollViewController.swift in Sources */,
|
5442E25225FC541700A26F43 /* NestedScrollViewController.swift in Sources */,
|
||||||
5442E22825FC51E200A26F43 /* MultiPanelController.swift in Sources */,
|
5442E22825FC51E200A26F43 /* MultiPanelController.swift in Sources */,
|
||||||
546341A125C6415100CA0596 /* UseCase.swift in Sources */,
|
546341A125C6415100CA0596 /* UseCase.swift in Sources */,
|
||||||
545DB9F021511E6300CA77B8 /* MainViewController.swift in Sources */,
|
545DB9F021511E6300CA77B8 /* MainViewController.swift in Sources */,
|
||||||
545DB9EE21511E6300CA77B8 /* AppDelegate.swift in Sources */,
|
545DB9EE21511E6300CA77B8 /* AppDelegate.swift in Sources */,
|
||||||
5442E23025FC525200A26F43 /* TabBarViewController.swift in Sources */,
|
5442E23025FC525200A26F43 /* TabBarViewController.swift in Sources */,
|
||||||
54EAD35B263A75EB006A36EA /* Layouts.swift in Sources */,
|
54EAD35B263A75EB006A36EA /* PanelLayouts.swift in Sources */,
|
||||||
54EAD365263A765F006A36EA /* PagePanelController.swift in Sources */,
|
54EAD365263A765F006A36EA /* PagePanelController.swift in Sources */,
|
||||||
5442E24425FC538200A26F43 /* InspectorViewController.swift in Sources */,
|
5442E24425FC538200A26F43 /* InspectorViewController.swift in Sources */,
|
||||||
5442E22C25FC521F00A26F43 /* SettingsViewController.swift in Sources */,
|
5442E22C25FC521F00A26F43 /* SettingsViewController.swift in Sources */,
|
||||||
);
|
54F185842BF4B82E00916F57 /* UnavailableViewController.swift in Sources */,
|
||||||
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;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* 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 */
|
/* Begin PBXVariantGroup section */
|
||||||
545DB9F121511E6300CA77B8 /* Main.storyboard */ = {
|
545DB9F121511E6300CA77B8 /* Main.storyboard */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
@@ -463,6 +328,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -524,6 +390,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -560,12 +427,16 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
INFOPLIST_FILE = Sources/Info.plist;
|
INFOPLIST_FILE = Sources/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelSample;
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SDKROOT)/usr/lib/swift",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = example.Samples;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
@@ -579,102 +450,22 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
INFOPLIST_FILE = Sources/Info.plist;
|
INFOPLIST_FILE = Sources/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelSample;
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SDKROOT)/usr/lib/swift",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = example.Samples;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
};
|
};
|
||||||
name = Release;
|
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 */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
@@ -696,24 +487,6 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
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 */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 545DB9E221511E6300CA77B8 /* Project object */;
|
rootObject = 545DB9E221511E6300CA77B8 /* Project object */;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1000"
|
LastUpgradeVersion = "1300"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -27,8 +27,6 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
@@ -38,8 +36,8 @@
|
|||||||
ReferencedContainer = "container:Samples.xcodeproj">
|
ReferencedContainer = "container:Samples.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
<Testables>
|
||||||
</AdditionalOptions>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@@ -61,8 +59,6 @@
|
|||||||
ReferencedContainer = "container:Samples.xcodeproj">
|
ReferencedContainer = "container:Samples.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" 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="32700.99.1234" 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>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="Stack View standard spacing" minToolsVersion="9.0"/>
|
<capability name="Stack View standard spacing" minToolsVersion="9.0"/>
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
@@ -13,7 +15,7 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<navigationController storyboardIdentifier="RootNavigationController" id="RoN-h0-uBD" sceneMemberID="viewController">
|
<navigationController storyboardIdentifier="RootNavigationController" id="RoN-h0-uBD" sceneMemberID="viewController">
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="hNW-5m-Omi">
|
<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"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
<connections>
|
<connections>
|
||||||
@@ -29,22 +31,22 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<viewController id="jF4-A0-Eq6" customClass="MainViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="jF4-A0-Eq6" customClass="MainViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="Smh-Bd-AAc">
|
<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"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<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">
|
<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"/>
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="M0G-C8-hAO" style="IBUITableViewCellStyleDefault" id="ySY-oA-g81">
|
<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"/>
|
<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">
|
<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"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="M0G-C8-hAO">
|
<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"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
@@ -85,7 +87,7 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<viewController storyboardIdentifier="SettingsViewController" id="C1X-9Z-TyQ" customClass="SettingsViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
<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">
|
<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"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillProportionally" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="n93-ZL-fmC">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillProportionally" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="n93-ZL-fmC">
|
||||||
@@ -123,7 +125,7 @@
|
|||||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="js8-Qv-lUC">
|
<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"/>
|
<rect key="frame" x="262" y="0.5" width="51" height="31"/>
|
||||||
<connections>
|
<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>
|
</connections>
|
||||||
</switch>
|
</switch>
|
||||||
</subviews>
|
</subviews>
|
||||||
@@ -140,7 +142,7 @@
|
|||||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="s6b-j9-8Kw">
|
<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"/>
|
<rect key="frame" x="262" y="0.5" width="51" height="31"/>
|
||||||
<connections>
|
<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>
|
</connections>
|
||||||
</switch>
|
</switch>
|
||||||
</subviews>
|
</subviews>
|
||||||
@@ -161,8 +163,8 @@
|
|||||||
</view>
|
</view>
|
||||||
<size key="freeformSize" width="375" height="197.33000000000001"/>
|
<size key="freeformSize" width="375" height="197.33000000000001"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="largeTitlesSwicth" destination="js8-Qv-lUC" id="FOm-6k-ffi"/>
|
<outlet property="largeTitlesSwitch" destination="js8-Qv-lUC" id="szl-pU-uRE"/>
|
||||||
<outlet property="translucentSwicth" destination="s6b-j9-8Kw" id="jmf-WH-bzZ"/>
|
<outlet property="translucentSwitch" destination="s6b-j9-8Kw" id="8yK-Du-jkM"/>
|
||||||
<outlet property="versionLabel" destination="WmC-Tq-NDN" id="Woh-kK-U0m"/>
|
<outlet property="versionLabel" destination="WmC-Tq-NDN" id="Woh-kK-U0m"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
@@ -175,11 +177,11 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<viewController id="RpE-lI-27a" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="RpE-lI-27a" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="JER-jz-KSq">
|
<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"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IvG-yp-yzI">
|
<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"/>
|
<state key="normal" title="Close"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="closeWithSender:" destination="RpE-lI-27a" eventType="touchUpInside" id="hj3-Xv-6Gq"/>
|
<action selector="closeWithSender:" destination="RpE-lI-27a" eventType="touchUpInside" id="hj3-Xv-6Gq"/>
|
||||||
@@ -206,11 +208,11 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<viewController id="pOk-Zm-vD9" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="pOk-Zm-vD9" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="85d-ub-G8k">
|
<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"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NbG-e8-HdI">
|
<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"/>
|
<state key="normal" title="Close"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="closeWithSender:" destination="pOk-Zm-vD9" eventType="touchUpInside" id="111-PD-Pop"/>
|
<action selector="closeWithSender:" destination="pOk-Zm-vD9" eventType="touchUpInside" id="111-PD-Pop"/>
|
||||||
@@ -237,11 +239,11 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<viewController id="lto-Zc-Vtp" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="lto-Zc-Vtp" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="ji9-Ez-N7i">
|
<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"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eFN-tN-4Ct">
|
<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"/>
|
<state key="normal" title="Close"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="closeWithSender:" destination="bYI-y3-Rzb" eventType="touchUpInside" id="YL4-GP-ZEZ"/>
|
<action selector="closeWithSender:" destination="bYI-y3-Rzb" eventType="touchUpInside" id="YL4-GP-ZEZ"/>
|
||||||
@@ -381,11 +383,11 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<viewController storyboardIdentifier="ModalViewController" id="bYI-y3-Rzb" customClass="ModalViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController storyboardIdentifier="ModalViewController" id="bYI-y3-Rzb" customClass="ModalViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="qwo-GK-p1U">
|
<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="768"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vut-mK-Y4t" customClass="SafeAreaView" customModule="Samples" customModuleProvider="target">
|
<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="768" width="375" height="0.0"/>
|
||||||
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
</view>
|
</view>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sbF-Az-7sy">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sbF-Az-7sy">
|
||||||
@@ -596,11 +598,11 @@
|
|||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<view alpha="0.5" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Kva-Z7-0qY" customClass="OnSafeAreaView" customModule="Samples" customModuleProvider="target">
|
<view alpha="0.5" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Kva-Z7-0qY" customClass="OnSafeAreaView" customModule="Samples" customModuleProvider="target">
|
||||||
<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="744"/>
|
||||||
<color key="backgroundColor" red="0.0078431372550000003" green="0.72156862749999995" blue="0.45882352939999999" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="backgroundColor" red="0.0078431372550000003" green="0.72156862749999995" blue="0.45882352939999999" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
</view>
|
</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">
|
<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>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="44" id="0jg-5D-A1F"/>
|
<constraint firstAttribute="height" constant="44" id="0jg-5D-A1F"/>
|
||||||
<constraint firstAttribute="width" constant="44" id="1Cq-PA-wgW"/>
|
<constraint firstAttribute="width" constant="44" id="1Cq-PA-wgW"/>
|
||||||
@@ -610,7 +612,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillProportionally" alignment="center" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="qux-uG-4o2">
|
<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>
|
<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">
|
<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"/>
|
<rect key="frame" x="0.0" y="5.5" width="91" height="20.5"/>
|
||||||
@@ -658,19 +660,19 @@
|
|||||||
<color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<gestureRecognizers/>
|
<gestureRecognizers/>
|
||||||
<constraints>
|
<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="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" priority="750" id="JOL-wC-w74"/>
|
||||||
<constraint firstItem="8yw-OC-Ubk" firstAttribute="bottom" secondItem="g7l-kO-y7q" secondAttribute="bottom" 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="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="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="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="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="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="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="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" constant="88" id="vKQ-h9-uKt"/>
|
<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"/>
|
<constraint firstItem="qux-uG-4o2" firstAttribute="leading" secondItem="g7l-kO-y7q" secondAttribute="leading" constant="8" id="zXb-R9-bMO"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<connections>
|
<connections>
|
||||||
@@ -759,14 +761,33 @@ Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
|
|||||||
<fontDescription key="fontDescription" name="CourierNewPSMT" family="Courier New" pointSize="14"/>
|
<fontDescription key="fontDescription" name="CourierNewPSMT" family="Courier New" pointSize="14"/>
|
||||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||||
</textView>
|
</textView>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillProportionally" alignment="center" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="wUo-kb-NIn">
|
||||||
|
<rect key="frame" x="159" y="16" width="200" height="31"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Expand top margin" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OC3-od-ldC">
|
||||||
|
<rect key="frame" x="0.0" y="5.5" width="143" height="20.5"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="XFC-sq-pWj">
|
||||||
|
<rect key="frame" x="151" y="0.0" width="51" height="31"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleTopMargin:" destination="tvD-nO-QUb" eventType="valueChanged" id="XWo-eX-0Jn"/>
|
||||||
|
</connections>
|
||||||
|
</switch>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<viewLayoutGuide key="safeArea" id="5ET-zC-lCb"/>
|
<viewLayoutGuide key="safeArea" id="5ET-zC-lCb"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="rN1-HL-YHv" firstAttribute="leading" secondItem="5ET-zC-lCb" secondAttribute="leading" id="7V3-KL-vXd"/>
|
<constraint firstItem="rN1-HL-YHv" firstAttribute="leading" secondItem="5ET-zC-lCb" secondAttribute="leading" id="7V3-KL-vXd"/>
|
||||||
|
<constraint firstItem="5ET-zC-lCb" firstAttribute="trailing" secondItem="wUo-kb-NIn" secondAttribute="trailing" constant="16" id="CtG-H5-tAI"/>
|
||||||
<constraint firstAttribute="bottom" secondItem="rN1-HL-YHv" secondAttribute="bottom" id="efD-U5-Tet"/>
|
<constraint firstAttribute="bottom" secondItem="rN1-HL-YHv" secondAttribute="bottom" id="efD-U5-Tet"/>
|
||||||
<constraint firstItem="rN1-HL-YHv" firstAttribute="top" secondItem="9YG-0j-Zzg" secondAttribute="top" constant="17" id="fiO-LL-nSC"/>
|
<constraint firstItem="rN1-HL-YHv" firstAttribute="top" secondItem="9YG-0j-Zzg" secondAttribute="top" constant="17" id="fiO-LL-nSC"/>
|
||||||
<constraint firstItem="rN1-HL-YHv" firstAttribute="trailing" secondItem="5ET-zC-lCb" secondAttribute="trailing" id="lfg-EE-euw"/>
|
<constraint firstItem="rN1-HL-YHv" firstAttribute="trailing" secondItem="5ET-zC-lCb" secondAttribute="trailing" id="lfg-EE-euw"/>
|
||||||
|
<constraint firstItem="wUo-kb-NIn" firstAttribute="top" secondItem="9YG-0j-Zzg" secondAttribute="top" constant="16" id="ogC-1W-upw"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<size key="freeformSize" width="375" height="778"/>
|
<size key="freeformSize" width="375" height="778"/>
|
||||||
@@ -778,9 +799,59 @@ Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
|
|||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="x1h-y1-h8q" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="x1h-y1-h8q" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-1" y="734"/>
|
<point key="canvasLocation" x="-2.1739130434782612" y="733.92857142857144"/>
|
||||||
|
</scene>
|
||||||
|
<!--Table View Controller For Adaptive Layout-->
|
||||||
|
<scene sceneID="rDI-lU-wEx">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="TableViewControllerForAdaptiveLayout" id="5nC-6E-bXf" customClass="TableViewControllerForAdaptiveLayout" 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>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
|
<designables>
|
||||||
|
<designable name="noi-1a-5bZ">
|
||||||
|
<size key="intrinsicContentSize" width="30" height="30"/>
|
||||||
|
</designable>
|
||||||
|
</designables>
|
||||||
<inferredMetricsTieBreakers>
|
<inferredMetricsTieBreakers>
|
||||||
<segue reference="r1P-2i-NDe"/>
|
<segue reference="r1P-2i-NDe"/>
|
||||||
</inferredMetricsTieBreakers>
|
</inferredMetricsTieBreakers>
|
||||||
@@ -790,13 +861,13 @@ Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
|
|||||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
<systemColor name="systemOrangeColor">
|
<systemColor name="systemOrangeColor">
|
||||||
<color red="1" green="0.58431372549019611" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="1" green="0.58431372550000005" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
<systemColor name="systemPurpleColor">
|
<systemColor name="systemPurpleColor">
|
||||||
<color red="0.68627450980392157" green="0.32156862745098042" blue="0.87058823529411766" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.68627450980000004" green="0.32156862749999998" blue="0.87058823529999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
<systemColor name="systemTealColor">
|
<systemColor name="systemTealColor">
|
||||||
<color red="0.35294117647058826" green="0.78431372549019607" blue="0.98039215686274506" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.18823529410000001" green="0.69019607839999997" blue="0.78039215689999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import FloatingPanel
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
class CollectionViewControllerForAdaptiveLayout: UIViewController {
|
||||||
|
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
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LayoutType {
|
||||||
|
case flow
|
||||||
|
case compositional
|
||||||
|
}
|
||||||
|
|
||||||
|
weak var collectionView: UICollectionView!
|
||||||
|
var layoutType: LayoutType = .flow
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
setupCollectionView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupCollectionView() {
|
||||||
|
let collectionViewLayout = {
|
||||||
|
switch layoutType {
|
||||||
|
case .flow:
|
||||||
|
CollectionViewLayoutFactory.flowLayout
|
||||||
|
case .compositional:
|
||||||
|
CollectionViewLayoutFactory.compositionalLayout
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
let collectionView = IntrinsicCollectionView(
|
||||||
|
frame: .zero,
|
||||||
|
collectionViewLayout: collectionViewLayout
|
||||||
|
)
|
||||||
|
|
||||||
|
collectionView.delegate = self
|
||||||
|
collectionView.dataSource = self
|
||||||
|
collectionView.backgroundColor = .yellow
|
||||||
|
collectionView.register(Cell.self, forCellWithReuseIdentifier: Cell.reuseIdentifier)
|
||||||
|
|
||||||
|
view.addSubview(collectionView)
|
||||||
|
self.collectionView = collectionView
|
||||||
|
|
||||||
|
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||||
|
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||||
|
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||||
|
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
extension CollectionViewControllerForAdaptiveLayout: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return 5 // Only three cells needed to fill the space
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Cell.reuseIdentifier, for: indexPath) as! Cell
|
||||||
|
cell.configure(text: "Item \(indexPath.row)")
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||||
|
let width = collectionView.frame.width
|
||||||
|
return CGSize(width: width, height: 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
extension CollectionViewControllerForAdaptiveLayout {
|
||||||
|
enum CollectionViewLayoutFactory {
|
||||||
|
static var flowLayout: UICollectionViewLayout {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
layout.scrollDirection = .vertical
|
||||||
|
layout.minimumLineSpacing = 8 // Vertical spacing between rows
|
||||||
|
return layout
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
static var compositionalLayout: UICollectionViewLayout {
|
||||||
|
UICollectionViewCompositionalLayout { (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
|
||||||
|
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(100))
|
||||||
|
let item = NSCollectionLayoutItem(layoutSize: itemSize)
|
||||||
|
|
||||||
|
let group = NSCollectionLayoutGroup.horizontal(layoutSize: itemSize, subitems: [item])
|
||||||
|
|
||||||
|
let section = NSCollectionLayoutSection(group: group)
|
||||||
|
section.interGroupSpacing = 8 // Spacing between each group/item
|
||||||
|
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
|
||||||
|
|
||||||
|
return section
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final class Cell: UICollectionViewCell {
|
||||||
|
static let reuseIdentifier = "Cell"
|
||||||
|
|
||||||
|
private let label: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.textAlignment = .center
|
||||||
|
label.textColor = .white
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
commonInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func commonInit() {
|
||||||
|
backgroundColor = .systemBlue
|
||||||
|
addSubview(label)
|
||||||
|
label.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
label.centerXAnchor.constraint(equalTo: centerXAnchor),
|
||||||
|
label.centerYAnchor.constraint(equalTo: centerYAnchor)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(text: String) {
|
||||||
|
label.text = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class IntrinsicCollectionView: UICollectionView {
|
||||||
|
override public var contentSize: CGSize {
|
||||||
|
didSet {
|
||||||
|
invalidateIntrinsicContentSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public var intrinsicContentSize: CGSize {
|
||||||
|
layoutIfNeeded()
|
||||||
|
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
-19
@@ -5,29 +5,25 @@ import FloatingPanel
|
|||||||
|
|
||||||
final class ImageViewController: UIViewController {
|
final class ImageViewController: UIViewController {
|
||||||
class PanelLayout: FloatingPanelLayout {
|
class PanelLayout: FloatingPanelLayout {
|
||||||
weak var targetGuide: UILayoutGuide?
|
private unowned var targetGuide: UILayoutGuide
|
||||||
init(targetGuide: UILayoutGuide?) {
|
init(targetGuide: UILayoutGuide) {
|
||||||
self.targetGuide = targetGuide
|
self.targetGuide = targetGuide
|
||||||
}
|
}
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||||
if #available(iOS 11.0, *), let targetGuide = targetGuide {
|
return [
|
||||||
return [
|
.full: FloatingPanelAdaptiveLayoutAnchor(
|
||||||
.full: FloatingPanelAdaptiveLayoutAnchor(absoluteOffset: 0,
|
absoluteOffset: 0,
|
||||||
contentLayout: targetGuide,
|
contentLayout: targetGuide,
|
||||||
referenceGuide: .superview),
|
referenceGuide: .superview
|
||||||
.half: FloatingPanelAdaptiveLayoutAnchor(fractionalOffset: 0.5,
|
),
|
||||||
contentLayout: targetGuide,
|
.half: FloatingPanelAdaptiveLayoutAnchor(
|
||||||
referenceGuide: .superview)
|
fractionalOffset: 0.5,
|
||||||
]
|
contentLayout: targetGuide,
|
||||||
} else {
|
referenceGuide: .superview
|
||||||
return [
|
)
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 500,
|
]
|
||||||
edge: .bottom,
|
|
||||||
referenceGuide: .superview)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +37,6 @@ final class ImageViewController: UIViewController {
|
|||||||
case withHeaderFooter
|
case withHeaderFooter
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS 11.0, *)
|
|
||||||
func layoutGuideFor(mode: Mode) -> UILayoutGuide {
|
func layoutGuideFor(mode: Mode) -> UILayoutGuide {
|
||||||
switch mode {
|
switch mode {
|
||||||
case .onlyImage:
|
case .onlyImage:
|
||||||
+88
@@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import FloatingPanel
|
||||||
|
|
||||||
|
final class TableViewControllerForAdaptiveLayout: 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
@available(iOS 14, *)
|
||||||
|
class DebugListCollectionViewController: UIViewController {
|
||||||
|
|
||||||
|
enum Section {
|
||||||
|
case main
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataSource: UICollectionViewDiffableDataSource<Section, Int>! = nil
|
||||||
|
var collectionView: UICollectionView! = nil
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
navigationItem.title = "List"
|
||||||
|
configureHierarchy()
|
||||||
|
configureDataSource()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 14, *)
|
||||||
|
extension DebugListCollectionViewController {
|
||||||
|
/// - Tag: List
|
||||||
|
private func createLayout() -> UICollectionViewLayout {
|
||||||
|
var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
|
||||||
|
config.trailingSwipeActionsConfigurationProvider = { indexPath -> UISwipeActionsConfiguration? in
|
||||||
|
return UISwipeActionsConfiguration(
|
||||||
|
actions: [UIContextualAction(
|
||||||
|
style: .destructive,
|
||||||
|
title: "Delete",
|
||||||
|
handler: { _, _, completion in
|
||||||
|
// Do nothing now
|
||||||
|
}
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return UICollectionViewCompositionalLayout.list(using: config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 14, *)
|
||||||
|
extension DebugListCollectionViewController {
|
||||||
|
private func configureHierarchy() {
|
||||||
|
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
|
||||||
|
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
view.addSubview(collectionView)
|
||||||
|
collectionView.delegate = self
|
||||||
|
}
|
||||||
|
private func configureDataSource() {
|
||||||
|
|
||||||
|
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { (cell, indexPath, item) in
|
||||||
|
var content = cell.defaultContentConfiguration()
|
||||||
|
content.text = "\(item)"
|
||||||
|
cell.contentConfiguration = content
|
||||||
|
}
|
||||||
|
|
||||||
|
dataSource = UICollectionViewDiffableDataSource<Section, Int>(collectionView: collectionView) {
|
||||||
|
(collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -> UICollectionViewCell? in
|
||||||
|
|
||||||
|
return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Int>()
|
||||||
|
snapshot.appendSections([.main])
|
||||||
|
snapshot.appendItems(Array(0..<94))
|
||||||
|
dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 14, *)
|
||||||
|
extension DebugListCollectionViewController: UICollectionViewDelegate {
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
collectionView.deselectItem(at: indexPath, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
-33
@@ -18,7 +18,7 @@ class DebugTableViewController: InspectableViewController {
|
|||||||
stackView.axis = .vertical
|
stackView.axis = .vertical
|
||||||
stackView.distribution = .fillEqually
|
stackView.distribution = .fillEqually
|
||||||
stackView.alignment = .trailing
|
stackView.alignment = .trailing
|
||||||
stackView.spacing = 10.0
|
stackView.spacing = 4
|
||||||
return stackView
|
return stackView
|
||||||
}()
|
}()
|
||||||
private lazy var reorderButton: UIButton = {
|
private lazy var reorderButton: UIButton = {
|
||||||
@@ -28,39 +28,48 @@ class DebugTableViewController: InspectableViewController {
|
|||||||
button.addTarget(self, action: #selector(reorderItems), for: .touchUpInside)
|
button.addTarget(self, action: #selector(reorderItems), for: .touchUpInside)
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
private lazy var trackingSwitchWrapper: UIStackView = {
|
private lazy var trackingButton: UIButton = {
|
||||||
let stackView = UIStackView()
|
let button = UIButton()
|
||||||
stackView.axis = .horizontal
|
button.setTitle(Menu.trackScrolling.rawValue, for: .normal)
|
||||||
stackView.distribution = .fillProportionally
|
button.setTitleColor(view.tintColor, for: .normal)
|
||||||
stackView.alignment = .center
|
button.addTarget(self, action: #selector(toggleTrackingScroll), for: .touchUpInside)
|
||||||
stackView.spacing = 8.0
|
return button
|
||||||
stackView.addArrangedSubview(trackingLabel)
|
|
||||||
stackView.addArrangedSubview(trackingSwitch)
|
|
||||||
return stackView
|
|
||||||
}()
|
}()
|
||||||
private lazy var trackingLabel: UILabel = {
|
private lazy var followingButton: UIButton = {
|
||||||
let label = UILabel()
|
let button = UIButton()
|
||||||
label.text = "Tracking"
|
button.setTitleColor(view.tintColor, for: .normal)
|
||||||
label.font = UIFont.systemFont(ofSize: 17.0, weight: .regular)
|
button.addTarget(self, action: #selector(toggleFollowingScroll), for: .touchUpInside)
|
||||||
return label
|
return button
|
||||||
}()
|
|
||||||
private lazy var trackingSwitch: UISwitch = {
|
|
||||||
let trackingSwitch = UISwitch()
|
|
||||||
trackingSwitch.isOn = true
|
|
||||||
trackingSwitch.addTarget(self, action: #selector(turnTrackingOn), for: .touchUpInside)
|
|
||||||
return trackingSwitch
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
|
|
||||||
|
var kvoObservers: [NSKeyValueObservation] = []
|
||||||
|
|
||||||
|
private var fpc: FloatingPanelController? { parent as? FloatingPanelController }
|
||||||
private lazy var items: [String] = {
|
private lazy var items: [String] = {
|
||||||
let items = (0..<100).map { "Items \($0)" }
|
let items = (0..<100).map { "Items \($0)" }
|
||||||
return Command.replace(items: items)
|
return Command.replace(items: items)
|
||||||
}()
|
}()
|
||||||
private var itemHeight: CGFloat = 66.0
|
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 {
|
enum Menu: String, CaseIterable {
|
||||||
case turnOffTracking = "Tracking"
|
case trackScrolling = "Tracking"
|
||||||
|
case followScrolling = "Following"
|
||||||
case reorder = "Reorder"
|
case reorder = "Reorder"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,12 +96,12 @@ class DebugTableViewController: InspectableViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func execute(for vc: DebugTableViewController) {
|
func execute(for vc: DebugTableViewController, sourceView: UIView) {
|
||||||
switch self {
|
switch self {
|
||||||
case .animateScroll:
|
case .animateScroll:
|
||||||
vc.animateScroll()
|
vc.animateScroll()
|
||||||
case .changeContentSize:
|
case .changeContentSize:
|
||||||
vc.changeContentSize()
|
vc.changeContentSize(sourceView: sourceView)
|
||||||
case .moveToFull:
|
case .moveToFull:
|
||||||
vc.moveToFull()
|
vc.moveToFull()
|
||||||
case .moveToHalf:
|
case .moveToHalf:
|
||||||
@@ -135,13 +144,19 @@ class DebugTableViewController: InspectableViewController {
|
|||||||
switch menu {
|
switch menu {
|
||||||
case .reorder:
|
case .reorder:
|
||||||
buttonStackView.addArrangedSubview(reorderButton)
|
buttonStackView.addArrangedSubview(reorderButton)
|
||||||
case .turnOffTracking:
|
case .trackScrolling:
|
||||||
buttonStackView.addArrangedSubview(trackingSwitchWrapper)
|
buttonStackView.addArrangedSubview(trackingButton)
|
||||||
|
case .followScrolling:
|
||||||
|
buttonStackView.addArrangedSubview(followingButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Set titles
|
||||||
|
tracksScrollView = true
|
||||||
|
followsScrollViewBouncing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Menu
|
// MARK: - Menu
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
private func reorderItems() {
|
private func reorderItems() {
|
||||||
if reorderButton.titleLabel?.text == Menu.reorder.rawValue {
|
if reorderButton.titleLabel?.text == Menu.reorder.rawValue {
|
||||||
@@ -154,19 +169,25 @@ class DebugTableViewController: InspectableViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
private func turnTrackingOn(_ sender: UISwitch) {
|
private func toggleTrackingScroll() {
|
||||||
guard let fpc = self.parent as? FloatingPanelController else { return }
|
tracksScrollView.toggle()
|
||||||
if sender.isOn {
|
guard let fpc = fpc else { return }
|
||||||
|
if tracksScrollView {
|
||||||
fpc.track(scrollView: tableView)
|
fpc.track(scrollView: tableView)
|
||||||
} else {
|
} else {
|
||||||
fpc.untrack(scrollView: tableView)
|
fpc.untrack(scrollView: tableView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
private func toggleFollowingScroll() {
|
||||||
|
followsScrollViewBouncing.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Actions
|
// MARK: - Actions
|
||||||
|
|
||||||
private func execute(command: Command) {
|
private func execute(command: Command, sourceView: UIView) {
|
||||||
command.execute(for: self)
|
command.execute(for: self, sourceView: sourceView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
@@ -177,7 +198,7 @@ class DebugTableViewController: InspectableViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
private func changeContentSize() {
|
private func changeContentSize(sourceView: UIView) {
|
||||||
let actionSheet = UIAlertController(title: "Change content size", message: "", preferredStyle: .actionSheet)
|
let actionSheet = UIAlertController(title: "Change content size", message: "", preferredStyle: .actionSheet)
|
||||||
actionSheet.addAction(UIAlertAction(title: "Large", style: .default, handler: { (_) in
|
actionSheet.addAction(UIAlertAction(title: "Large", style: .default, handler: { (_) in
|
||||||
self.itemHeight = 66.0
|
self.itemHeight = 66.0
|
||||||
@@ -201,6 +222,11 @@ class DebugTableViewController: InspectableViewController {
|
|||||||
self.changeItems(3)
|
self.changeItems(3)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
if let popoverController = actionSheet.popoverPresentationController {
|
||||||
|
popoverController.sourceView = sourceView
|
||||||
|
popoverController.sourceRect = sourceView.bounds
|
||||||
|
}
|
||||||
|
|
||||||
self.present(actionSheet, animated: true, completion: nil)
|
self.present(actionSheet, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,13 +270,17 @@ extension DebugTableViewController: UITableViewDataSource {
|
|||||||
|
|
||||||
extension DebugTableViewController: UITableViewDelegate {
|
extension DebugTableViewController: UITableViewDelegate {
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
|
if followsScrollViewBouncing {
|
||||||
|
fpc?.followScrollViewBouncing()
|
||||||
|
}
|
||||||
print("TableView --- ", scrollView.contentOffset, scrollView.contentInset)
|
print("TableView --- ", scrollView.contentOffset, scrollView.contentInset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
print("DebugTableViewController -- select row \(indexPath.row)")
|
print("DebugTableViewController -- select row \(indexPath.row)")
|
||||||
guard let action = Command(rawValue: indexPath.row) else { return }
|
guard let action = Command(rawValue: indexPath.row) else { return }
|
||||||
execute(command: action)
|
let cell = tableView.cellForRow(at: indexPath)
|
||||||
|
execute(command: action, sourceView: cell ?? tableView)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
|
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
|
||||||
+9
-5
@@ -11,9 +11,7 @@ final class DebugTextViewController: UIViewController, UITextViewDelegate {
|
|||||||
textView.delegate = self
|
textView.delegate = self
|
||||||
print("viewDidLoad: TextView --- ", textView.contentOffset, textView.contentInset)
|
print("viewDidLoad: TextView --- ", textView.contentOffset, textView.contentInset)
|
||||||
|
|
||||||
if #available(iOS 11.0, *) {
|
textView.contentInsetAdjustmentBehavior = .never
|
||||||
textView.contentInsetAdjustmentBehavior = .never
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillLayoutSubviews() {
|
override func viewWillLayoutSubviews() {
|
||||||
@@ -32,8 +30,14 @@ final class DebugTextViewController: UIViewController, UITextViewDelegate {
|
|||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
print("TextView --- ", scrollView.contentOffset, scrollView.contentInset)
|
print("TextView --- ", scrollView.contentOffset, scrollView.contentInset)
|
||||||
if #available(iOS 11.0, *) {
|
print("TextView --- ", scrollView.adjustedContentInset)
|
||||||
print("TextView --- ", scrollView.adjustedContentInset)
|
}
|
||||||
|
|
||||||
|
@IBAction func toggleTopMargin(_ sender: UISwitch) {
|
||||||
|
if sender.isOn {
|
||||||
|
textViewTopConstraint.constant = 160
|
||||||
|
} else {
|
||||||
|
textViewTopConstraint.constant = 16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+7
-10
@@ -56,7 +56,6 @@ final class ModalViewController: UIViewController, FloatingPanelControllerDelega
|
|||||||
@IBAction func updateLayout(_ sender: Any) {
|
@IBAction func updateLayout(_ sender: Any) {
|
||||||
isNewlayout = !isNewlayout
|
isNewlayout = !isNewlayout
|
||||||
UIView.animate(withDuration: 0.5) {
|
UIView.animate(withDuration: 0.5) {
|
||||||
self.fpc.layout = (self.isNewlayout) ? ModalSecondLayout() : FloatingPanelBottomLayout()
|
|
||||||
self.fpc.invalidateLayout()
|
self.fpc.invalidateLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,14 +65,12 @@ final class ModalViewController: UIViewController, FloatingPanelControllerDelega
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ModalSecondLayout: FloatingPanelLayout {
|
class ModalSecondLayout: FloatingPanelLayout {
|
||||||
var position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
var initialState: FloatingPanelState { .half }
|
let initialState: FloatingPanelState = .half
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 262, edge: .top, referenceGuide: .safeArea),
|
||||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 262, edge: .top, referenceGuide: .safeArea),
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea)
|
||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea)
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-5
@@ -57,11 +57,9 @@ final class MultiPanelController: FloatingPanelController, FloatingPanelControll
|
|||||||
private final class FirstViewLayout: FloatingPanelLayout {
|
private final class FirstViewLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 40.0, edge: .top, referenceGuide: .superview)
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 40.0, edge: .top, referenceGuide: .superview)
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import FloatingPanel
|
||||||
|
|
||||||
|
final class SettingsViewController: InspectableViewController {
|
||||||
|
@IBOutlet weak var largeTitlesSwitch: UISwitch!
|
||||||
|
@IBOutlet weak var translucentSwitch: UISwitch!
|
||||||
|
@IBOutlet weak var versionLabel: UILabel!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
versionLabel.text = "Version: \(Bundle.main.infoDictionary?["CFBundleVersion"] ?? "--")"
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLayoutSubviews() {
|
||||||
|
super.viewDidLayoutSubviews()
|
||||||
|
let prefersLargeTitles = navigationController!.navigationBar.prefersLargeTitles
|
||||||
|
largeTitlesSwitch.setOn(prefersLargeTitles, animated: false)
|
||||||
|
|
||||||
|
let isTranslucent = navigationController!.navigationBar.isTranslucent
|
||||||
|
translucentSwitch.setOn(isTranslucent, animated: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func toggleLargeTitle(_ sender: UISwitch) {
|
||||||
|
navigationController?.navigationBar.prefersLargeTitles = sender.isOn
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func toggleTranslucent(_ sender: UISwitch) {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+12
-15
@@ -41,6 +41,10 @@ final class TabBarContentViewController: UIViewController {
|
|||||||
fpc.addPanel(toParent: self)
|
fpc.addPanel(toParent: self)
|
||||||
|
|
||||||
|
|
||||||
|
if #available(iOS 15, *) {
|
||||||
|
tabBarController?.tabBar.scrollEdgeAppearance = UITabBarAppearance()
|
||||||
|
}
|
||||||
|
|
||||||
switch tabBarItem.tag {
|
switch tabBarItem.tag {
|
||||||
case 1:
|
case 1:
|
||||||
fpc.behavior = TwoTabBarPanelBehavior()
|
fpc.behavior = TwoTabBarPanelBehavior()
|
||||||
@@ -191,14 +195,12 @@ class OneTabBarPanelLayout: FloatingPanelLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TwoTabBarPanelLayout: FloatingPanelLayout {
|
class TwoTabBarPanelLayout: FloatingPanelLayout {
|
||||||
var initialState: FloatingPanelState { .half }
|
let initialState: FloatingPanelState = .half
|
||||||
var position: FloatingPanelPosition { .bottom }
|
let position: FloatingPanelPosition = .bottom
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 261.0, edge: .bottom, referenceGuide: .safeArea)
|
||||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 261.0, edge: .bottom, referenceGuide: .safeArea)
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class TwoTabBarPanelBehavior: FloatingPanelBehavior {
|
class TwoTabBarPanelBehavior: FloatingPanelBehavior {
|
||||||
@@ -236,13 +238,8 @@ class ThreeTabBarPanelLayout: FloatingPanelLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
||||||
if #available(iOS 11.0, *) {
|
leftConstraint = surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 0.0)
|
||||||
leftConstraint = surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 0.0)
|
rightConstraint = surfaceView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: 0.0)
|
||||||
rightConstraint = surfaceView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: 0.0)
|
|
||||||
} else {
|
|
||||||
leftConstraint = surfaceView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0.0)
|
|
||||||
rightConstraint = surfaceView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0.0)
|
|
||||||
}
|
|
||||||
return [ leftConstraint, rightConstraint ]
|
return [ leftConstraint, rightConstraint ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class UnavailableViewController: UIViewController {
|
||||||
|
weak var label: UILabel!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
let label = UILabel()
|
||||||
|
label.text = "Unavailable content"
|
||||||
|
label.numberOfLines = 0
|
||||||
|
label.textAlignment = .center
|
||||||
|
label.frame = view.bounds
|
||||||
|
label.autoresizingMask = [
|
||||||
|
.flexibleTopMargin,
|
||||||
|
.flexibleLeftMargin,
|
||||||
|
.flexibleBottomMargin,
|
||||||
|
.flexibleRightMargin
|
||||||
|
]
|
||||||
|
view.addSubview(label)
|
||||||
|
self.label = label
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,20 @@
|
|||||||
|
|
||||||
import UIKit
|
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 {
|
protocol LayoutGuideProvider {
|
||||||
var topAnchor: NSLayoutYAxisAnchor { get }
|
var topAnchor: NSLayoutYAxisAnchor { get }
|
||||||
var bottomAnchor: NSLayoutYAxisAnchor { get }
|
var bottomAnchor: NSLayoutYAxisAnchor { get }
|
||||||
@@ -19,22 +33,10 @@ class CustomLayoutGuide: LayoutGuideProvider {
|
|||||||
|
|
||||||
extension UIViewController {
|
extension UIViewController {
|
||||||
var layoutInsets: UIEdgeInsets {
|
var layoutInsets: UIEdgeInsets {
|
||||||
if #available(iOS 11.0, *) {
|
return view.safeAreaInsets
|
||||||
return view.safeAreaInsets
|
|
||||||
} else {
|
|
||||||
return UIEdgeInsets(top: topLayoutGuide.length,
|
|
||||||
left: 0.0,
|
|
||||||
bottom: bottomLayoutGuide.length,
|
|
||||||
right: 0.0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var layoutGuide: LayoutGuideProvider {
|
var layoutGuide: LayoutGuideProvider {
|
||||||
if #available(iOS 11.0, *) {
|
return view.safeAreaLayoutGuide
|
||||||
return view!.safeAreaLayoutGuide
|
|
||||||
} else {
|
|
||||||
return CustomLayoutGuide(topAnchor: topLayoutGuide.bottomAnchor,
|
|
||||||
bottomAnchor: bottomLayoutGuide.topAnchor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,24 +6,20 @@ import FloatingPanel
|
|||||||
final class MainViewController: UIViewController {
|
final class MainViewController: UIViewController {
|
||||||
@IBOutlet weak var tableView: UITableView!
|
@IBOutlet weak var tableView: UITableView!
|
||||||
private var observations: [NSKeyValueObservation] = []
|
private var observations: [NSKeyValueObservation] = []
|
||||||
|
|
||||||
private lazy var useCaseController = UseCaseController(mainVC: self)
|
private lazy var useCaseController = UseCaseController(mainVC: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MainViewController {
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
tableView.dataSource = self
|
tableView.dataSource = self
|
||||||
tableView.delegate = self
|
tableView.delegate = self
|
||||||
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
|
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
|
||||||
automaticallyAdjustsScrollViewInsets = false
|
|
||||||
|
|
||||||
let searchController = UISearchController(searchResultsController: nil)
|
let searchController = UISearchController(searchResultsController: nil)
|
||||||
if #available(iOS 11.0, *) {
|
navigationItem.searchController = searchController
|
||||||
navigationItem.searchController = searchController
|
navigationItem.hidesSearchBarWhenScrolling = false
|
||||||
navigationItem.hidesSearchBarWhenScrolling = false
|
navigationItem.largeTitleDisplayMode = .automatic
|
||||||
navigationItem.largeTitleDisplayMode = .automatic
|
|
||||||
} else {
|
|
||||||
// Fallback on earlier versions
|
|
||||||
}
|
|
||||||
var insets = UIEdgeInsets.zero
|
var insets = UIEdgeInsets.zero
|
||||||
insets.bottom += 69.0
|
insets.bottom += 69.0
|
||||||
tableView.contentInset = insets
|
tableView.contentInset = insets
|
||||||
@@ -34,12 +30,10 @@ final class MainViewController: UIViewController {
|
|||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
if #available(iOS 11.0, *) {
|
if let observation = navigationController?.navigationBar.observe(\.prefersLargeTitles, changeHandler: { (bar, _) in
|
||||||
if let observation = navigationController?.navigationBar.observe(\.prefersLargeTitles, changeHandler: { (bar, _) in
|
self.tableView.reloadData()
|
||||||
self.tableView.reloadData()
|
}) {
|
||||||
}) {
|
observations.append(observation)
|
||||||
observations.append(observation)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +41,9 @@ final class MainViewController: UIViewController {
|
|||||||
super.viewWillDisappear(animated)
|
super.viewWillDisappear(animated)
|
||||||
observations.removeAll()
|
observations.removeAll()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK:- Actions
|
extension MainViewController {
|
||||||
@IBAction func showDebugMenu(_ sender: UIBarButtonItem) {
|
@IBAction func showDebugMenu(_ sender: UIBarButtonItem) {
|
||||||
useCaseController.setUpSettingsPanel(for: self)
|
useCaseController.setUpSettingsPanel(for: self)
|
||||||
}
|
}
|
||||||
@@ -56,12 +51,8 @@ final class MainViewController: UIViewController {
|
|||||||
|
|
||||||
extension MainViewController: UITableViewDataSource {
|
extension MainViewController: UITableViewDataSource {
|
||||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
if #available(iOS 11.0, *) {
|
if navigationController?.navigationBar.prefersLargeTitles == true {
|
||||||
if navigationController?.navigationBar.prefersLargeTitles == true {
|
return UseCase.allCases.count + 30
|
||||||
return UseCase.allCases.count + 30
|
|
||||||
} else {
|
|
||||||
return UseCase.allCases.count
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return UseCase.allCases.count
|
return UseCase.allCases.count
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-26
@@ -23,14 +23,11 @@ extension MainViewController: FloatingPanelLayout {
|
|||||||
class TopPositionedPanelLayout: FloatingPanelLayout {
|
class TopPositionedPanelLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .top
|
let position: FloatingPanelPosition = .top
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 88.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
return [
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .top, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 88.0, edge: .bottom, referenceGuide: .safeArea),
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .top, referenceGuide: .safeArea)
|
||||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .top, referenceGuide: .safeArea),
|
]
|
||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .top, referenceGuide: .safeArea)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class IntrinsicPanelLayout: FloatingPanelBottomLayout {
|
class IntrinsicPanelLayout: FloatingPanelBottomLayout {
|
||||||
@@ -45,13 +42,10 @@ class IntrinsicPanelLayout: FloatingPanelBottomLayout {
|
|||||||
class RemovablePanelLayout: FloatingPanelLayout {
|
class RemovablePanelLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
let initialState: FloatingPanelState = .half
|
let initialState: FloatingPanelState = .half
|
||||||
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
|
||||||
return [
|
|
||||||
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
||||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 130.0, edge: .bottom, referenceGuide: .safeArea)
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 130.0, edge: .bottom, referenceGuide: .safeArea)
|
||||||
]
|
]
|
||||||
}
|
|
||||||
|
|
||||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||||
return 0.3
|
return 0.3
|
||||||
@@ -61,13 +55,10 @@ class RemovablePanelLayout: FloatingPanelLayout {
|
|||||||
class RemovablePanelLandscapeLayout: FloatingPanelLayout {
|
class RemovablePanelLandscapeLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
||||||
return [
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .bottom, referenceGuide: .safeArea)
|
||||||
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
]
|
||||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .bottom, referenceGuide: .safeArea)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||||
return 0.3
|
return 0.3
|
||||||
@@ -77,12 +68,9 @@ class RemovablePanelLandscapeLayout: FloatingPanelLayout {
|
|||||||
class ModalPanelLayout: FloatingPanelLayout {
|
class ModalPanelLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .safeArea),
|
||||||
return [
|
]
|
||||||
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .safeArea),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||||
return 0.3
|
return 0.3
|
||||||
+7
-7
@@ -4,6 +4,9 @@ import UIKit
|
|||||||
|
|
||||||
@IBDesignable
|
@IBDesignable
|
||||||
final class CloseButton: UIButton {
|
final class CloseButton: UIButton {
|
||||||
|
override var isHighlighted: Bool { didSet { setNeedsDisplay() } }
|
||||||
|
override var isSelected: Bool { didSet { setNeedsDisplay() } }
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
required init?(coder aDecoder: NSCoder) {
|
||||||
super.init(coder: aDecoder)
|
super.init(coder: aDecoder)
|
||||||
render()
|
render()
|
||||||
@@ -17,14 +20,11 @@ final class CloseButton: UIButton {
|
|||||||
self.backgroundColor = .clear
|
self.backgroundColor = .clear
|
||||||
}
|
}
|
||||||
|
|
||||||
func p(_ p: CGFloat) -> CGFloat {
|
|
||||||
return p * (2.0 / 3.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
override var isHighlighted: Bool { didSet { setNeedsDisplay() } }
|
|
||||||
override var isSelected: Bool { didSet { setNeedsDisplay() } }
|
|
||||||
|
|
||||||
override func draw(_ rect: CGRect) {
|
override func draw(_ rect: CGRect) {
|
||||||
|
func p(_ p: CGFloat) -> CGFloat {
|
||||||
|
return p * (2.0 / 3.0)
|
||||||
|
}
|
||||||
|
|
||||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||||
|
|
||||||
context.setLineWidth(p(1.0))
|
context.setLineWidth(p(1.0))
|
||||||
@@ -1,329 +0,0 @@
|
|||||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import FloatingPanel
|
|
||||||
|
|
||||||
final class UseCaseController: NSObject {
|
|
||||||
unowned let mainVC: MainViewController
|
|
||||||
private(set) var useCase: UseCase = .trackingTableView
|
|
||||||
|
|
||||||
fileprivate var mainPanelVC: FloatingPanelController!
|
|
||||||
private var detailPanelVC: FloatingPanelController!
|
|
||||||
private var settingsPanelVC: FloatingPanelController!
|
|
||||||
|
|
||||||
private lazy var pagePanelController = PagePanelController()
|
|
||||||
|
|
||||||
private var mainPanelObserves: [NSKeyValueObservation] = []
|
|
||||||
|
|
||||||
init(mainVC: MainViewController) {
|
|
||||||
self.mainVC = mainVC
|
|
||||||
}
|
|
||||||
|
|
||||||
func set(useCase: UseCase) {
|
|
||||||
self.useCase = useCase
|
|
||||||
|
|
||||||
let contentVC = useCase.makeContentViewController(with: mainVC.storyboard!)
|
|
||||||
|
|
||||||
detailPanelVC?.removePanelFromParent(animated: true, completion: nil)
|
|
||||||
detailPanelVC = nil
|
|
||||||
|
|
||||||
switch useCase {
|
|
||||||
case .showDetail:
|
|
||||||
detailPanelVC?.removePanelFromParent(animated: false)
|
|
||||||
|
|
||||||
// Initialize FloatingPanelController
|
|
||||||
detailPanelVC = FloatingPanelController()
|
|
||||||
detailPanelVC.delegate = self
|
|
||||||
|
|
||||||
let appearance = SurfaceAppearance()
|
|
||||||
appearance.cornerRadius = 6.0
|
|
||||||
detailPanelVC.surfaceView.appearance = appearance
|
|
||||||
|
|
||||||
// Set a content view controller
|
|
||||||
detailPanelVC.set(contentViewController: contentVC)
|
|
||||||
|
|
||||||
detailPanelVC.contentMode = .fitToBounds
|
|
||||||
(contentVC as? DetailViewController)?.intrinsicHeightConstraint.isActive = false
|
|
||||||
|
|
||||||
// Add FloatingPanel to self.view
|
|
||||||
detailPanelVC.addPanel(toParent: mainVC, animated: true)
|
|
||||||
case .showModal, .showTabBar:
|
|
||||||
let modalVC = contentVC
|
|
||||||
modalVC.modalPresentationStyle = .fullScreen
|
|
||||||
mainVC.present(modalVC, animated: true, completion: nil)
|
|
||||||
|
|
||||||
case .showPageView:
|
|
||||||
let pageVC = pagePanelController.makePageViewController(for: mainVC)
|
|
||||||
mainVC.present(pageVC, animated: true, completion: nil)
|
|
||||||
|
|
||||||
case .showPageContentView:
|
|
||||||
let pageVC = pagePanelController.makePageViewControllerForContent()
|
|
||||||
self.addMainPanel(with: pageVC)
|
|
||||||
case .showPanelModal:
|
|
||||||
let fpc = FloatingPanelController()
|
|
||||||
let contentVC = mainVC.storyboard!.instantiateViewController(withIdentifier: "DetailViewController")
|
|
||||||
contentVC.loadViewIfNeeded()
|
|
||||||
(contentVC as? DetailViewController)?.modeChangeView.isHidden = true
|
|
||||||
fpc.set(contentViewController: contentVC)
|
|
||||||
fpc.delegate = self
|
|
||||||
|
|
||||||
let appearance = SurfaceAppearance()
|
|
||||||
appearance.cornerRadius = 38.5
|
|
||||||
fpc.surfaceView.appearance = appearance
|
|
||||||
fpc.backdropView.dismissalTapGestureRecognizer.isEnabled = true
|
|
||||||
|
|
||||||
fpc.isRemovalInteractionEnabled = true
|
|
||||||
|
|
||||||
mainVC.present(fpc, animated: true, completion: nil)
|
|
||||||
|
|
||||||
case .showMultiPanelModal:
|
|
||||||
let fpc = MultiPanelController()
|
|
||||||
mainVC.present(fpc, animated: true, completion: nil)
|
|
||||||
|
|
||||||
case .showPanelInSheetModal:
|
|
||||||
let fpc = FloatingPanelController()
|
|
||||||
let contentVC = UIViewController()
|
|
||||||
fpc.set(contentViewController: contentVC)
|
|
||||||
fpc.delegate = self
|
|
||||||
|
|
||||||
let apprearance = SurfaceAppearance()
|
|
||||||
apprearance.cornerRadius = 38.5
|
|
||||||
apprearance.shadows = []
|
|
||||||
fpc.surfaceView.appearance = apprearance
|
|
||||||
fpc.isRemovalInteractionEnabled = true
|
|
||||||
|
|
||||||
let mvc = UIViewController()
|
|
||||||
mvc.view.backgroundColor = UIColor(displayP3Red: 2/255, green: 184/255, blue: 117/255, alpha: 1.0)
|
|
||||||
fpc.addPanel(toParent: mvc)
|
|
||||||
mainVC.present(mvc, animated: true, completion: nil)
|
|
||||||
case .showContentInset:
|
|
||||||
let contentViewController = UIViewController()
|
|
||||||
contentViewController.view.backgroundColor = .green
|
|
||||||
|
|
||||||
let fpc = FloatingPanelController()
|
|
||||||
fpc.set(contentViewController: contentViewController)
|
|
||||||
fpc.surfaceView.contentPadding = .init(top: 20, left: 20, bottom: 20, right: 20)
|
|
||||||
|
|
||||||
fpc.delegate = self
|
|
||||||
fpc.isRemovalInteractionEnabled = true
|
|
||||||
mainVC.present(fpc, animated: true, completion: nil)
|
|
||||||
|
|
||||||
case .showContainerMargins:
|
|
||||||
let fpc = FloatingPanelController()
|
|
||||||
|
|
||||||
let appearance = SurfaceAppearance()
|
|
||||||
appearance.cornerRadius = 38.5
|
|
||||||
fpc.surfaceView.appearance = appearance
|
|
||||||
|
|
||||||
fpc.surfaceView.backgroundColor = .red
|
|
||||||
fpc.surfaceView.containerMargins = .init(top: 24.0, left: 8.0, bottom: max(mainVC.layoutInsets.bottom, 8.0), right: 8.0)
|
|
||||||
#if swift(>=5.1) // Actually Xcode 11 or later
|
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
fpc.surfaceView.layer.cornerCurve = .continuous
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fpc.delegate = self
|
|
||||||
fpc.isRemovalInteractionEnabled = true
|
|
||||||
mainVC.present(fpc, animated: true, completion: nil)
|
|
||||||
default:
|
|
||||||
self.addMainPanel(with: contentVC)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func addMainPanel(with contentVC: UIViewController) {
|
|
||||||
mainPanelObserves.removeAll()
|
|
||||||
|
|
||||||
let oldMainPanelVC = mainPanelVC
|
|
||||||
|
|
||||||
mainPanelVC = FloatingPanelController()
|
|
||||||
mainPanelVC.delegate = self
|
|
||||||
mainPanelVC.contentInsetAdjustmentBehavior = .always
|
|
||||||
|
|
||||||
let appearance = SurfaceAppearance()
|
|
||||||
appearance.cornerRadius = 6.0
|
|
||||||
mainPanelVC.surfaceView.appearance = appearance
|
|
||||||
|
|
||||||
set(contentViewController: contentVC)
|
|
||||||
|
|
||||||
useCase.setUpInteraction(for: self)
|
|
||||||
|
|
||||||
// Add FloatingPanel to self.view
|
|
||||||
if let oldMainPanelVC = oldMainPanelVC {
|
|
||||||
oldMainPanelVC.removePanelFromParent(animated: true, completion: {
|
|
||||||
self.mainPanelVC.addPanel(toParent: self.mainVC, animated: true)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
mainPanelVC.addPanel(toParent: mainVC, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func set(contentViewController contentVC: UIViewController) {
|
|
||||||
mainPanelVC.set(contentViewController: contentVC)
|
|
||||||
// Track a scroll view
|
|
||||||
switch contentVC {
|
|
||||||
case let consoleVC as DebugTextViewController:
|
|
||||||
mainPanelVC.track(scrollView: consoleVC.textView)
|
|
||||||
|
|
||||||
case let contentVC as DebugTableViewController:
|
|
||||||
let ob = contentVC.tableView.observe(\.isEditing) { (tableView, _) in
|
|
||||||
self.mainPanelVC.panGestureRecognizer.isEnabled = !tableView.isEditing
|
|
||||||
}
|
|
||||||
mainPanelObserves.append(ob)
|
|
||||||
mainPanelVC.track(scrollView: contentVC.tableView)
|
|
||||||
case let contentVC as NestedScrollViewController:
|
|
||||||
mainPanelVC.track(scrollView: contentVC.scrollView)
|
|
||||||
case let navVC as UINavigationController:
|
|
||||||
if let rootVC = (navVC.topViewController as? MainViewController) {
|
|
||||||
rootVC.loadViewIfNeeded()
|
|
||||||
mainPanelVC.track(scrollView: rootVC.tableView)
|
|
||||||
}
|
|
||||||
case let contentVC as ImageViewController:
|
|
||||||
if #available(iOS 11.0, *) {
|
|
||||||
let mode: ImageViewController.Mode = (useCase == .showAdaptivePanelWithCustomGuide) ? .withHeaderFooter : .onlyImage
|
|
||||||
let layoutGuide = contentVC.layoutGuideFor(mode: mode)
|
|
||||||
mainPanelVC.layout = ImageViewController.PanelLayout(targetGuide: layoutGuide)
|
|
||||||
} else {
|
|
||||||
mainPanelVC.layout = ImageViewController.PanelLayout(targetGuide: nil)
|
|
||||||
}
|
|
||||||
mainPanelVC.delegate = nil
|
|
||||||
mainPanelVC.isRemovalInteractionEnabled = true
|
|
||||||
mainPanelVC.track(scrollView: contentVC.scrollView)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc
|
|
||||||
fileprivate func handleSurface(tapGesture: UITapGestureRecognizer) {
|
|
||||||
switch mainPanelVC.state {
|
|
||||||
case .full:
|
|
||||||
mainPanelVC.move(to: .half, animated: true)
|
|
||||||
default:
|
|
||||||
mainPanelVC.move(to: .full, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setUpSettingsPanel(for mainVC: MainViewController) {
|
|
||||||
guard settingsPanelVC == nil else { return }
|
|
||||||
// Initialize FloatingPanelController
|
|
||||||
settingsPanelVC = FloatingPanelController()
|
|
||||||
|
|
||||||
let appearance = SurfaceAppearance()
|
|
||||||
appearance.cornerRadius = 6.0
|
|
||||||
settingsPanelVC.surfaceView.appearance = appearance
|
|
||||||
|
|
||||||
settingsPanelVC.isRemovalInteractionEnabled = true
|
|
||||||
settingsPanelVC.backdropView.dismissalTapGestureRecognizer.isEnabled = true
|
|
||||||
settingsPanelVC.delegate = self
|
|
||||||
|
|
||||||
let contentVC = mainVC.storyboard?.instantiateViewController(withIdentifier: "SettingsViewController")
|
|
||||||
|
|
||||||
// Set a content view controller
|
|
||||||
settingsPanelVC.set(contentViewController: contentVC)
|
|
||||||
|
|
||||||
// Add FloatingPanel to self.view
|
|
||||||
settingsPanelVC.addPanel(toParent: mainVC, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UseCaseController: FloatingPanelControllerDelegate {
|
|
||||||
func floatingPanel(_ vc: FloatingPanelController, contentOffsetForPinning trackingScrollView: UIScrollView) -> CGPoint {
|
|
||||||
if useCase == .showNavigationController, #available(iOS 11.0, *) {
|
|
||||||
// 148.0 is the SafeArea's top value for a navigation bar with a large title.
|
|
||||||
return CGPoint(x: 0.0, y: 0.0 - trackingScrollView.contentInset.top - 148.0)
|
|
||||||
}
|
|
||||||
return CGPoint(x: 0.0, y: 0.0 - trackingScrollView.contentInset.top)
|
|
||||||
}
|
|
||||||
|
|
||||||
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
|
|
||||||
if vc == settingsPanelVC {
|
|
||||||
return IntrinsicPanelLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch useCase {
|
|
||||||
case .showTopPositionedPanel:
|
|
||||||
return TopPositionedPanelLayout()
|
|
||||||
case .showRemovablePanel:
|
|
||||||
return newCollection.verticalSizeClass == .compact ? RemovablePanelLandscapeLayout() : RemovablePanelLayout()
|
|
||||||
case .showIntrinsicView:
|
|
||||||
return IntrinsicPanelLayout()
|
|
||||||
case .showPanelModal:
|
|
||||||
if vc != mainPanelVC && vc != detailPanelVC {
|
|
||||||
return ModalPanelLayout()
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case .showContentInset:
|
|
||||||
return FloatingPanelBottomLayout()
|
|
||||||
case .showCustomStatePanel:
|
|
||||||
return FloatingPanelLayoutWithCustomState()
|
|
||||||
default:
|
|
||||||
return (newCollection.verticalSizeClass == .compact) ? FloatingPanelBottomLayout() : mainVC
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func floatingPanelDidRemove(_ vc: FloatingPanelController) {
|
|
||||||
switch vc {
|
|
||||||
case settingsPanelVC:
|
|
||||||
settingsPanelVC = nil
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UseCaseController: UIGestureRecognizerDelegate {
|
|
||||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
||||||
switch useCase {
|
|
||||||
case .showNestedScrollView:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UseCase {
|
|
||||||
func makeContentViewController(with storyboard: UIStoryboard) -> UIViewController {
|
|
||||||
guard let storyboardID = self.storyboardID else { return DebugTableViewController() }
|
|
||||||
return storyboard.instantiateViewController(withIdentifier: storyboardID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setUpInteraction(for useCaseController: UseCaseController) {
|
|
||||||
let mainVC = useCaseController.mainVC
|
|
||||||
let mainPanelVC = useCaseController.mainPanelVC!
|
|
||||||
|
|
||||||
// Enable tap-to-hide and removal interaction
|
|
||||||
switch self {
|
|
||||||
case .trackingTableView:
|
|
||||||
let tapGesture = UITapGestureRecognizer(target: useCaseController, action: #selector(UseCaseController.handleSurface(tapGesture:)))
|
|
||||||
tapGesture.cancelsTouchesInView = false
|
|
||||||
tapGesture.numberOfTapsRequired = 2
|
|
||||||
// Prevents a delay to response a tap in menus of DebugTableViewController.
|
|
||||||
tapGesture.delaysTouchesEnded = false
|
|
||||||
mainPanelVC.surfaceView.addGestureRecognizer(tapGesture)
|
|
||||||
case .showNestedScrollView:
|
|
||||||
mainPanelVC.panGestureRecognizer.delegateProxy = useCaseController
|
|
||||||
case .showPageContentView:
|
|
||||||
if let page = (mainPanelVC.contentViewController as? UIPageViewController)?.viewControllers?.first {
|
|
||||||
mainPanelVC.track(scrollView: (page as! DebugTableViewController).tableView)
|
|
||||||
}
|
|
||||||
case .showRemovablePanel, .showIntrinsicView:
|
|
||||||
mainPanelVC.isRemovalInteractionEnabled = true
|
|
||||||
mainPanelVC.backdropView.dismissalTapGestureRecognizer.isEnabled = true
|
|
||||||
case .showNavigationController:
|
|
||||||
mainPanelVC.contentInsetAdjustmentBehavior = .never
|
|
||||||
case .showTopPositionedPanel: // For debug
|
|
||||||
let contentVC = UIViewController()
|
|
||||||
contentVC.view.backgroundColor = .red
|
|
||||||
mainPanelVC.set(contentViewController: contentVC)
|
|
||||||
mainPanelVC.addPanel(toParent: mainVC, animated: true)
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,14 +4,10 @@ import UIKit
|
|||||||
import FloatingPanel
|
import FloatingPanel
|
||||||
|
|
||||||
final class PagePanelController: NSObject {
|
final class PagePanelController: NSObject {
|
||||||
lazy var pages = [UIColor.blue, .red, .green].compactMap({ (color) -> UIViewController in
|
var pages: [UIViewController] = []
|
||||||
let page = FloatingPanelController(delegate: self)
|
}
|
||||||
page.view.backgroundColor = color
|
|
||||||
page.panGestureRecognizer.delegateProxy = self
|
|
||||||
page.show()
|
|
||||||
return page
|
|
||||||
})
|
|
||||||
|
|
||||||
|
extension PagePanelController {
|
||||||
func makePageViewControllerForContent() -> UIPageViewController {
|
func makePageViewControllerForContent() -> UIPageViewController {
|
||||||
pages = [DebugTableViewController(), DebugTableViewController(), DebugTableViewController()]
|
pages = [DebugTableViewController(), DebugTableViewController(), DebugTableViewController()]
|
||||||
let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:])
|
let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:])
|
||||||
@@ -22,6 +18,13 @@ final class PagePanelController: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makePageViewController(for vc: MainViewController) -> UIPageViewController {
|
func makePageViewController(for vc: MainViewController) -> UIPageViewController {
|
||||||
|
pages = [UIColor.blue, .red, .green].compactMap({ (color) -> UIViewController in
|
||||||
|
let page = FloatingPanelController(delegate: self)
|
||||||
|
page.view.backgroundColor = color
|
||||||
|
page.panGestureRecognizer.delegateProxy = self
|
||||||
|
page.show()
|
||||||
|
return page
|
||||||
|
})
|
||||||
let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:])
|
let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:])
|
||||||
let closeButton = UIButton(type: .custom)
|
let closeButton = UIButton(type: .custom)
|
||||||
pageVC.view.addSubview(closeButton)
|
pageVC.view.addSubview(closeButton)
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||||
|
|
||||||
import Foundation
|
import UIKit
|
||||||
|
|
||||||
enum UseCase: Int, CaseIterable {
|
enum UseCase: Int, CaseIterable {
|
||||||
case trackingTableView
|
case trackingTableView
|
||||||
case trackingTextView
|
case trackingTextView
|
||||||
|
case trackingCollectionViewList
|
||||||
case showDetail
|
case showDetail
|
||||||
case showModal
|
case showModal
|
||||||
case showPanelModal
|
case showPanelModal
|
||||||
case showMultiPanelModal
|
case showMultiPanelModal
|
||||||
case showPanelInSheetModal
|
case showPanelInSheetModal
|
||||||
|
case showOnWindow
|
||||||
case showTabBar
|
case showTabBar
|
||||||
case showPageView
|
case showPageView
|
||||||
case showPageContentView
|
case showPageContentView
|
||||||
@@ -21,17 +23,24 @@ enum UseCase: Int, CaseIterable {
|
|||||||
case showNavigationController
|
case showNavigationController
|
||||||
case showTopPositionedPanel
|
case showTopPositionedPanel
|
||||||
case showAdaptivePanel
|
case showAdaptivePanel
|
||||||
case showAdaptivePanelWithCustomGuide
|
case showAdaptivePanelWithTableView
|
||||||
|
case showAdaptivePanelWithCollectionView
|
||||||
|
case showAdaptivePanelWithCompositionalCollectionView
|
||||||
case showCustomStatePanel
|
case showCustomStatePanel
|
||||||
|
case showCustomBackdrop
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UseCase {
|
||||||
var name: String {
|
var name: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .trackingTableView: return "Scroll tracking(TableView)"
|
case .trackingTableView: return "Scroll tracking(TableView)"
|
||||||
|
case .trackingCollectionViewList: return "Scroll tracking(List CollectionView)"
|
||||||
case .trackingTextView: return "Scroll tracking(TextView)"
|
case .trackingTextView: return "Scroll tracking(TextView)"
|
||||||
case .showDetail: return "Show Detail Panel"
|
case .showDetail: return "Show Detail Panel"
|
||||||
case .showModal: return "Show Modal"
|
case .showModal: return "Show Modal"
|
||||||
case .showPanelModal: return "Show Panel Modal"
|
case .showPanelModal: return "Show Panel Modal"
|
||||||
case .showMultiPanelModal: return "Show Multi Panel Modal"
|
case .showMultiPanelModal: return "Show Multi Panel Modal"
|
||||||
|
case .showOnWindow: return "Show Panel over Window"
|
||||||
case .showPanelInSheetModal: return "Show Panel in Sheet Modal"
|
case .showPanelInSheetModal: return "Show Panel in Sheet Modal"
|
||||||
case .showTabBar: return "Show Tab Bar"
|
case .showTabBar: return "Show Tab Bar"
|
||||||
case .showPageView: return "Show Page View"
|
case .showPageView: return "Show Page View"
|
||||||
@@ -44,35 +53,79 @@ enum UseCase: Int, CaseIterable {
|
|||||||
case .showNavigationController: return "Show Navigation Controller"
|
case .showNavigationController: return "Show Navigation Controller"
|
||||||
case .showTopPositionedPanel: return "Show Top Positioned Panel"
|
case .showTopPositionedPanel: return "Show Top Positioned Panel"
|
||||||
case .showAdaptivePanel: return "Show Adaptive Panel"
|
case .showAdaptivePanel: return "Show Adaptive Panel"
|
||||||
case .showAdaptivePanelWithCustomGuide: return "Show Adaptive Panel (Custom Layout Guide)"
|
case .showAdaptivePanelWithTableView: return "Show Adaptive Panel (TableView)"
|
||||||
|
case .showAdaptivePanelWithCollectionView: return "Show Adaptive Panel (CollectionView)"
|
||||||
|
case .showAdaptivePanelWithCompositionalCollectionView: return "Show Adaptive Panel (Compositional CollectionView)"
|
||||||
case .showCustomStatePanel: return "Show Panel with Custom state"
|
case .showCustomStatePanel: return "Show Panel with Custom state"
|
||||||
}
|
case .showCustomBackdrop: return "Show Panel with Custom Backdrop"
|
||||||
}
|
|
||||||
|
|
||||||
var storyboardID: String? {
|
|
||||||
switch self {
|
|
||||||
case .trackingTableView: return nil
|
|
||||||
case .trackingTextView: return "ConsoleViewController" // Storyboard only
|
|
||||||
case .showDetail: return String(describing: DetailViewController.self)
|
|
||||||
case .showModal: return String(describing: ModalViewController.self)
|
|
||||||
case .showMultiPanelModal: return nil
|
|
||||||
case .showPanelInSheetModal: return nil
|
|
||||||
case .showPanelModal: return nil
|
|
||||||
case .showTabBar: return String(describing: TabBarViewController.self)
|
|
||||||
case .showPageView: return nil
|
|
||||||
case .showPageContentView: return nil
|
|
||||||
case .showNestedScrollView: return String(describing: NestedScrollViewController.self)
|
|
||||||
case .showRemovablePanel: return String(describing: DetailViewController.self)
|
|
||||||
case .showIntrinsicView: return "IntrinsicViewController" // Storyboard only
|
|
||||||
case .showContentInset: return nil
|
|
||||||
case .showContainerMargins: return nil
|
|
||||||
case .showNavigationController: return "RootNavigationController" // Storyboard only
|
|
||||||
case .showTopPositionedPanel: return nil
|
|
||||||
case .showAdaptivePanel,
|
|
||||||
.showAdaptivePanelWithCustomGuide:
|
|
||||||
return String(describing: ImageViewController.self)
|
|
||||||
case .showCustomStatePanel:
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension UseCase {
|
||||||
|
private enum Content {
|
||||||
|
case storyboard(String)
|
||||||
|
case viewController(UIViewController)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var content: Content {
|
||||||
|
switch self {
|
||||||
|
case .trackingTableView: return .viewController(DebugTableViewController())
|
||||||
|
case .trackingCollectionViewList:
|
||||||
|
if #available(iOS 14, *) {
|
||||||
|
return .viewController(DebugListCollectionViewController())
|
||||||
|
} else {
|
||||||
|
let msg = "UICollectionLayoutListConfiguration is unavailable.\nBuild this app on iOS 14 and later."
|
||||||
|
return makeUnavailableViewContent(message: msg)
|
||||||
|
}
|
||||||
|
case .trackingTextView: return .storyboard("ConsoleViewController") // Storyboard only
|
||||||
|
case .showDetail: return .storyboard(String(describing: DetailViewController.self))
|
||||||
|
case .showModal: return .storyboard(String(describing: ModalViewController.self))
|
||||||
|
case .showMultiPanelModal: return .viewController(DebugTableViewController())
|
||||||
|
case .showOnWindow: return .viewController(DebugTableViewController())
|
||||||
|
case .showPanelInSheetModal: return .viewController(DebugTableViewController())
|
||||||
|
case .showPanelModal: return .viewController(DebugTableViewController())
|
||||||
|
case .showTabBar: return .storyboard(String(describing: TabBarViewController.self))
|
||||||
|
case .showPageView: return .viewController(DebugTableViewController())
|
||||||
|
case .showPageContentView: return .viewController(DebugTableViewController())
|
||||||
|
case .showNestedScrollView: return .storyboard(String(describing: NestedScrollViewController.self))
|
||||||
|
case .showRemovablePanel: return .storyboard(String(describing: DetailViewController.self))
|
||||||
|
case .showIntrinsicView: return .storyboard("IntrinsicViewController") // Storyboard only
|
||||||
|
case .showContentInset: return .viewController(DebugTableViewController())
|
||||||
|
case .showContainerMargins: return .viewController(DebugTableViewController())
|
||||||
|
case .showNavigationController: return .storyboard("RootNavigationController") // Storyboard only
|
||||||
|
case .showTopPositionedPanel: return .viewController(DebugTableViewController())
|
||||||
|
case .showAdaptivePanel: return .storyboard(String(describing: ImageViewController.self))
|
||||||
|
case .showAdaptivePanelWithTableView: return .storyboard(String(describing: TableViewControllerForAdaptiveLayout.self))
|
||||||
|
case .showAdaptivePanelWithCollectionView,
|
||||||
|
.showAdaptivePanelWithCompositionalCollectionView:
|
||||||
|
if #available(iOS 13, *) {
|
||||||
|
let vc = CollectionViewControllerForAdaptiveLayout()
|
||||||
|
vc.layoutType = self == .showAdaptivePanelWithCollectionView ? .flow : .compositional
|
||||||
|
return .viewController(vc)
|
||||||
|
} else {
|
||||||
|
let msg = "Compositional layout is unavailable.\nBuild this app on iOS 13 and later."
|
||||||
|
return makeUnavailableViewContent(message: msg)
|
||||||
|
}
|
||||||
|
case .showCustomStatePanel: return .viewController(DebugTableViewController())
|
||||||
|
case .showCustomBackdrop: return .viewController(UIViewController())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeContentViewController(with storyboard: UIStoryboard) -> UIViewController {
|
||||||
|
switch content {
|
||||||
|
case .storyboard(let id):
|
||||||
|
return storyboard.instantiateViewController(withIdentifier: id)
|
||||||
|
case .viewController(let vc):
|
||||||
|
vc.loadViewIfNeeded()
|
||||||
|
return vc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makeUnavailableViewContent(message: String) -> Content {
|
||||||
|
let vc = UnavailableViewController()
|
||||||
|
vc.loadViewIfNeeded()
|
||||||
|
vc.label.text = message
|
||||||
|
return .viewController(vc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,507 @@
|
|||||||
|
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import FloatingPanel
|
||||||
|
|
||||||
|
final class UseCaseController: NSObject {
|
||||||
|
unowned let mainVC: MainViewController
|
||||||
|
private(set) var useCase: UseCase
|
||||||
|
|
||||||
|
private var mainPanelVC: FloatingPanelController!
|
||||||
|
private var detailPanelVC: FloatingPanelController!
|
||||||
|
private var settingsPanelVC: FloatingPanelController!
|
||||||
|
private lazy var pagePanelController = PagePanelController()
|
||||||
|
private lazy var overWindowPanelVC = FloatingPanelController()
|
||||||
|
|
||||||
|
init(mainVC: MainViewController) {
|
||||||
|
self.mainVC = mainVC
|
||||||
|
self.useCase = .trackingTableView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UseCaseController {
|
||||||
|
func set(useCase: UseCase) {
|
||||||
|
self.useCase = useCase
|
||||||
|
|
||||||
|
let contentVC = useCase.makeContentViewController(with: mainVC.storyboard!)
|
||||||
|
|
||||||
|
if let fpc = detailPanelVC {
|
||||||
|
fpc.removePanelFromParent(animated: true, completion: nil)
|
||||||
|
self.detailPanelVC = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch useCase {
|
||||||
|
case .trackingTableView:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.delegate = self
|
||||||
|
fpc.contentInsetAdjustmentBehavior = .always
|
||||||
|
fpc.surfaceView.appearance = {
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerRadius = 6.0
|
||||||
|
return appearance
|
||||||
|
}()
|
||||||
|
|
||||||
|
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(UseCaseController.handleSurface(tapGesture:)))
|
||||||
|
tapGesture.cancelsTouchesInView = false
|
||||||
|
tapGesture.numberOfTapsRequired = 2
|
||||||
|
// Prevents a delay to response a tap in menus of DebugTableViewController.
|
||||||
|
tapGesture.delaysTouchesEnded = false
|
||||||
|
fpc.surfaceView.addGestureRecognizer(tapGesture)
|
||||||
|
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.ext_trackScrollView(in: contentVC)
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .trackingCollectionViewList:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.delegate = self
|
||||||
|
fpc.contentInsetAdjustmentBehavior = .always
|
||||||
|
fpc.surfaceView.appearance = {
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerRadius = 6.0
|
||||||
|
return appearance
|
||||||
|
}()
|
||||||
|
|
||||||
|
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(UseCaseController.handleSurface(tapGesture:)))
|
||||||
|
tapGesture.cancelsTouchesInView = false
|
||||||
|
tapGesture.numberOfTapsRequired = 2
|
||||||
|
// Prevents a delay to response a tap in menus of DebugTableViewController.
|
||||||
|
tapGesture.delaysTouchesEnded = false
|
||||||
|
fpc.surfaceView.addGestureRecognizer(tapGesture)
|
||||||
|
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
if #available(iOS 14, *),
|
||||||
|
let scrollView = (fpc.contentViewController as? DebugListCollectionViewController)?.collectionView {
|
||||||
|
fpc.track(scrollView: scrollView)
|
||||||
|
}
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .trackingTextView:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.delegate = self
|
||||||
|
fpc.contentInsetAdjustmentBehavior = .always
|
||||||
|
fpc.surfaceView.appearance = {
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerRadius = 6.0
|
||||||
|
return appearance
|
||||||
|
}()
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.ext_trackScrollView(in: contentVC)
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .showDetail:
|
||||||
|
// Initialize FloatingPanelController
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.delegate = self
|
||||||
|
fpc.surfaceView.appearance = {
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerRadius = 6.0
|
||||||
|
return appearance
|
||||||
|
}()
|
||||||
|
// Set a content view controller
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.contentMode = .fitToBounds
|
||||||
|
(contentVC as? DetailViewController)?.intrinsicHeightConstraint.isActive = false
|
||||||
|
|
||||||
|
detailPanelVC = fpc
|
||||||
|
// Add FloatingPanel to self.view
|
||||||
|
fpc.addPanel(toParent: mainVC, animated: true)
|
||||||
|
|
||||||
|
case .showModal, .showTabBar:
|
||||||
|
let modalVC = contentVC
|
||||||
|
modalVC.modalPresentationStyle = .fullScreen
|
||||||
|
mainVC.present(modalVC, animated: true, completion: nil)
|
||||||
|
|
||||||
|
case .showPageView:
|
||||||
|
let pageVC = pagePanelController.makePageViewController(for: mainVC)
|
||||||
|
mainVC.present(pageVC, animated: true, completion: nil)
|
||||||
|
|
||||||
|
case .showPageContentView:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.delegate = self
|
||||||
|
fpc.contentInsetAdjustmentBehavior = .always
|
||||||
|
fpc.surfaceView.appearance = {
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerRadius = 6.0
|
||||||
|
return appearance
|
||||||
|
}()
|
||||||
|
let pageVC = pagePanelController.makePageViewControllerForContent()
|
||||||
|
if let page = (fpc.contentViewController as? UIPageViewController)?.viewControllers?.first {
|
||||||
|
fpc.track(scrollView: (page as! DebugTableViewController).tableView)
|
||||||
|
}
|
||||||
|
fpc.set(contentViewController: pageVC)
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .showRemovablePanel, .showIntrinsicView:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.isRemovalInteractionEnabled = true
|
||||||
|
fpc.backdropView.dismissalTapGestureRecognizer.isEnabled = true
|
||||||
|
fpc.delegate = self
|
||||||
|
fpc.contentInsetAdjustmentBehavior = .always
|
||||||
|
fpc.surfaceView.appearance = {
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerRadius = 6.0
|
||||||
|
return appearance
|
||||||
|
}()
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.ext_trackScrollView(in: contentVC)
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .showNestedScrollView:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.panGestureRecognizer.delegateProxy = self
|
||||||
|
fpc.delegate = self
|
||||||
|
fpc.contentInsetAdjustmentBehavior = .always
|
||||||
|
fpc.surfaceView.appearance = {
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerRadius = 6.0
|
||||||
|
return appearance
|
||||||
|
}()
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.ext_trackScrollView(in: contentVC)
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .showPanelModal:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
let contentVC = mainVC.storyboard!.instantiateViewController(withIdentifier: "DetailViewController")
|
||||||
|
contentVC.loadViewIfNeeded()
|
||||||
|
(contentVC as? DetailViewController)?.modeChangeView.isHidden = true
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.delegate = self
|
||||||
|
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerRadius = 38.5
|
||||||
|
fpc.surfaceView.appearance = appearance
|
||||||
|
fpc.backdropView.dismissalTapGestureRecognizer.isEnabled = true
|
||||||
|
|
||||||
|
fpc.isRemovalInteractionEnabled = true
|
||||||
|
|
||||||
|
mainVC.present(fpc, animated: true, completion: nil)
|
||||||
|
|
||||||
|
case .showMultiPanelModal:
|
||||||
|
let fpc = MultiPanelController()
|
||||||
|
mainVC.present(fpc, animated: true, completion: nil)
|
||||||
|
|
||||||
|
case .showOnWindow:
|
||||||
|
let fpc = overWindowPanelVC
|
||||||
|
fpc.backdropView.dismissalTapGestureRecognizer.isEnabled = true
|
||||||
|
fpc.isRemovalInteractionEnabled = true
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.ext_trackScrollView(in: contentVC)
|
||||||
|
|
||||||
|
guard let window = UIApplication.shared.windows.first else { fatalError("Any window not found") }
|
||||||
|
|
||||||
|
window.addSubview(fpc.view)
|
||||||
|
fpc.view.frame = window.bounds
|
||||||
|
fpc.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
|
||||||
|
fpc.show(animated: true)
|
||||||
|
case .showPanelInSheetModal:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
let contentVC = UIViewController()
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.delegate = self
|
||||||
|
|
||||||
|
let apprearance = SurfaceAppearance()
|
||||||
|
apprearance.cornerRadius = 38.5
|
||||||
|
apprearance.shadows = []
|
||||||
|
fpc.surfaceView.appearance = apprearance
|
||||||
|
fpc.isRemovalInteractionEnabled = true
|
||||||
|
|
||||||
|
let mvc = UIViewController()
|
||||||
|
mvc.view.backgroundColor = UIColor(displayP3Red: 2/255, green: 184/255, blue: 117/255, alpha: 1.0)
|
||||||
|
fpc.addPanel(toParent: mvc)
|
||||||
|
mainVC.present(mvc, animated: true, completion: nil)
|
||||||
|
|
||||||
|
case .showContentInset:
|
||||||
|
let contentViewController = UIViewController()
|
||||||
|
contentViewController.view.backgroundColor = .green
|
||||||
|
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.set(contentViewController: contentViewController)
|
||||||
|
fpc.surfaceView.contentPadding = .init(top: 20, left: 20, bottom: 20, right: 20)
|
||||||
|
|
||||||
|
fpc.delegate = self
|
||||||
|
fpc.isRemovalInteractionEnabled = true
|
||||||
|
mainVC.present(fpc, animated: true, completion: nil)
|
||||||
|
|
||||||
|
case .showContainerMargins:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerRadius = 38.5
|
||||||
|
fpc.surfaceView.appearance = appearance
|
||||||
|
|
||||||
|
fpc.surfaceView.backgroundColor = .red
|
||||||
|
fpc.surfaceView.containerMargins = .init(top: 24.0, left: 8.0, bottom: max(mainVC.layoutInsets.bottom, 8.0), right: 8.0)
|
||||||
|
#if swift(>=5.1) // Actually Xcode 11 or later
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
fpc.surfaceView.layer.cornerCurve = .continuous
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fpc.delegate = self
|
||||||
|
fpc.isRemovalInteractionEnabled = true
|
||||||
|
mainVC.present(fpc, animated: true, completion: nil)
|
||||||
|
|
||||||
|
case .showNavigationController:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.contentInsetAdjustmentBehavior = .never
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.ext_trackScrollView(in: contentVC)
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .showTopPositionedPanel: // For debug
|
||||||
|
let fpc = FloatingPanelController(delegate: self)
|
||||||
|
let contentVC = UIViewController()
|
||||||
|
contentVC.view.backgroundColor = .red
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .showAdaptivePanel:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.isRemovalInteractionEnabled = true
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.ext_trackScrollView(in: contentVC)
|
||||||
|
if case let contentVC as ImageViewController = contentVC {
|
||||||
|
let mode: ImageViewController.Mode = (useCase == .showAdaptivePanelWithTableView) ? .withHeaderFooter : .onlyImage
|
||||||
|
let layoutGuide = contentVC.layoutGuideFor(mode: mode)
|
||||||
|
fpc.layout = ImageViewController.PanelLayout(targetGuide: layoutGuide)
|
||||||
|
}
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .showAdaptivePanelWithTableView:
|
||||||
|
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.track(scrollView: (contentVC as! TableViewControllerForAdaptiveLayout).tableView)
|
||||||
|
fpc.layout = TableViewControllerForAdaptiveLayout.PanelLayout(targetGuide: contentVC.view.makeBoundsLayoutGuide())
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .showAdaptivePanelWithCollectionView, .showAdaptivePanelWithCompositionalCollectionView:
|
||||||
|
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)
|
||||||
|
if #available(iOS 13, *) {
|
||||||
|
fpc.track(scrollView: (contentVC as! CollectionViewControllerForAdaptiveLayout).collectionView)
|
||||||
|
fpc.layout = CollectionViewControllerForAdaptiveLayout.PanelLayout(targetGuide: contentVC.view.makeBoundsLayoutGuide())
|
||||||
|
}
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .showCustomStatePanel:
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.delegate = self
|
||||||
|
fpc.contentInsetAdjustmentBehavior = .always
|
||||||
|
fpc.surfaceView.appearance = {
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerRadius = 6.0
|
||||||
|
return appearance
|
||||||
|
}()
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.ext_trackScrollView(in: contentVC)
|
||||||
|
addMain(panel: fpc)
|
||||||
|
|
||||||
|
case .showCustomBackdrop:
|
||||||
|
class BlurBackdropView: BackdropView {
|
||||||
|
var effectView: UIVisualEffectView!
|
||||||
|
override var alpha: CGFloat {
|
||||||
|
set {
|
||||||
|
effectView.alpha = newValue
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
effectView.alpha
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override init() {
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
let effect = UIBlurEffect(style: .prominent)
|
||||||
|
let effectView = UIVisualEffectView(effect: effect)
|
||||||
|
addSubview(effectView)
|
||||||
|
effectView.frame = bounds
|
||||||
|
effectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
self.effectView = effectView
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class CustomBottomLayout: FloatingPanelBottomLayout {
|
||||||
|
override var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||||
|
return [
|
||||||
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
|
||||||
|
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||||
|
.tip: FloatingPanelLayoutAnchor(fractionalInset: 0.1, edge: .bottom, referenceGuide: .safeArea),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
override func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||||
|
return state == .full ? 0.8 : 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.delegate = self
|
||||||
|
fpc.set(contentViewController: contentVC)
|
||||||
|
fpc.backdropView = BlurBackdropView()
|
||||||
|
fpc.layout = CustomBottomLayout()
|
||||||
|
addMain(panel: fpc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUpSettingsPanel(for mainVC: MainViewController) {
|
||||||
|
guard settingsPanelVC == nil else { return }
|
||||||
|
// Initialize FloatingPanelController
|
||||||
|
settingsPanelVC = FloatingPanelController()
|
||||||
|
|
||||||
|
let appearance = SurfaceAppearance()
|
||||||
|
appearance.cornerRadius = 6.0
|
||||||
|
settingsPanelVC.surfaceView.appearance = appearance
|
||||||
|
|
||||||
|
settingsPanelVC.isRemovalInteractionEnabled = true
|
||||||
|
settingsPanelVC.backdropView.dismissalTapGestureRecognizer.isEnabled = true
|
||||||
|
settingsPanelVC.delegate = self
|
||||||
|
|
||||||
|
let contentVC = mainVC.storyboard?.instantiateViewController(withIdentifier: "SettingsViewController")
|
||||||
|
|
||||||
|
// Set a content view controller
|
||||||
|
settingsPanelVC.set(contentViewController: contentVC)
|
||||||
|
|
||||||
|
// Add FloatingPanel to self.view
|
||||||
|
settingsPanelVC.addPanel(toParent: mainVC, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addMain(panel fpc: FloatingPanelController) {
|
||||||
|
let oldMainPanelVC = mainPanelVC
|
||||||
|
mainPanelVC = fpc
|
||||||
|
if let oldMainPanelVC = oldMainPanelVC {
|
||||||
|
oldMainPanelVC.removePanelFromParent(animated: true, completion: {
|
||||||
|
self.mainPanelVC.addPanel(toParent: self.mainVC, animated: true)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
mainPanelVC.addPanel(toParent: mainVC, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
private func handleSurface(tapGesture: UITapGestureRecognizer) {
|
||||||
|
switch mainPanelVC.state {
|
||||||
|
case .full:
|
||||||
|
mainPanelVC.move(to: .half, animated: true)
|
||||||
|
default:
|
||||||
|
mainPanelVC.move(to: .full, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UseCaseController: FloatingPanelControllerDelegate {
|
||||||
|
func floatingPanel(
|
||||||
|
_ fpc: FloatingPanelController,
|
||||||
|
shouldAllowToScroll scrollView: UIScrollView,
|
||||||
|
in state: FloatingPanelState
|
||||||
|
) -> Bool {
|
||||||
|
return state == .full || state == .half
|
||||||
|
}
|
||||||
|
|
||||||
|
func floatingPanel(_ vc: FloatingPanelController, contentOffsetForPinning trackingScrollView: UIScrollView) -> CGPoint {
|
||||||
|
if useCase == .showNavigationController {
|
||||||
|
// 148.0 is the SafeArea's top value for a navigation bar with a large title.
|
||||||
|
return CGPoint(x: 0.0, y: 0.0 - trackingScrollView.contentInset.top - 148.0)
|
||||||
|
}
|
||||||
|
return CGPoint(x: 0.0, y: 0.0 - trackingScrollView.contentInset.top)
|
||||||
|
}
|
||||||
|
|
||||||
|
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
|
||||||
|
if vc == settingsPanelVC {
|
||||||
|
return IntrinsicPanelLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch useCase {
|
||||||
|
case .showTopPositionedPanel:
|
||||||
|
return TopPositionedPanelLayout()
|
||||||
|
case .showRemovablePanel:
|
||||||
|
return newCollection.verticalSizeClass == .compact ? RemovablePanelLandscapeLayout() : RemovablePanelLayout()
|
||||||
|
case .showIntrinsicView:
|
||||||
|
return IntrinsicPanelLayout()
|
||||||
|
case .showPanelModal:
|
||||||
|
if vc != mainPanelVC && vc != detailPanelVC {
|
||||||
|
return ModalPanelLayout()
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case .showContentInset:
|
||||||
|
return FloatingPanelBottomLayout()
|
||||||
|
case .showCustomStatePanel:
|
||||||
|
return FloatingPanelLayoutWithCustomState()
|
||||||
|
default:
|
||||||
|
return (newCollection.verticalSizeClass == .compact) ? FloatingPanelBottomLayout() : mainVC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func floatingPanelDidRemove(_ vc: FloatingPanelController) {
|
||||||
|
switch vc {
|
||||||
|
case settingsPanelVC:
|
||||||
|
settingsPanelVC = nil
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UseCaseController: UIGestureRecognizerDelegate {
|
||||||
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
if case .showNestedScrollView = useCase {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension FloatingPanelController {
|
||||||
|
func ext_trackScrollView(in contentVC: UIViewController) {
|
||||||
|
switch contentVC {
|
||||||
|
case let consoleVC as DebugTextViewController:
|
||||||
|
track(scrollView: consoleVC.textView)
|
||||||
|
|
||||||
|
case let contentVC as DebugTableViewController:
|
||||||
|
let ob = contentVC.tableView.observe(\.isEditing) { [weak self] (tableView, _) in
|
||||||
|
self?.panGestureRecognizer.isEnabled = !tableView.isEditing
|
||||||
|
}
|
||||||
|
contentVC.kvoObservers.append(ob)
|
||||||
|
track(scrollView: contentVC.tableView)
|
||||||
|
|
||||||
|
case let contentVC as NestedScrollViewController:
|
||||||
|
track(scrollView: contentVC.scrollView)
|
||||||
|
|
||||||
|
case let navVC as UINavigationController:
|
||||||
|
if let rootVC = (navVC.topViewController as? MainViewController) {
|
||||||
|
rootVC.loadViewIfNeeded()
|
||||||
|
track(scrollView: rootVC.tableView)
|
||||||
|
}
|
||||||
|
|
||||||
|
case let contentVC as ImageViewController:
|
||||||
|
track(scrollView: contentVC.scrollView)
|
||||||
|
|
||||||
|
case let contentVC as TableViewControllerForAdaptiveLayout:
|
||||||
|
track(scrollView: contentVC.tableView)
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import FloatingPanel
|
|
||||||
|
|
||||||
final class SettingsViewController: InspectableViewController {
|
|
||||||
@IBOutlet weak var largeTitlesSwicth: UISwitch!
|
|
||||||
@IBOutlet weak var translucentSwicth: UISwitch!
|
|
||||||
@IBOutlet weak var versionLabel: UILabel!
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
versionLabel.text = "Version: \(Bundle.main.infoDictionary?["CFBundleVersion"] ?? "--")"
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLayoutSubviews() {
|
|
||||||
super.viewDidLayoutSubviews()
|
|
||||||
if #available(iOS 11.0, *) {
|
|
||||||
let prefersLargeTitles = navigationController!.navigationBar.prefersLargeTitles
|
|
||||||
largeTitlesSwicth.setOn(prefersLargeTitles, animated: false)
|
|
||||||
} else {
|
|
||||||
largeTitlesSwicth.isEnabled = false
|
|
||||||
}
|
|
||||||
let isTranslucent = navigationController!.navigationBar.isTranslucent
|
|
||||||
translucentSwicth.setOn(isTranslucent, animated: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func toggleLargeTitle(_ sender: UISwitch) {
|
|
||||||
if #available(iOS 11.0, *) {
|
|
||||||
navigationController?.navigationBar.prefersLargeTitles = sender.isOn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@IBAction func toggleTranslucent(_ sender: UISwitch) {
|
|
||||||
navigationController?.navigationBar.isTranslucent = sender.isOn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
@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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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 it’s 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.
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
545BA70621BA3214007F7846 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 545BA70521BA3214007F7846 /* AppDelegate.m */; };
|
545BA70621BA3214007F7846 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 545BA70521BA3214007F7846 /* AppDelegate.m */; };
|
||||||
545BA70921BA3214007F7846 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 545BA70821BA3214007F7846 /* ViewController.m */; };
|
545BA70921BA3214007F7846 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 545BA70821BA3214007F7846 /* MainViewController.m */; };
|
||||||
545BA70C21BA3214007F7846 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545BA70A21BA3214007F7846 /* Main.storyboard */; };
|
545BA70C21BA3214007F7846 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545BA70A21BA3214007F7846 /* Main.storyboard */; };
|
||||||
545BA70E21BA3217007F7846 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 545BA70D21BA3217007F7846 /* Assets.xcassets */; };
|
545BA70E21BA3217007F7846 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 545BA70D21BA3217007F7846 /* Assets.xcassets */; };
|
||||||
545BA71121BA3217007F7846 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545BA70F21BA3217007F7846 /* LaunchScreen.storyboard */; };
|
545BA71121BA3217007F7846 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545BA70F21BA3217007F7846 /* LaunchScreen.storyboard */; };
|
||||||
@@ -35,8 +35,8 @@
|
|||||||
545BA70121BA3214007F7846 /* SamplesObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SamplesObjC.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
545BA70121BA3214007F7846 /* SamplesObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SamplesObjC.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
545BA70421BA3214007F7846 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
545BA70421BA3214007F7846 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||||
545BA70521BA3214007F7846 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
545BA70521BA3214007F7846 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||||
545BA70721BA3214007F7846 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
|
545BA70721BA3214007F7846 /* MainViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = "<group>"; };
|
||||||
545BA70821BA3214007F7846 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
|
545BA70821BA3214007F7846 /* MainViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = "<group>"; };
|
||||||
545BA70B21BA3214007F7846 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
545BA70B21BA3214007F7846 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
545BA70D21BA3217007F7846 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
545BA70D21BA3217007F7846 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
545BA71021BA3217007F7846 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
545BA71021BA3217007F7846 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
545BA71321BA3217007F7846 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
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>"; };
|
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; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -64,6 +65,7 @@
|
|||||||
545BA72521BA3BAF007F7846 /* FloatingPanel.framework */,
|
545BA72521BA3BAF007F7846 /* FloatingPanel.framework */,
|
||||||
545BA70321BA3214007F7846 /* SamplesObjC */,
|
545BA70321BA3214007F7846 /* SamplesObjC */,
|
||||||
545BA70221BA3214007F7846 /* Products */,
|
545BA70221BA3214007F7846 /* Products */,
|
||||||
|
5D82A6AE28D18443006A44BA /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -78,13 +80,13 @@
|
|||||||
545BA70321BA3214007F7846 /* SamplesObjC */ = {
|
545BA70321BA3214007F7846 /* SamplesObjC */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
545BA70421BA3214007F7846 /* AppDelegate.h */,
|
|
||||||
545BA70521BA3214007F7846 /* AppDelegate.m */,
|
|
||||||
545BA70721BA3214007F7846 /* ViewController.h */,
|
|
||||||
545BA70821BA3214007F7846 /* ViewController.m */,
|
|
||||||
545BA70A21BA3214007F7846 /* Main.storyboard */,
|
|
||||||
545BA70D21BA3217007F7846 /* Assets.xcassets */,
|
545BA70D21BA3217007F7846 /* Assets.xcassets */,
|
||||||
545BA70F21BA3217007F7846 /* LaunchScreen.storyboard */,
|
545BA70F21BA3217007F7846 /* LaunchScreen.storyboard */,
|
||||||
|
545BA70A21BA3214007F7846 /* Main.storyboard */,
|
||||||
|
545BA70421BA3214007F7846 /* AppDelegate.h */,
|
||||||
|
545BA70521BA3214007F7846 /* AppDelegate.m */,
|
||||||
|
545BA70721BA3214007F7846 /* MainViewController.h */,
|
||||||
|
545BA70821BA3214007F7846 /* MainViewController.m */,
|
||||||
545BA71221BA3217007F7846 /* Info.plist */,
|
545BA71221BA3217007F7846 /* Info.plist */,
|
||||||
545BA71321BA3217007F7846 /* main.m */,
|
545BA71321BA3217007F7846 /* main.m */,
|
||||||
545BA72221BA3867007F7846 /* SamplesObjC-Bridging-Header.h */,
|
545BA72221BA3867007F7846 /* SamplesObjC-Bridging-Header.h */,
|
||||||
@@ -92,6 +94,14 @@
|
|||||||
path = SamplesObjC;
|
path = SamplesObjC;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
5D82A6AE28D18443006A44BA /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5D82A6AF28D18443006A44BA /* libswiftCoreGraphics.tbd */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -119,7 +129,7 @@
|
|||||||
545BA6F921BA3214007F7846 /* Project object */ = {
|
545BA6F921BA3214007F7846 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 1110;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = "Shin Yamamoto";
|
ORGANIZATIONNAME = "Shin Yamamoto";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
545BA70021BA3214007F7846 = {
|
545BA70021BA3214007F7846 = {
|
||||||
@@ -164,7 +174,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
545BA70921BA3214007F7846 /* ViewController.m in Sources */,
|
545BA70921BA3214007F7846 /* MainViewController.m in Sources */,
|
||||||
545BA71421BA3217007F7846 /* main.m in Sources */,
|
545BA71421BA3217007F7846 /* main.m in Sources */,
|
||||||
545BA70621BA3214007F7846 /* AppDelegate.m in Sources */,
|
545BA70621BA3214007F7846 /* AppDelegate.m in Sources */,
|
||||||
);
|
);
|
||||||
@@ -218,6 +228,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -277,6 +288,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -318,7 +330,11 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.SamplesObjC;
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SDKROOT)/usr/lib/swift",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = example.SamplesObjC;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "SamplesObjC/SamplesObjC-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "SamplesObjC/SamplesObjC-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
@@ -340,7 +356,11 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.SamplesObjC;
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SDKROOT)/usr/lib/swift",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = example.SamplesObjC;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "SamplesObjC/SamplesObjC-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "SamplesObjC/SamplesObjC-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 4.2;
|
SWIFT_VERSION = 4.2;
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1110"
|
LastUpgradeVersion = "1300"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
<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="BYZ-38-t0r">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--View Controller-->
|
<!--Main View Controller-->
|
||||||
<scene sceneID="tne-QT-ifu">
|
<scene sceneID="tne-QT-ifu">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
|
<viewController id="BYZ-38-t0r" customClass="MainViewController" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
||||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||||
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
@import FloatingPanel;
|
@import FloatingPanel;
|
||||||
|
|
||||||
@interface ViewController : UIViewController
|
@interface MainViewController : UIViewController
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MyFloatingPanelLayout : NSObject <FloatingPanelLayout>
|
@interface MyFloatingPanelLayout : NSObject <FloatingPanelLayout>
|
||||||
+4
-5
@@ -1,6 +1,6 @@
|
|||||||
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
#import "ViewController.h"
|
#import "MainViewController.h"
|
||||||
@import FloatingPanel;
|
@import FloatingPanel;
|
||||||
|
|
||||||
// Defining a custom FloatingPanelState
|
// Defining a custom FloatingPanelState
|
||||||
@@ -19,24 +19,23 @@ static FloatingPanelState *_lastQuart;
|
|||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface ViewController()<FloatingPanelControllerDelegate>
|
@interface MainViewController()<FloatingPanelControllerDelegate>
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation ViewController
|
@implementation MainViewController
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
- (void)viewDidLoad {
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
|
|
||||||
FloatingPanelController *fpc = [[FloatingPanelController alloc] init];
|
FloatingPanelController *fpc = [[FloatingPanelController alloc] init];
|
||||||
[fpc setContentViewController:nil];
|
[fpc setContentViewController:nil];
|
||||||
[fpc trackScrollView:nil];
|
|
||||||
[fpc setDelegate:self];
|
[fpc setDelegate:self];
|
||||||
|
|
||||||
[fpc setLayout: [MyFloatingPanelLayout new]];
|
[fpc setLayout: [MyFloatingPanelLayout new]];
|
||||||
[fpc setBehavior:[MyFloatingPanelBehavior new]];
|
[fpc setBehavior:[MyFloatingPanelBehavior new]];
|
||||||
[fpc setRemovalInteractionEnabled:NO];
|
[fpc setRemovalInteractionEnabled:NO];
|
||||||
|
|
||||||
[fpc addPanelToParent:self at:self.view.subviews.count animated:NO];
|
[fpc addPanelToParent:self at:self.view.subviews.count animated:NO completion:nil];
|
||||||
[fpc moveToState:FloatingPanelState.Tip animated:true completion:nil];
|
[fpc moveToState:FloatingPanelState.Tip animated:true completion:nil];
|
||||||
|
|
||||||
[self updateAppearance: fpc];
|
[self updateAppearance: fpc];
|
||||||
@@ -8,12 +8,13 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
548DF95421705BE00041922A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 548DF95321705BE00041922A /* AppDelegate.swift */; };
|
548DF95421705BE00041922A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 548DF95321705BE00041922A /* AppDelegate.swift */; };
|
||||||
548DF95621705BE00041922A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 548DF95521705BE00041922A /* ViewController.swift */; };
|
548DF95621705BE00041922A /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 548DF95521705BE00041922A /* MainViewController.swift */; };
|
||||||
548DF95921705BE00041922A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 548DF95721705BE00041922A /* Main.storyboard */; };
|
548DF95921705BE00041922A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 548DF95721705BE00041922A /* Main.storyboard */; };
|
||||||
548DF95B21705BE10041922A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 548DF95A21705BE10041922A /* Assets.xcassets */; };
|
548DF95B21705BE10041922A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 548DF95A21705BE10041922A /* Assets.xcassets */; };
|
||||||
548DF95E21705BE10041922A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 548DF95C21705BE10041922A /* LaunchScreen.storyboard */; };
|
548DF95E21705BE10041922A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 548DF95C21705BE10041922A /* LaunchScreen.storyboard */; };
|
||||||
549D23CF233C77CF008EF4D7 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23CE233C77CF008EF4D7 /* FloatingPanel.framework */; };
|
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, ); }; };
|
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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
@@ -33,12 +34,13 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
548DF95021705BE00041922A /* Stocks.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Stocks.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
548DF95021705BE00041922A /* Stocks.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Stocks.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
548DF95321705BE00041922A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
548DF95321705BE00041922A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
548DF95521705BE00041922A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
548DF95521705BE00041922A /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
|
||||||
548DF95821705BE00041922A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
548DF95821705BE00041922A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
548DF95A21705BE10041922A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
548DF95A21705BE10041922A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
548DF95D21705BE10041922A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
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>"; };
|
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; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -46,6 +48,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
5D82A6AA28D18432006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
|
||||||
549D23CF233C77CF008EF4D7 /* FloatingPanel.framework in Frameworks */,
|
549D23CF233C77CF008EF4D7 /* FloatingPanel.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -59,6 +62,7 @@
|
|||||||
549D23CE233C77CF008EF4D7 /* FloatingPanel.framework */,
|
549D23CE233C77CF008EF4D7 /* FloatingPanel.framework */,
|
||||||
548DF95221705BE00041922A /* Stocks */,
|
548DF95221705BE00041922A /* Stocks */,
|
||||||
548DF95121705BE00041922A /* Products */,
|
548DF95121705BE00041922A /* Products */,
|
||||||
|
5D82A6A828D1842A006A44BA /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -73,16 +77,24 @@
|
|||||||
548DF95221705BE00041922A /* Stocks */ = {
|
548DF95221705BE00041922A /* Stocks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
548DF95321705BE00041922A /* AppDelegate.swift */,
|
|
||||||
548DF95521705BE00041922A /* ViewController.swift */,
|
|
||||||
548DF95721705BE00041922A /* Main.storyboard */,
|
|
||||||
548DF95A21705BE10041922A /* Assets.xcassets */,
|
548DF95A21705BE10041922A /* Assets.xcassets */,
|
||||||
548DF95C21705BE10041922A /* LaunchScreen.storyboard */,
|
548DF95C21705BE10041922A /* LaunchScreen.storyboard */,
|
||||||
|
548DF95721705BE00041922A /* Main.storyboard */,
|
||||||
|
548DF95321705BE00041922A /* AppDelegate.swift */,
|
||||||
|
548DF95521705BE00041922A /* MainViewController.swift */,
|
||||||
548DF95F21705BE10041922A /* Info.plist */,
|
548DF95F21705BE10041922A /* Info.plist */,
|
||||||
);
|
);
|
||||||
path = Stocks;
|
path = Stocks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
5D82A6A828D1842A006A44BA /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5D82A6A928D1842B006A44BA /* libswiftCoreGraphics.tbd */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -111,7 +123,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 1000;
|
LastSwiftUpdateCheck = 1000;
|
||||||
LastUpgradeCheck = 1000;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = scenee;
|
ORGANIZATIONNAME = scenee;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
548DF94F21705BE00041922A = {
|
548DF94F21705BE00041922A = {
|
||||||
@@ -155,7 +167,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
548DF95621705BE00041922A /* ViewController.swift in Sources */,
|
548DF95621705BE00041922A /* MainViewController.swift in Sources */,
|
||||||
548DF95421705BE00041922A /* AppDelegate.swift in Sources */,
|
548DF95421705BE00041922A /* AppDelegate.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -208,6 +220,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -269,6 +282,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -310,7 +324,11 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.Stocks;
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SDKROOT)/usr/lib/swift",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = example.Stocks;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
@@ -329,7 +347,11 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.Stocks;
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SDKROOT)/usr/lib/swift",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = example.Stocks;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1000"
|
LastUpgradeVersion = "1300"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -27,8 +27,6 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
@@ -38,8 +36,8 @@
|
|||||||
ReferencedContainer = "container:Stocks.xcodeproj">
|
ReferencedContainer = "container:Stocks.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
<Testables>
|
||||||
</AdditionalOptions>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@@ -61,8 +59,6 @@
|
|||||||
ReferencedContainer = "container:Stocks.xcodeproj">
|
ReferencedContainer = "container:Stocks.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
<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="BYZ-38-t0r">
|
||||||
<device id="retina4_7" orientation="portrait">
|
|
||||||
<adaptation id="fullscreen"/>
|
|
||||||
</device>
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--View Controller-->
|
<!--Main View Controller-->
|
||||||
<scene sceneID="tne-QT-ifu">
|
<scene sceneID="tne-QT-ifu">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="Stocks" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="BYZ-38-t0r" customClass="MainViewController" customModule="Stocks" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Uop-sw-I6p">
|
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Uop-sw-I6p">
|
||||||
<rect key="frame" x="0.0" y="85" width="375" height="537.5"/>
|
<rect key="frame" x="0.0" y="65" width="600" height="490.5"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" placeholderIntrinsicWidth="375" placeholderIntrinsicHeight="625" image="stocks_list" translatesAutoresizingMaskIntoConstraints="NO" id="XJR-iK-fem">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" placeholderIntrinsicWidth="375" placeholderIntrinsicHeight="625" image="stocks_list" translatesAutoresizingMaskIntoConstraints="NO" id="XJR-iK-fem">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="625"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="625"/>
|
||||||
@@ -34,10 +30,10 @@
|
|||||||
</constraints>
|
</constraints>
|
||||||
</scrollView>
|
</scrollView>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dFl-81-6ok">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dFl-81-6ok">
|
||||||
<rect key="frame" x="0.0" y="622.5" width="375" height="44.5"/>
|
<rect key="frame" x="0.0" y="555.5" width="600" height="44.5"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="yahoo_bottom_bar" translatesAutoresizingMaskIntoConstraints="NO" id="NKr-gS-mpx">
|
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="yahoo_bottom_bar" translatesAutoresizingMaskIntoConstraints="NO" id="NKr-gS-mpx">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="44.5"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="44.5" id="B5t-ZF-qUj"/>
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="44.5" id="B5t-ZF-qUj"/>
|
||||||
@@ -53,10 +49,10 @@
|
|||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="-8" translatesAutoresizingMaskIntoConstraints="NO" id="f7r-Al-pIN">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="-8" translatesAutoresizingMaskIntoConstraints="NO" id="f7r-Al-pIN">
|
||||||
<rect key="frame" x="16" y="20" width="153.5" height="57"/>
|
<rect key="frame" x="16" y="0.0" width="153.5" height="57"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="STOCKS" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PCG-Wl-fXa">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="STOCKS" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PCG-Wl-fXa">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="111.5" height="32.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="111" height="32.5"/>
|
||||||
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="27"/>
|
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="27"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
@@ -70,6 +66,7 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
</subviews>
|
</subviews>
|
||||||
|
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||||
<color key="backgroundColor" red="0.11764924973249435" green="0.11764311045408249" blue="0.11764728277921677" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
|
<color key="backgroundColor" red="0.11764924973249435" green="0.11764311045408249" blue="0.11764728277921677" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="dFl-81-6ok" secondAttribute="trailing" id="20i-yz-AaQ"/>
|
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="dFl-81-6ok" secondAttribute="trailing" id="20i-yz-AaQ"/>
|
||||||
@@ -83,7 +80,6 @@
|
|||||||
<constraint firstItem="dFl-81-6ok" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="nlX-Ab-1aI"/>
|
<constraint firstItem="dFl-81-6ok" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="nlX-Ab-1aI"/>
|
||||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="NKr-gS-mpx" secondAttribute="bottom" id="yeu-NH-Pmp"/>
|
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="NKr-gS-mpx" secondAttribute="bottom" id="yeu-NH-Pmp"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="bottomToolView" destination="dFl-81-6ok" id="NXn-af-lFv"/>
|
<outlet property="bottomToolView" destination="dFl-81-6ok" id="NXn-af-lFv"/>
|
||||||
@@ -122,6 +118,7 @@
|
|||||||
</constraints>
|
</constraints>
|
||||||
</scrollView>
|
</scrollView>
|
||||||
</subviews>
|
</subviews>
|
||||||
|
<viewLayoutGuide key="safeArea" id="INo-op-FLO"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="bottom" secondItem="h7M-7T-4k4" secondAttribute="bottom" id="2b8-Wo-tm0"/>
|
<constraint firstAttribute="bottom" secondItem="h7M-7T-4k4" secondAttribute="bottom" id="2b8-Wo-tm0"/>
|
||||||
@@ -129,7 +126,6 @@
|
|||||||
<constraint firstItem="h7M-7T-4k4" firstAttribute="trailing" secondItem="INo-op-FLO" secondAttribute="trailing" id="WTc-1l-3Ha"/>
|
<constraint firstItem="h7M-7T-4k4" firstAttribute="trailing" secondItem="INo-op-FLO" secondAttribute="trailing" id="WTc-1l-3Ha"/>
|
||||||
<constraint firstItem="h7M-7T-4k4" firstAttribute="leading" secondItem="INo-op-FLO" secondAttribute="leading" id="na8-TO-WiG"/>
|
<constraint firstItem="h7M-7T-4k4" firstAttribute="leading" secondItem="INo-op-FLO" secondAttribute="leading" id="na8-TO-WiG"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<viewLayoutGuide key="safeArea" id="INo-op-FLO"/>
|
|
||||||
</view>
|
</view>
|
||||||
<size key="freeformSize" width="375" height="600"/>
|
<size key="freeformSize" width="375" height="600"/>
|
||||||
<connections>
|
<connections>
|
||||||
|
|||||||
+8
-10
@@ -3,7 +3,7 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import FloatingPanel
|
import FloatingPanel
|
||||||
|
|
||||||
class ViewController: UIViewController, FloatingPanelControllerDelegate {
|
class MainViewController: UIViewController, FloatingPanelControllerDelegate {
|
||||||
@IBOutlet var topBannerView: UIImageView!
|
@IBOutlet var topBannerView: UIImageView!
|
||||||
@IBOutlet weak var labelStackView: UIStackView!
|
@IBOutlet weak var labelStackView: UIStackView!
|
||||||
@IBOutlet weak var bottomToolView: UIView!
|
@IBOutlet weak var bottomToolView: UIView!
|
||||||
@@ -81,7 +81,7 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func hideStockTickerBanner() {
|
private func hideStockTickerBanner() {
|
||||||
// Dimiss top bar with dissolve animation
|
// Dismiss top bar with dissolve animation
|
||||||
UIView.animate(withDuration: 0.25) {
|
UIView.animate(withDuration: 0.25) {
|
||||||
self.topBannerView.alpha = 0.0
|
self.topBannerView.alpha = 0.0
|
||||||
self.labelStackView.alpha = 1.0
|
self.labelStackView.alpha = 1.0
|
||||||
@@ -101,14 +101,12 @@ class FloatingPanelStocksLayout: FloatingPanelLayout {
|
|||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
let initialState: FloatingPanelState = .tip
|
let initialState: FloatingPanelState = .tip
|
||||||
|
|
||||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 56.0, edge: .top, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 56.0, edge: .top, referenceGuide: .safeArea),
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 262.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 262.0, edge: .bottom, referenceGuide: .safeArea),
|
/* Visible + ToolView */
|
||||||
/* Visible + ToolView */
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: 85.0 + 44.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 85.0 + 44.0, edge: .bottom, referenceGuide: .safeArea),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
|
|
||||||
s.name = "FloatingPanel"
|
s.name = "FloatingPanel"
|
||||||
s.version = "2.3.1"
|
s.version = "2.8.6"
|
||||||
s.summary = "FloatingPanel is a clean and easy-to-use UI component of a floating panel interface."
|
s.summary = "FloatingPanel is a clean and easy-to-use UI component of a floating panel interface."
|
||||||
s.description = <<-DESC
|
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.
|
FloatingPanel is a clean 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.
|
The new interface displays the related contents and utilities in parallel as a user wants.
|
||||||
DESC
|
DESC
|
||||||
s.homepage = "https://github.com/SCENEE/FloatingPanel"
|
s.homepage = "https://github.com/scenee/FloatingPanel"
|
||||||
s.author = "Shin Yamamoto"
|
s.author = "Shin Yamamoto"
|
||||||
s.social_media_url = "https://twitter.com/scenee"
|
s.social_media_url = "https://twitter.com/scenee"
|
||||||
|
|
||||||
s.platform = :ios, "10.0"
|
s.platform = :ios, "11.0"
|
||||||
s.source = { :git => "https://github.com/SCENEE/FloatingPanel.git", :tag => s.version.to_s }
|
s.source = { :git => "https://github.com/scenee/FloatingPanel.git", :tag => s.version.to_s }
|
||||||
s.source_files = "Sources/*.swift"
|
s.source_files = "Sources/*.swift"
|
||||||
s.swift_versions = ['5.1', '5.2', '5.3']
|
s.swift_version = '5.0'
|
||||||
|
|
||||||
s.framework = "UIKit"
|
s.framework = "UIKit"
|
||||||
|
|
||||||
|
|||||||
@@ -17,22 +17,23 @@
|
|||||||
545DB9E021511AC100CA77B8 /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9DF21511AC100CA77B8 /* Controller.swift */; };
|
545DB9E021511AC100CA77B8 /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9DF21511AC100CA77B8 /* Controller.swift */; };
|
||||||
545DBA2B2152383100CA77B8 /* GrabberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DBA2A2152383100CA77B8 /* GrabberView.swift */; };
|
545DBA2B2152383100CA77B8 /* GrabberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DBA2A2152383100CA77B8 /* GrabberView.swift */; };
|
||||||
546055BF2333C4740069F400 /* TestSupports.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542753C722C49A8F00D17955 /* TestSupports.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 */; };
|
5469F4AE24B30D7E00537F8A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4AD24B30D7E00537F8A /* State.swift */; };
|
||||||
5469F4B024B30E1500537F8A /* LayoutAnchoring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4AF24B30E1500537F8A /* LayoutAnchoring.swift */; };
|
5469F4B024B30E1500537F8A /* LayoutAnchoring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4AF24B30E1500537F8A /* LayoutAnchoring.swift */; };
|
||||||
5469F4B224B30F1100537F8A /* Position.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4B124B30F1100537F8A /* Position.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 */; };
|
||||||
|
547F7A9C2A6E946000303905 /* GestureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 547F7A9B2A6E946000303905 /* GestureTests.swift */; };
|
||||||
549C371F2361E15E007D8058 /* ExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549C371E2361E15D007D8058 /* ExtensionTests.swift */; };
|
549C371F2361E15E007D8058 /* ExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549C371E2361E15D007D8058 /* ExtensionTests.swift */; };
|
||||||
549E944522CF295D0050AECF /* StateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549E944422CF295D0050AECF /* StateTests.swift */; };
|
549E944522CF295D0050AECF /* StateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549E944422CF295D0050AECF /* StateTests.swift */; };
|
||||||
54A6B6B122968B530077F348 /* CoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A6B6B022968B530077F348 /* CoreTests.swift */; };
|
54A6B6B122968B530077F348 /* CoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A6B6B022968B530077F348 /* CoreTests.swift */; };
|
||||||
54A6B6B82296A8520077F348 /* SurfaceViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A6B6B72296A8520077F348 /* SurfaceViewTests.swift */; };
|
54A6B6B82296A8520077F348 /* SurfaceViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A6B6B72296A8520077F348 /* SurfaceViewTests.swift */; };
|
||||||
54ABD7AF216CCFF7002E6C13 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54ABD7AE216CCFF7002E6C13 /* Logger.swift */; };
|
54ABD7AF216CCFF7002E6C13 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54ABD7AE216CCFF7002E6C13 /* Logging.swift */; };
|
||||||
54CDC5D3215B6D5A007D205C /* SurfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D2215B6D5A007D205C /* SurfaceView.swift */; };
|
54CDC5D3215B6D5A007D205C /* SurfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D2215B6D5A007D205C /* SurfaceView.swift */; };
|
||||||
54CDC5D5215B6D8D007D205C /* BackdropView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D4215B6D8D007D205C /* BackdropView.swift */; };
|
54CDC5D5215B6D8D007D205C /* BackdropView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D4215B6D8D007D205C /* BackdropView.swift */; };
|
||||||
54CFBFC3215CD045006B5735 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CFBFC2215CD045006B5735 /* Layout.swift */; };
|
54CFBFC3215CD045006B5735 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CFBFC2215CD045006B5735 /* Layout.swift */; };
|
||||||
54CFBFC5215CD09C006B5735 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CFBFC4215CD09C006B5735 /* Core.swift */; };
|
54CFBFC5215CD09C006B5735 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CFBFC4215CD09C006B5735 /* Core.swift */; };
|
||||||
54DBA3DC262E938500D75969 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54DBA3DB262E938500D75969 /* Extensions.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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -43,13 +44,6 @@
|
|||||||
remoteGlobalIDString = 545DB9C02151169500CA77B8;
|
remoteGlobalIDString = 545DB9C02151169500CA77B8;
|
||||||
remoteInfo = FloatingModalController;
|
remoteInfo = FloatingModalController;
|
||||||
};
|
};
|
||||||
54E740DC218AFE9F005C1A34 /* PBXContainerItemProxy */ = {
|
|
||||||
isa = PBXContainerItemProxy;
|
|
||||||
containerPortal = 545DB9B82151169500CA77B8 /* Project object */;
|
|
||||||
proxyType = 1;
|
|
||||||
remoteGlobalIDString = 54E740C9218AFD67005C1A34;
|
|
||||||
remoteInfo = TestingHost;
|
|
||||||
};
|
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@@ -66,24 +60,23 @@
|
|||||||
545DB9D12151169500CA77B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
||||||
|
547F7A9B2A6E946000303905 /* GestureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GestureTests.swift; sourceTree = "<group>"; };
|
||||||
549C371E2361E15D007D8058 /* ExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionTests.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>"; };
|
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>"; };
|
54A6B6B022968B530077F348 /* CoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreTests.swift; sourceTree = "<group>"; };
|
||||||
54A6B6B72296A8520077F348 /* SurfaceViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceViewTests.swift; sourceTree = "<group>"; };
|
54A6B6B72296A8520077F348 /* SurfaceViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceViewTests.swift; sourceTree = "<group>"; };
|
||||||
54ABD7AE216CCFF7002E6C13 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
54ABD7AE216CCFF7002E6C13 /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
|
||||||
54CDC5D2215B6D5A007D205C /* SurfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView.swift; sourceTree = "<group>"; };
|
54CDC5D2215B6D5A007D205C /* SurfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView.swift; sourceTree = "<group>"; };
|
||||||
54CDC5D4215B6D8D007D205C /* BackdropView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackdropView.swift; sourceTree = "<group>"; };
|
54CDC5D4215B6D8D007D205C /* BackdropView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackdropView.swift; sourceTree = "<group>"; };
|
||||||
54CFBFC2215CD045006B5735 /* Layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = "<group>"; };
|
54CFBFC2215CD045006B5735 /* Layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = "<group>"; };
|
||||||
54CFBFC4215CD09C006B5735 /* Core.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Core.swift; sourceTree = "<group>"; };
|
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>"; };
|
54DBA3DB262E938500D75969 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
|
||||||
54E740CA218AFD67005C1A34 /* FloatingPanelTesting.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FloatingPanelTesting.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
54E3992627141F5100A8F9ED /* FloatingPanel.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = FloatingPanel.docc; sourceTree = "<group>"; };
|
||||||
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -91,6 +84,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
5D82A6B528D18464006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -102,13 +96,6 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
54E740C7218AFD67005C1A34 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
@@ -118,6 +105,7 @@
|
|||||||
545DB9C32151169500CA77B8 /* Sources */,
|
545DB9C32151169500CA77B8 /* Sources */,
|
||||||
545DB9CE2151169500CA77B8 /* Tests */,
|
545DB9CE2151169500CA77B8 /* Tests */,
|
||||||
545DB9C22151169500CA77B8 /* Products */,
|
545DB9C22151169500CA77B8 /* Products */,
|
||||||
|
5D82A6B328D18460006A44BA /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -126,7 +114,6 @@
|
|||||||
children = (
|
children = (
|
||||||
545DB9C12151169500CA77B8 /* FloatingPanel.framework */,
|
545DB9C12151169500CA77B8 /* FloatingPanel.framework */,
|
||||||
545DB9CA2151169500CA77B8 /* FloatingPanelTests.xctest */,
|
545DB9CA2151169500CA77B8 /* FloatingPanelTests.xctest */,
|
||||||
54E740CA218AFD67005C1A34 /* FloatingPanelTesting.app */,
|
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -139,7 +126,7 @@
|
|||||||
5469F4B124B30F1100537F8A /* Position.swift */,
|
5469F4B124B30F1100537F8A /* Position.swift */,
|
||||||
54CFBFC4215CD09C006B5735 /* Core.swift */,
|
54CFBFC4215CD09C006B5735 /* Core.swift */,
|
||||||
54CFBFC2215CD045006B5735 /* Layout.swift */,
|
54CFBFC2215CD045006B5735 /* Layout.swift */,
|
||||||
5469F4B324B30F3500537F8A /* LayoutReferences.swift */,
|
5469F4B324B30F3500537F8A /* LayoutProperties.swift */,
|
||||||
5469F4AF24B30E1500537F8A /* LayoutAnchoring.swift */,
|
5469F4AF24B30E1500537F8A /* LayoutAnchoring.swift */,
|
||||||
5450EEE321646DF500135936 /* Behavior.swift */,
|
5450EEE321646DF500135936 /* Behavior.swift */,
|
||||||
54352E9721A521CA00CBCA08 /* PassthroughView.swift */,
|
54352E9721A521CA00CBCA08 /* PassthroughView.swift */,
|
||||||
@@ -148,9 +135,10 @@
|
|||||||
545DBA2A2152383100CA77B8 /* GrabberView.swift */,
|
545DBA2A2152383100CA77B8 /* GrabberView.swift */,
|
||||||
54352E9521A51A2500CBCA08 /* Transitioning.swift */,
|
54352E9521A51A2500CBCA08 /* Transitioning.swift */,
|
||||||
54DBA3DB262E938500D75969 /* Extensions.swift */,
|
54DBA3DB262E938500D75969 /* Extensions.swift */,
|
||||||
54ABD7AE216CCFF7002E6C13 /* Logger.swift */,
|
54ABD7AE216CCFF7002E6C13 /* Logging.swift */,
|
||||||
545DB9C42151169500CA77B8 /* FloatingPanel.h */,
|
545DB9C42151169500CA77B8 /* FloatingPanel.h */,
|
||||||
545DB9C52151169500CA77B8 /* Info.plist */,
|
545DB9C52151169500CA77B8 /* Info.plist */,
|
||||||
|
54E3992627141F5100A8F9ED /* FloatingPanel.docc */,
|
||||||
);
|
);
|
||||||
path = Sources;
|
path = Sources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -158,9 +146,9 @@
|
|||||||
545DB9CE2151169500CA77B8 /* Tests */ = {
|
545DB9CE2151169500CA77B8 /* Tests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5469F49E24B003EF00537F8A /* TestingApp */,
|
|
||||||
54A6B6B022968B530077F348 /* CoreTests.swift */,
|
54A6B6B022968B530077F348 /* CoreTests.swift */,
|
||||||
545DB9CF2151169500CA77B8 /* ControllerTests.swift */,
|
545DB9CF2151169500CA77B8 /* ControllerTests.swift */,
|
||||||
|
547F7A9B2A6E946000303905 /* GestureTests.swift */,
|
||||||
542753C522C49A6E00D17955 /* LayoutTests.swift */,
|
542753C522C49A6E00D17955 /* LayoutTests.swift */,
|
||||||
54A6B6B72296A8520077F348 /* SurfaceViewTests.swift */,
|
54A6B6B72296A8520077F348 /* SurfaceViewTests.swift */,
|
||||||
549E944422CF295D0050AECF /* StateTests.swift */,
|
549E944422CF295D0050AECF /* StateTests.swift */,
|
||||||
@@ -171,14 +159,12 @@
|
|||||||
path = Tests;
|
path = Tests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
5469F49E24B003EF00537F8A /* TestingApp */ = {
|
5D82A6B328D18460006A44BA /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5469F49F24B003EF00537F8A /* LaunchScreen.storyboard */,
|
5D82A6B428D18461006A44BA /* libswiftCoreGraphics.tbd */,
|
||||||
5469F4A024B003EF00537F8A /* AppDelegate.swift */,
|
|
||||||
5469F4A124B003EF00537F8A /* Info.plist */,
|
|
||||||
);
|
);
|
||||||
path = TestingApp;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
@@ -225,30 +211,12 @@
|
|||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
545DB9CD2151169500CA77B8 /* PBXTargetDependency */,
|
545DB9CD2151169500CA77B8 /* PBXTargetDependency */,
|
||||||
54E740DD218AFE9F005C1A34 /* PBXTargetDependency */,
|
|
||||||
);
|
);
|
||||||
name = FloatingPanelTests;
|
name = FloatingPanelTests;
|
||||||
productName = FloatingModalControllerTests;
|
productName = FloatingModalControllerTests;
|
||||||
productReference = 545DB9CA2151169500CA77B8 /* FloatingPanelTests.xctest */;
|
productReference = 545DB9CA2151169500CA77B8 /* FloatingPanelTests.xctest */;
|
||||||
productType = "com.apple.product-type.bundle.unit-test";
|
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 */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
@@ -256,7 +224,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 1010;
|
LastSwiftUpdateCheck = 1010;
|
||||||
LastUpgradeCheck = 1000;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = scenee;
|
ORGANIZATIONNAME = scenee;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
545DB9C02151169500CA77B8 = {
|
545DB9C02151169500CA77B8 = {
|
||||||
@@ -265,10 +233,6 @@
|
|||||||
};
|
};
|
||||||
545DB9C92151169500CA77B8 = {
|
545DB9C92151169500CA77B8 = {
|
||||||
CreatedOnToolsVersion = 10.0;
|
CreatedOnToolsVersion = 10.0;
|
||||||
TestTargetID = 54E740C9218AFD67005C1A34;
|
|
||||||
};
|
|
||||||
54E740C9218AFD67005C1A34 = {
|
|
||||||
CreatedOnToolsVersion = 10.1;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -287,7 +251,6 @@
|
|||||||
targets = (
|
targets = (
|
||||||
545DB9C02151169500CA77B8 /* FloatingPanel */,
|
545DB9C02151169500CA77B8 /* FloatingPanel */,
|
||||||
545DB9C92151169500CA77B8 /* FloatingPanelTests */,
|
545DB9C92151169500CA77B8 /* FloatingPanelTests */,
|
||||||
54E740C9218AFD67005C1A34 /* TestingApp */,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@@ -307,14 +270,6 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
54E740C8218AFD67005C1A34 /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
5469F4A224B003EF00537F8A /* LaunchScreen.storyboard in Resources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
@@ -326,15 +281,16 @@
|
|||||||
5469F4B024B30E1500537F8A /* LayoutAnchoring.swift in Sources */,
|
5469F4B024B30E1500537F8A /* LayoutAnchoring.swift in Sources */,
|
||||||
54CDC5D3215B6D5A007D205C /* SurfaceView.swift in Sources */,
|
54CDC5D3215B6D5A007D205C /* SurfaceView.swift in Sources */,
|
||||||
54CFBFC3215CD045006B5735 /* Layout.swift in Sources */,
|
54CFBFC3215CD045006B5735 /* Layout.swift in Sources */,
|
||||||
5469F4B424B30F3500537F8A /* LayoutReferences.swift in Sources */,
|
5469F4B424B30F3500537F8A /* LayoutProperties.swift in Sources */,
|
||||||
54CDC5D5215B6D8D007D205C /* BackdropView.swift in Sources */,
|
54CDC5D5215B6D8D007D205C /* BackdropView.swift in Sources */,
|
||||||
54352E9821A521CA00CBCA08 /* PassthroughView.swift in Sources */,
|
54352E9821A521CA00CBCA08 /* PassthroughView.swift in Sources */,
|
||||||
54CFBFC5215CD09C006B5735 /* Core.swift in Sources */,
|
54CFBFC5215CD09C006B5735 /* Core.swift in Sources */,
|
||||||
54ABD7AF216CCFF7002E6C13 /* Logger.swift in Sources */,
|
54ABD7AF216CCFF7002E6C13 /* Logging.swift in Sources */,
|
||||||
545DB9E021511AC100CA77B8 /* Controller.swift in Sources */,
|
545DB9E021511AC100CA77B8 /* Controller.swift in Sources */,
|
||||||
54DBA3DC262E938500D75969 /* Extensions.swift in Sources */,
|
54DBA3DC262E938500D75969 /* Extensions.swift in Sources */,
|
||||||
5450EEE421646DF500135936 /* Behavior.swift in Sources */,
|
5450EEE421646DF500135936 /* Behavior.swift in Sources */,
|
||||||
545DBA2B2152383100CA77B8 /* GrabberView.swift in Sources */,
|
545DBA2B2152383100CA77B8 /* GrabberView.swift in Sources */,
|
||||||
|
54E3992727141F5100A8F9ED /* FloatingPanel.docc in Sources */,
|
||||||
54352E9621A51A2500CBCA08 /* Transitioning.swift in Sources */,
|
54352E9621A51A2500CBCA08 /* Transitioning.swift in Sources */,
|
||||||
5469F4AE24B30D7E00537F8A /* State.swift in Sources */,
|
5469F4AE24B30D7E00537F8A /* State.swift in Sources */,
|
||||||
);
|
);
|
||||||
@@ -351,14 +307,7 @@
|
|||||||
542753C622C49A6E00D17955 /* LayoutTests.swift in Sources */,
|
542753C622C49A6E00D17955 /* LayoutTests.swift in Sources */,
|
||||||
54A6B6B82296A8520077F348 /* SurfaceViewTests.swift in Sources */,
|
54A6B6B82296A8520077F348 /* SurfaceViewTests.swift in Sources */,
|
||||||
546055BF2333C4740069F400 /* TestSupports.swift in Sources */,
|
546055BF2333C4740069F400 /* TestSupports.swift in Sources */,
|
||||||
);
|
547F7A9C2A6E946000303905 /* GestureTests.swift in Sources */,
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
54E740C6218AFD67005C1A34 /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
5469F4A324B003EF00537F8A /* AppDelegate.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -370,11 +319,6 @@
|
|||||||
target = 545DB9C02151169500CA77B8 /* FloatingPanel */;
|
target = 545DB9C02151169500CA77B8 /* FloatingPanel */;
|
||||||
targetProxy = 545DB9CC2151169500CA77B8 /* PBXContainerItemProxy */;
|
targetProxy = 545DB9CC2151169500CA77B8 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
54E740DD218AFE9F005C1A34 /* PBXTargetDependency */ = {
|
|
||||||
isa = PBXTargetDependency;
|
|
||||||
target = 54E740C9218AFD67005C1A34 /* TestingApp */;
|
|
||||||
targetProxy = 54E740DC218AFE9F005C1A34 /* PBXContainerItemProxy */;
|
|
||||||
};
|
|
||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
@@ -404,6 +348,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -469,6 +414,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -516,12 +462,13 @@
|
|||||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
INFOPLIST_FILE = Sources/Info.plist;
|
INFOPLIST_FILE = Sources/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@loader_path/Frameworks",
|
"@loader_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanel;
|
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanel;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
@@ -530,6 +477,7 @@
|
|||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
_EXPERIMENTAL_SWIFT_EXPLICIT_MODULES = YES;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@@ -547,18 +495,20 @@
|
|||||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
INFOPLIST_FILE = Sources/Info.plist;
|
INFOPLIST_FILE = Sources/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@loader_path/Frameworks",
|
"@loader_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanel;
|
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanel;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
_EXPERIMENTAL_SWIFT_EXPLICIT_MODULES = YES;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
@@ -569,17 +519,17 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
INFOPLIST_FILE = Tests/Info.plist;
|
INFOPLIST_FILE = Tests/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@loader_path/Frameworks",
|
"@loader_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalControllerTests;
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FloatingPanelTesting.app/FloatingPanelTesting";
|
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@@ -590,53 +540,17 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
INFOPLIST_FILE = Tests/Info.plist;
|
INFOPLIST_FILE = Tests/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@loader_path/Frameworks",
|
"@loader_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalControllerTests;
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
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;
|
name = Release;
|
||||||
};
|
};
|
||||||
@@ -666,6 +580,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -718,20 +633,22 @@
|
|||||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
INFOPLIST_FILE = Sources/Info.plist;
|
INFOPLIST_FILE = Sources/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@loader_path/Frameworks",
|
"@loader_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanel;
|
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanel;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "TEST DEBUG __FP_LOG";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "TEST DEBUG FP_LOG";
|
||||||
SWIFT_COMPILATION_MODE = singlefile;
|
SWIFT_COMPILATION_MODE = singlefile;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
_EXPERIMENTAL_SWIFT_EXPLICIT_MODULES = YES;
|
||||||
};
|
};
|
||||||
name = Test;
|
name = Test;
|
||||||
};
|
};
|
||||||
@@ -742,31 +659,17 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
INFOPLIST_FILE = Tests/Info.plist;
|
INFOPLIST_FILE = Tests/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@loader_path/Frameworks",
|
"@loader_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalControllerTests;
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
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;
|
name = Test;
|
||||||
};
|
};
|
||||||
@@ -803,16 +706,6 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
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 */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 545DB9B82151169500CA77B8 /* Project object */;
|
rootObject = 545DB9B82151169500CA77B8 /* Project object */;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1000"
|
LastUpgradeVersion = "1300"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@@ -27,6 +27,15 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "545DB9C02151169500CA77B8"
|
||||||
|
BuildableName = "FloatingPanel.framework"
|
||||||
|
BlueprintName = "FloatingPanel"
|
||||||
|
ReferencedContainer = "container:FloatingPanel.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
skipped = "NO"
|
skipped = "NO"
|
||||||
@@ -41,17 +50,6 @@
|
|||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
</Testables>
|
</Testables>
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "545DB9C02151169500CA77B8"
|
|
||||||
BuildableName = "FloatingPanel.framework"
|
|
||||||
BlueprintName = "FloatingPanel"
|
|
||||||
ReferencedContainer = "container:FloatingPanel.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@@ -72,8 +70,6 @@
|
|||||||
ReferencedContainer = "container:FloatingPanel.xcodeproj">
|
ReferencedContainer = "container:FloatingPanel.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
<Group
|
<Group
|
||||||
location = "group:Examples"
|
location = "group:Examples"
|
||||||
name = "Examples">
|
name = "Examples">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Maps-SwiftUI/Maps-SwiftUI.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Maps/Maps.xcodeproj">
|
location = "group:Maps/Maps.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import PackageDescription
|
|||||||
let package = Package(
|
let package = Package(
|
||||||
name: "FloatingPanel",
|
name: "FloatingPanel",
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v10)
|
.iOS(.v11)
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
[](https://travis-ci.org/SCENEE/FloatingPanel)
|
[](https://swift.org/)
|
||||||
[](https://cocoapods.org/pods/FloatingPanel)
|
|
||||||
[](https://github.com/Carthage/Carthage)
|
|
||||||
[](https://cocoapods.org/pods/FloatingPanel)
|
[](https://cocoapods.org/pods/FloatingPanel)
|
||||||
[](https://swift.org/)
|
[](https://cocoapods.org/pods/FloatingPanel)
|
||||||
|

|
||||||
|
|
||||||
# FloatingPanel
|
# FloatingPanel
|
||||||
|
|
||||||
FloatingPanel is a simple and easy-to-use UI component for a new interface introduced in Apple Maps, Shortcuts and Stocks app.
|
FloatingPanel is a simple and easy-to-use UI component designed for a user interface featured in Apple Maps, Shortcuts and Stocks app.
|
||||||
The new interface displays the related contents and utilities in parallel as a user wants.
|
The user interface displays related content and utilities alongside the main content.
|
||||||
|
|
||||||
|
Please see also [the API reference@SPI](https://swiftpackageindex.com/scenee/FloatingPanel/2.8.6/documentation/floatingpanel) for more details.
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
@@ -19,46 +20,46 @@ The new interface displays the related contents and utilities in parallel as a u
|
|||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
- [Requirements](#requirements)
|
- [Requirements](#requirements)
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [CocoaPods](#cocoapods)
|
- [CocoaPods](#cocoapods)
|
||||||
- [Carthage](#carthage)
|
- [Swift Package Manager](#swift-package-manager)
|
||||||
- [Swift Package Manager](#swift-package-manager)
|
|
||||||
- [Getting Started](#getting-started)
|
- [Getting Started](#getting-started)
|
||||||
- [Add a floating panel as a child view controller](#add-a-floating-panel-as-a-child-view-controller)
|
- [Add a floating panel as a child view controller](#add-a-floating-panel-as-a-child-view-controller)
|
||||||
- [Present a floating panel as a modality](#present-a-floating-panel-as-a-modality)
|
- [Present a floating panel as a modality](#present-a-floating-panel-as-a-modality)
|
||||||
- [View hierarchy](#view-hierarchy)
|
- [View hierarchy](#view-hierarchy)
|
||||||
- [Usage](#usage)
|
- [Usage](#usage)
|
||||||
- [Show/Hide a floating panel in a view with your view hierarchy](#showhide-a-floating-panel-in-a-view-with-your-view-hierarchy)
|
- [Show/Hide a floating panel in a view with your view hierarchy](#showhide-a-floating-panel-in-a-view-with-your-view-hierarchy)
|
||||||
- [Scale the content view when the surface position changes](#scale-the-content-view-when-the-surface-position-changes)
|
- [Scale the content view when the surface position changes](#scale-the-content-view-when-the-surface-position-changes)
|
||||||
- [Customize the layout with `FloatingPanelLayout` protocol](#customize-the-layout-with-floatingpanellayout-protocol)
|
- [Customize the layout with `FloatingPanelLayout` protocol](#customize-the-layout-with-floatingpanellayout-protocol)
|
||||||
- [Change the initial layout](#change-the-initial-layout)
|
- [Change the initial layout](#change-the-initial-layout)
|
||||||
- [Update your panel layout](#update-your-panel-layout)
|
- [Update your panel layout](#update-your-panel-layout)
|
||||||
- [Support your landscape layout](#support-your-landscape-layout)
|
- [Support your landscape layout](#support-your-landscape-layout)
|
||||||
- [Use the intrinsic size of a content in your panel layout](#use-the-intrinsic-size-of-a-content-in-your-panel-layout)
|
- [Use the intrinsic size of a content in your panel layout](#use-the-intrinsic-size-of-a-content-in-your-panel-layout)
|
||||||
- [Specify an anchor for each state by an inset of the `FloatingPanelController.view` frame](#specify-an-anchor-for-each-state-by-an-inset-of-the-floatingpanelcontrollerview-frame)
|
- [Specify an anchor for each state by an inset of the `FloatingPanelController.view` frame](#specify-an-anchor-for-each-state-by-an-inset-of-the-floatingpanelcontrollerview-frame)
|
||||||
- [Change the backdrop alpha](#change-the-backdrop-alpha)
|
- [Change the backdrop alpha](#change-the-backdrop-alpha)
|
||||||
- [Using custome panel states](#using-custome-panel-states)
|
- [Using custome panel states](#using-custome-panel-states)
|
||||||
- [Customize the behavior with `FloatingPanelBehavior` protocol](#customize-the-behavior-with-floatingpanelbehavior-protocol)
|
- [Customize the behavior with `FloatingPanelBehavior` protocol](#customize-the-behavior-with-floatingpanelbehavior-protocol)
|
||||||
- [Modify your floating panel's interaction](#modify-your-floating-panels-interaction)
|
- [Modify your floating panel's interaction](#modify-your-floating-panels-interaction)
|
||||||
- [Activate the rubber-band effect on panel edges](#activate-the-rubber-band-effect-on-panel-edges)
|
- [Activate the rubber-band effect on panel edges](#activate-the-rubber-band-effect-on-panel-edges)
|
||||||
- [Manage the projection of a pan gesture momentum](#manage-the-projection-of-a-pan-gesture-momentum)
|
- [Manage the projection of a pan gesture momentum](#manage-the-projection-of-a-pan-gesture-momentum)
|
||||||
- [Specify the panel move's boundary](#specify-the-panel-moves-boundary)
|
- [Specify the panel move's boundary](#specify-the-panel-moves-boundary)
|
||||||
- [Customize the surface design](#customize-the-surface-design)
|
- [Customize the surface design](#customize-the-surface-design)
|
||||||
- [Modify your surface appearance](#modify-your-surface-appearance)
|
- [Modify your surface appearance](#modify-your-surface-appearance)
|
||||||
- [Use a custom grabber handle](#use-a-custom-grabber-handle)
|
- [Use a custom grabber handle](#use-a-custom-grabber-handle)
|
||||||
- [Customize layout of the grabber handle](#customize-layout-of-the-grabber-handle)
|
- [Customize layout of the grabber handle](#customize-layout-of-the-grabber-handle)
|
||||||
- [Customize content padding from surface edges](#customize-content-padding-from-surface-edges)
|
- [Customize content padding from surface edges](#customize-content-padding-from-surface-edges)
|
||||||
- [Customize margins of the surface edges](#customize-margins-of-the-surface-edges)
|
- [Customize margins of the surface edges](#customize-margins-of-the-surface-edges)
|
||||||
- [Customize gestures](#customize-gestures)
|
- [Customize gestures](#customize-gestures)
|
||||||
- [Suppress the panel interaction](#suppress-the-panel-interaction)
|
- [Suppress the panel interaction](#suppress-the-panel-interaction)
|
||||||
- [Add tap gestures to the surface view](#add-tap-gestures-to-the-surface-view)
|
- [Add tap gestures to the surface view](#add-tap-gestures-to-the-surface-view)
|
||||||
- [Interrupt the delegate methods of `FloatingPanelController.panGestureRecognizer`](#interrupt-the-delegate-methods-of-floatingpanelcontrollerpangesturerecognizer)
|
- [Interrupt the delegate methods of `FloatingPanelController.panGestureRecognizer`](#interrupt-the-delegate-methods-of-floatingpanelcontrollerpangesturerecognizer)
|
||||||
- [Create an additional floating panel for a detail](#create-an-additional-floating-panel-for-a-detail)
|
- [Create an additional floating panel for a detail](#create-an-additional-floating-panel-for-a-detail)
|
||||||
- [Move a position with an animation](#move-a-position-with-an-animation)
|
- [Move a position with an animation](#move-a-position-with-an-animation)
|
||||||
- [Work your contents together with a floating panel behavior](#work-your-contents-together-with-a-floating-panel-behavior)
|
- [Work your contents together with a floating panel behavior](#work-your-contents-together-with-a-floating-panel-behavior)
|
||||||
|
- [Enabling the tap-to-dismiss action of the backdrop view](#enabling-the-tap-to-dismiss-action-of-the-backdrop-view)
|
||||||
|
- [Allow to scroll content of the tracking scroll view in addition to the most expanded state](#allow-to-scroll-content-of-the-tracking-scroll-view-in-addition-to-the-most-expanded-state)
|
||||||
- [Notes](#notes)
|
- [Notes](#notes)
|
||||||
- ['Show' or 'Show Detail' Segues from `FloatingPanelController`'s content view controller](#show-or-show-detail-segues-from-floatingpanelcontrollers-content-view-controller)
|
- ['Show' or 'Show Detail' Segues from `FloatingPanelController`'s content view controller](#show-or-show-detail-segues-from-floatingpanelcontrollers-content-view-controller)
|
||||||
- [UISearchController issue](#uisearchcontroller-issue)
|
- [UISearchController issue](#uisearchcontroller-issue)
|
||||||
- [FloatingPanelSurfaceView's issue on iOS 10](#floatingpanelsurfaceviews-issue-on-ios-10)
|
|
||||||
- [Maintainer](#maintainer)
|
- [Maintainer](#maintainer)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
|
|
||||||
@@ -72,14 +73,14 @@ The new interface displays the related contents and utilities in parallel as a u
|
|||||||
- [x] Removal interaction
|
- [x] Removal interaction
|
||||||
- [x] Multi panel support
|
- [x] Multi panel support
|
||||||
- [x] Modal presentation
|
- [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] 1 or more magnetic anchors(full, half, tip and more)
|
||||||
- [x] Layout support for all trait environments(i.e. Landscape orientation)
|
- [x] Layout support for all trait environments(i.e. Landscape orientation)
|
||||||
- [x] Common UI elements: surface, backdrop and grabber handle
|
- [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
|
- [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/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.
|
- [Examples/Stocks](https://github.com/SCENEE/FloatingPanel/tree/master/Examples/Stocks) like Apple Stocks.app.
|
||||||
@@ -88,11 +89,7 @@ Examples are here.
|
|||||||
|
|
||||||
## Requirements
|
## 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+.
|
|
||||||
|
|
||||||
:pencil2: You would like to use Swift 4.0. Please use FloatingPanel v1.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -105,16 +102,6 @@ it, simply add the following line to your Podfile:
|
|||||||
pod 'FloatingPanel'
|
pod 'FloatingPanel'
|
||||||
```
|
```
|
||||||
|
|
||||||
:pencil2: FloatingPanel v1.7.0 or later requires CocoaPods v1.7.0+ for `swift_versions` support.
|
|
||||||
|
|
||||||
### Carthage
|
|
||||||
|
|
||||||
For [Carthage](https://github.com/Carthage/Carthage), add the following to your `Cartfile`:
|
|
||||||
|
|
||||||
```ogdl
|
|
||||||
github "scenee/FloatingPanel"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Swift Package Manager
|
### Swift Package Manager
|
||||||
|
|
||||||
Follow [this doc](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app).
|
Follow [this doc](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app).
|
||||||
@@ -165,7 +152,8 @@ self.present(fpc, animated: true, completion: nil)
|
|||||||
|
|
||||||
You can show a floating panel over UINavigationController from the container view controllers as a modality of `.overCurrentContext` style.
|
You can show a floating panel over UINavigationController from the container view controllers as a modality of `.overCurrentContext` style.
|
||||||
|
|
||||||
:pencil2: FloatingPanelController has the custom presentation controller. If you would like to customize the presentation/dismissal, please see [Transitioning](https://github.com/SCENEE/FloatingPanel/blob/master/Sources/Transitioning.swift).
|
> [!NOTE]
|
||||||
|
> FloatingPanelController has the custom presentation controller. If you would like to customize the presentation/dismissal, please see [Transitioning](https://github.com/SCENEE/FloatingPanel/blob/master/Sources/Transitioning.swift).
|
||||||
|
|
||||||
## View hierarchy
|
## View hierarchy
|
||||||
|
|
||||||
@@ -249,7 +237,8 @@ fpc.contentMode = .fitToBounds
|
|||||||
|
|
||||||
Otherwise, `FloatingPanelController` fixes the content by the height of the top most position.
|
Otherwise, `FloatingPanelController` fixes the content by the height of the top most position.
|
||||||
|
|
||||||
:pencil2: In `.fitToBounds` mode, the surface height changes as following a user interaction so that you have a responsibility to configure Auto Layout constrains not to break the layout of a content view by the elastic surface height.
|
> [!NOTE]
|
||||||
|
> In `.fitToBounds` mode, the surface height changes as following a user interaction so that you have a responsibility to configure Auto Layout constrains not to break the layout of a content view by the elastic surface height.
|
||||||
|
|
||||||
### Customize the layout with `FloatingPanelLayout` protocol
|
### Customize the layout with `FloatingPanelLayout` protocol
|
||||||
|
|
||||||
@@ -266,13 +255,11 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
|
|||||||
class MyFloatingPanelLayout: FloatingPanelLayout {
|
class MyFloatingPanelLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
let initialState: FloatingPanelState = .tip
|
let initialState: FloatingPanelState = .tip
|
||||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -287,6 +274,8 @@ fpc.layout = MyPanelLayout()
|
|||||||
fpc.invalidateLayout() // If needed
|
fpc.invalidateLayout() // If needed
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: If you already set the `delegate` property of your `FloatingPanelController` instance, `invalidateLayout()` overrides the layout object of `FloatingPanelController` with one returned by the delegate object.
|
||||||
|
|
||||||
2. Returns an appropriate layout object in one of 2 `floatingPanel(_:layoutFor:)` delegates.
|
2. Returns an appropriate layout object in one of 2 `floatingPanel(_:layoutFor:)` delegates.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
@@ -316,12 +305,11 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
|
|||||||
class LandscapePanelLayout: FloatingPanelLayout {
|
class LandscapePanelLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
let initialState: FloatingPanelState = .tip
|
let initialState: FloatingPanelState = .tip
|
||||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
||||||
return [
|
return [
|
||||||
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 8.0),
|
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 8.0),
|
||||||
@@ -340,17 +328,16 @@ class LandscapePanelLayout: FloatingPanelLayout {
|
|||||||
class IntrinsicPanelLayout: FloatingPanelLayout {
|
class IntrinsicPanelLayout: FloatingPanelLayout {
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0, referenceGuide: .safeArea),
|
.half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
|
||||||
.half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
:pencil2: `FloatingPanelIntrinsicLayout` is deprecated on v1.
|
> [!WARNING]
|
||||||
|
> `FloatingPanelIntrinsicLayout` is deprecated on v1.
|
||||||
|
|
||||||
#### Specify an anchor for each state by an inset of the `FloatingPanelController.view` frame
|
#### Specify an anchor for each state by an inset of the `FloatingPanelController.view` frame
|
||||||
|
|
||||||
@@ -359,18 +346,16 @@ Use `.superview` reference guide in your anchors.
|
|||||||
```swift
|
```swift
|
||||||
class MyFullScreenLayout: FloatingPanelLayout {
|
class MyFullScreenLayout: FloatingPanelLayout {
|
||||||
...
|
...
|
||||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .superview),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .superview),
|
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
|
||||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .superview),
|
||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .superview),
|
]
|
||||||
]
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
:pencil2: `FloatingPanelFullScreenLayout` is deprecated on v1.
|
> [!WARNING]
|
||||||
|
> `FloatingPanelFullScreenLayout` is deprecated on v1.
|
||||||
|
|
||||||
#### Change the backdrop alpha
|
#### Change the backdrop alpha
|
||||||
|
|
||||||
@@ -428,13 +413,14 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
|
|||||||
class CustomPanelBehavior: FloatingPanelBehavior {
|
class CustomPanelBehavior: FloatingPanelBehavior {
|
||||||
let springDecelerationRate = UIScrollView.DecelerationRate.fast.rawValue + 0.02
|
let springDecelerationRate = UIScrollView.DecelerationRate.fast.rawValue + 0.02
|
||||||
let springResponseTime = 0.4
|
let springResponseTime = 0.4
|
||||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedTargetPosition: FloatingPanelState) -> Bool {
|
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedState: FloatingPanelState) -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
:pencil2: `floatingPanel(_ vc:behaviorFor:)` is deprecated on v1.
|
> [!WARNING]
|
||||||
|
> `floatingPanel(_ vc:behaviorFor:)` is deprecated on v1.
|
||||||
|
|
||||||
#### Activate the rubber-band effect on panel edges
|
#### Activate the rubber-band effect on panel edges
|
||||||
|
|
||||||
@@ -454,7 +440,7 @@ This allows full projectional panel behavior. For example, a user can swipe up a
|
|||||||
```swift
|
```swift
|
||||||
class MyPanelBehavior: FloatingPanelBehavior {
|
class MyPanelBehavior: FloatingPanelBehavior {
|
||||||
...
|
...
|
||||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedTargetPosition: FloatingPanelPosition) -> Bool {
|
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedState: FloatingPanelPosition) -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -476,7 +462,8 @@ func floatingPanelDidMove(_ vc: FloatingPanelController) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
:pencil2: `{top,bottom}InteractionBuffer` property is removed from `FloatingPanelLayout` since v2.
|
> [!WARNING]
|
||||||
|
> `{top,bottom}InteractionBuffer` property is removed from `FloatingPanelLayout` since v2.
|
||||||
|
|
||||||
### Customize the surface design
|
### Customize the surface design
|
||||||
|
|
||||||
@@ -517,7 +504,8 @@ fpc.surfaceView.grabberHandlePadding = 10.0
|
|||||||
fpc.surfaceView.grabberHandleSize = .init(width: 44.0, height: 12.0)
|
fpc.surfaceView.grabberHandleSize = .init(width: 44.0, height: 12.0)
|
||||||
```
|
```
|
||||||
|
|
||||||
:pencil2: Note that `grabberHandleSize` width and height are reversed in the left/right position.
|
> [!NOTE]
|
||||||
|
> `grabberHandleSize` width and height are reversed in the left/right position.
|
||||||
|
|
||||||
#### Customize content padding from surface edges
|
#### Customize content padding from surface edges
|
||||||
|
|
||||||
@@ -564,8 +552,8 @@ override func viewDidLoad() {
|
|||||||
surfaceTapGesture.isEnabled = (fpc.position == .tip)
|
surfaceTapGesture.isEnabled = (fpc.position == .tip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable `surfaceTapGesture` only at `tip` position
|
// Enable `surfaceTapGesture` only at `tip` state
|
||||||
func floatingPanelDidChangePosition(_ vc: FloatingPanelController) {
|
func floatingPanelDidChangeState(_ vc: FloatingPanelController) {
|
||||||
surfaceTapGesture.isEnabled = (vc.position == .tip)
|
surfaceTapGesture.isEnabled = (vc.position == .tip)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -658,6 +646,32 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Enabling the tap-to-dismiss action of the backdrop view
|
||||||
|
|
||||||
|
The tap-to-dismiss action is disabled by default. So it needs to be enabled as below.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
fpc.backdropView.dismissalTapGestureRecognizer.isEnabled = true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Allow to scroll content of the tracking scroll view in addition to the most expanded state
|
||||||
|
|
||||||
|
Just define conditions to allow content scrolling in `floatingPanel(:_:shouldAllowToScroll:in)` delegate method. If the returned value is true, the scroll content scrolls when its scroll position is not at the top of the content.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
class MyViewController: FloatingPanelControllerDelegate {
|
||||||
|
...
|
||||||
|
|
||||||
|
func floatingPanel(
|
||||||
|
_ fpc: FloatingPanelController,
|
||||||
|
shouldAllowToScroll trackingScrollView: UIScrollView,
|
||||||
|
in state: FloatingPanelState
|
||||||
|
) -> Bool {
|
||||||
|
return state == .full || state == .half
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
### 'Show' or 'Show Detail' Segues from `FloatingPanelController`'s content view controller
|
### 'Show' or 'Show Detail' Segues from `FloatingPanelController`'s content view controller
|
||||||
@@ -698,21 +712,6 @@ It's a great way to decouple between a floating panel and the content VC.
|
|||||||
|
|
||||||
Because `UISearchController` automatically presents itself modally when a user interacts with the search bar, and then it swaps the superview of the search bar to the view managed by itself while it displays. As a result, `FloatingPanelController` can't control the search bar when it's active, as you can see from [the screen shot](https://github.com/SCENEE/FloatingPanel/issues/248#issuecomment-521263831).
|
Because `UISearchController` automatically presents itself modally when a user interacts with the search bar, and then it swaps the superview of the search bar to the view managed by itself while it displays. As a result, `FloatingPanelController` can't control the search bar when it's active, as you can see from [the screen shot](https://github.com/SCENEE/FloatingPanel/issues/248#issuecomment-521263831).
|
||||||
|
|
||||||
### FloatingPanelSurfaceView's issue on iOS 10
|
|
||||||
|
|
||||||
* On iOS 10, `FloatingPanelSurfaceView.cornerRadius` isn't not automatically masked with the top rounded corners because of `UIVisualEffectView` issue. See https://forums.developer.apple.com/thread/50854.
|
|
||||||
So you need to draw top rounding corners of your content. Here is an example in Examples/Maps.
|
|
||||||
```swift
|
|
||||||
override func viewDidLayoutSubviews() {
|
|
||||||
super.viewDidLayoutSubviews()
|
|
||||||
if #available(iOS 10, *) {
|
|
||||||
visualEffectView.layer.cornerRadius = 9.0
|
|
||||||
visualEffectView.clipsToBounds = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* If you sets clear color to `FloatingPanelSurfaceView.backgroundColor`, please note the bottom overflow of your content on bouncing at full position. To prevent it, you need to expand your content. For example, See Example/Maps App's Auto Layout settings of `UIVisualEffectView` in Main.storyboard.
|
|
||||||
|
|
||||||
## Maintainer
|
## Maintainer
|
||||||
|
|
||||||
Shin Yamamoto <shin@scenee.com> | [@scenee](https://twitter.com/scenee)
|
Shin Yamamoto <shin@scenee.com> | [@scenee](https://twitter.com/scenee)
|
||||||
|
|||||||
@@ -4,8 +4,22 @@ import UIKit
|
|||||||
|
|
||||||
/// A view that presents a backdrop interface behind a panel.
|
/// A view that presents a backdrop interface behind a panel.
|
||||||
@objc(FloatingPanelBackdropView)
|
@objc(FloatingPanelBackdropView)
|
||||||
public class BackdropView: UIView {
|
open class BackdropView: UIView {
|
||||||
|
|
||||||
/// The gesture recognizer for tap gestures to dismiss a panel.
|
/// The gesture recognizer for tap gestures to dismiss a panel.
|
||||||
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
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
dismissalTapGestureRecognizer = UITapGestureRecognizer()
|
||||||
|
dismissalTapGestureRecognizer.isEnabled = false
|
||||||
|
super.init(frame: .zero)
|
||||||
|
addGestureRecognizer(dismissalTapGestureRecognizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,12 +25,16 @@ public protocol FloatingPanelBehavior {
|
|||||||
@objc optional
|
@objc optional
|
||||||
var momentumProjectionRate: CGFloat { get }
|
var momentumProjectionRate: CGFloat { get }
|
||||||
|
|
||||||
/// Asks the behavior if a panel should project a momentum of a user interaction to move the proposed position.
|
/// Asks the behavior if a panel should project a momentum of a user interaction to move the
|
||||||
|
/// proposed state.
|
||||||
///
|
///
|
||||||
/// The default implementation of this method returns true. This method is called for a layout to support all positions(tip, half and full).
|
/// The default implementation of this method returns `false`. This method is called for called
|
||||||
/// Therefore, `proposedTargetPosition` can only be `FloatingPanelState.tip` or `FloatingPanelState.full`.
|
/// for all states defined by the current layout object.
|
||||||
@objc optional
|
@objc optional
|
||||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedTargetPosition: FloatingPanelState) -> Bool
|
func shouldProjectMomentum(
|
||||||
|
_ fpc: FloatingPanelController,
|
||||||
|
to proposedState: FloatingPanelState
|
||||||
|
) -> Bool
|
||||||
|
|
||||||
/// Returns the progress to redirect to the previous position.
|
/// Returns the progress to redirect to the previous position.
|
||||||
///
|
///
|
||||||
@@ -46,7 +50,7 @@ public protocol FloatingPanelBehavior {
|
|||||||
|
|
||||||
/// Returns the velocity threshold for the default interactive removal gesture.
|
/// Returns the velocity threshold for the default interactive removal gesture.
|
||||||
///
|
///
|
||||||
/// In case `floatingPanel:shouldRemoveAt:with` is implemented, this value will not be used. The default value of `FloatingPanelDefaultBehavior` is 5.5
|
/// In case ``FloatingPanel/FloatingPanelControllerDelegate/floatingPanel(_:shouldRemoveAt:with:)`` is implemented, this value will not be used. The default value of ``FloatingPanelDefaultBehavior`` is 5.5
|
||||||
@objc optional
|
@objc optional
|
||||||
var removalInteractionVelocityThreshold: CGFloat { get }
|
var removalInteractionVelocityThreshold: CGFloat { get }
|
||||||
}
|
}
|
||||||
|
|||||||
+150
-93
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import os.log
|
||||||
|
|
||||||
/// A set of methods implemented by the delegate of a panel controller allows the adopting delegate to respond to
|
/// A set of methods implemented by the delegate of a panel controller allows the adopting delegate to respond to
|
||||||
/// messages from the FloatingPanelController class and thus respond to, and in some affect, operations such as
|
/// messages from the FloatingPanelController class and thus respond to, and in some affect, operations such as
|
||||||
@@ -26,18 +27,20 @@ import UIKit
|
|||||||
@objc(floatingPanel:animatorForDismissingWithVelocity:) optional
|
@objc(floatingPanel:animatorForDismissingWithVelocity:) optional
|
||||||
func floatingPanel(_ fpc: FloatingPanelController, animatorForDismissingWith velocity: CGVector) -> UIViewPropertyAnimator
|
func floatingPanel(_ fpc: FloatingPanelController, animatorForDismissingWith velocity: CGVector) -> UIViewPropertyAnimator
|
||||||
|
|
||||||
/// Called when a panel has changed to a new position. Can be called inside an animation block, so any
|
/// Called when a panel has changed to a new state.
|
||||||
/// view properties set inside this function will be automatically animated alongside a panel.
|
///
|
||||||
|
/// This can be called inside an animation block for presenting, dismissing a panel or moving a panel with your
|
||||||
|
/// animation. So any view properties set inside this function will be automatically animated alongside a panel.
|
||||||
@objc optional
|
@objc optional
|
||||||
func floatingPanelDidChangePosition(_ fpc: FloatingPanelController)
|
func floatingPanelDidChangeState(_ fpc: FloatingPanelController)
|
||||||
|
|
||||||
/// Asks the delegate if dragging should begin by the pan gesture recognizer.
|
/// Asks the delegate if dragging should begin by the pan gesture recognizer.
|
||||||
@objc optional
|
@objc optional
|
||||||
func floatingPanelShouldBeginDragging(_ fpc: FloatingPanelController) -> Bool
|
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
|
@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)
|
/// Called on start of dragging (may require some time and or distance to move)
|
||||||
@objc optional
|
@objc optional
|
||||||
@@ -49,7 +52,12 @@ import UIKit
|
|||||||
|
|
||||||
/// Called on finger up if the user dragged.
|
/// Called on finger up if the user dragged.
|
||||||
///
|
///
|
||||||
/// If `attract` is true, it will continue moving afterwards to a nearby state anchor.
|
/// If `attract` is true, the panel continues moving towards the nearby state
|
||||||
|
/// anchor. Otherwise, it stops at the closest state anchor.
|
||||||
|
///
|
||||||
|
/// - Note: If `attract` is false, ``FloatingPanelController.state`` property has
|
||||||
|
/// already changed to the closest anchor's state by the time this delegate method
|
||||||
|
/// is called.
|
||||||
@objc optional
|
@objc optional
|
||||||
func floatingPanelDidEndDragging(_ fpc: FloatingPanelController, willAttract attract: Bool)
|
func floatingPanelDidEndDragging(_ fpc: FloatingPanelController, willAttract attract: Bool)
|
||||||
|
|
||||||
@@ -63,7 +71,7 @@ import UIKit
|
|||||||
|
|
||||||
/// Asks the delegate whether a panel should be removed when dragging ended at the specified location
|
/// Asks the delegate whether a panel should be removed when dragging ended at the specified location
|
||||||
///
|
///
|
||||||
/// This delegate method is called only where `FloatingPanelController.isRemovalInteractionEnabled` is `true`.
|
/// This delegate method is called only where ``FloatingPanel/FloatingPanelController/isRemovalInteractionEnabled`` is `true`.
|
||||||
/// The velocity vector is calculated from the distance to a point of the hidden state and the pan gesture's velocity.
|
/// The velocity vector is calculated from the distance to a point of the hidden state and the pan gesture's velocity.
|
||||||
@objc(floatingPanel:shouldRemoveAtLocation:withVelocity:)
|
@objc(floatingPanel:shouldRemoveAtLocation:withVelocity:)
|
||||||
optional
|
optional
|
||||||
@@ -88,6 +96,35 @@ import UIKit
|
|||||||
@objc(floatingPanel:contentOffsetForPinningScrollView:)
|
@objc(floatingPanel:contentOffsetForPinningScrollView:)
|
||||||
optional
|
optional
|
||||||
func floatingPanel(_ fpc: FloatingPanelController, contentOffsetForPinning trackingScrollView: UIScrollView) -> CGPoint
|
func floatingPanel(_ fpc: FloatingPanelController, contentOffsetForPinning trackingScrollView: UIScrollView) -> CGPoint
|
||||||
|
|
||||||
|
/// Returns a Boolean value that determines whether the tracking scroll view should
|
||||||
|
/// scroll or not
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// If you return true, the scroll content scrolls when its scroll position is not
|
||||||
|
/// at the top of the content. If the delegate doesn’t implement this method, its
|
||||||
|
/// content can be scrolled only in the most expanded state.
|
||||||
|
///
|
||||||
|
/// Basically, the decision to scroll is based on the `state` property like the
|
||||||
|
/// following code.
|
||||||
|
/// ```swift
|
||||||
|
/// func floatingPanel(
|
||||||
|
/// _ fpc: FloatingPanelController,
|
||||||
|
/// shouldAllowToScroll scrollView: UIScrollView,
|
||||||
|
/// in state: FloatingPanelState
|
||||||
|
/// ) -> Bool {
|
||||||
|
/// return state == .full || state == .half
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// - Attention: It is recommended that this method always returns the most expanded state(i.e.
|
||||||
|
/// .full). If it excludes the state, the panel might do unexpected behaviors.
|
||||||
|
@objc(floatingPanel:shouldAllowToScroll:in:)
|
||||||
|
optional func floatingPanel(
|
||||||
|
_ fpc: FloatingPanelController,
|
||||||
|
shouldAllowToScroll scrollView: UIScrollView,
|
||||||
|
in state: FloatingPanelState
|
||||||
|
) -> Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -121,14 +158,15 @@ open class FloatingPanelController: UIViewController {
|
|||||||
|
|
||||||
/// Returns the surface view managed by the controller object. It's the same as `self.view`.
|
/// Returns the surface view managed by the controller object. It's the same as `self.view`.
|
||||||
@objc
|
@objc
|
||||||
public var surfaceView: SurfaceView! {
|
public var surfaceView: SurfaceView {
|
||||||
return floatingPanel.surfaceView
|
return floatingPanel.surfaceView
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the backdrop view managed by the controller object.
|
/// Returns the backdrop view managed by the controller object.
|
||||||
@objc
|
@objc
|
||||||
public var backdropView: BackdropView! {
|
public var backdropView: BackdropView {
|
||||||
return floatingPanel.backdropView
|
set { floatingPanel.backdropView = newValue }
|
||||||
|
get { return floatingPanel.backdropView }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the scroll view that the controller tracks.
|
/// Returns the scroll view that the controller tracks.
|
||||||
@@ -155,26 +193,31 @@ open class FloatingPanelController: UIViewController {
|
|||||||
return floatingPanel.isAttracting
|
return floatingPanel.isAttracting
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The layout object managed by the controller
|
/// The layout object that the controller manages
|
||||||
|
///
|
||||||
|
/// You need to call ``invalidateLayout()`` if you want to apply a new layout object into the panel
|
||||||
|
/// immediately.
|
||||||
@objc
|
@objc
|
||||||
public var layout: FloatingPanelLayout {
|
public var layout: FloatingPanelLayout {
|
||||||
get { _layout }
|
get { _layout }
|
||||||
set {
|
set {
|
||||||
_layout = newValue
|
_layout = newValue
|
||||||
if let parent = parent, let layout = newValue as? UIViewController, layout == parent {
|
if let parent = parent, let layout = newValue as? UIViewController, layout == parent {
|
||||||
log.warning("A memory leak will occur by a retain cycle because \(self) owns the parent view controller(\(parent)) as the layout object. Don't let the parent adopt FloatingPanelLayout.")
|
let log = "Warning: A memory leak occurs due to a retain cycle, as \(self) owns the parent view controller(\(parent)) as its layout object. Don't allow the parent to adopt FloatingPanelLayout."
|
||||||
|
os_log(msg, log: sysLog, type: .error, log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The behavior object managed by the controller
|
/// The behavior object that the controller manages
|
||||||
@objc
|
@objc
|
||||||
public var behavior: FloatingPanelBehavior {
|
public var behavior: FloatingPanelBehavior {
|
||||||
get { _behavior }
|
get { _behavior }
|
||||||
set {
|
set {
|
||||||
_behavior = newValue
|
_behavior = newValue
|
||||||
if let parent = parent, let behavior = newValue as? UIViewController, behavior == parent {
|
if let parent = parent, let behavior = newValue as? UIViewController, behavior == parent {
|
||||||
log.warning("A memory leak will occur by a retain cycle because \(self) owns the parent view controller(\(parent)) as the behavior object. Don't let the parent adopt FloatingPanelBehavior.")
|
let log = "Warning: A memory leak occurs due to a retain cycle, as \(self) owns the parent view controller(\(parent)) as its behavior object. Don't allow the parent to adopt FloatingPanelBehavior."
|
||||||
|
os_log(msg, log: sysLog, type: .error, log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,7 +230,7 @@ open class FloatingPanelController: UIViewController {
|
|||||||
|
|
||||||
/// The behavior for determining the adjusted content offsets.
|
/// The behavior for determining the adjusted content offsets.
|
||||||
///
|
///
|
||||||
/// This property specifies how the content area of the tracking scroll view is modified using `adjustedContentInsets`. The default value of this property is FloatingPanelController.ContentInsetAdjustmentBehavior.always.
|
/// This property specifies how the content area of the tracking scroll view is modified using ``adjustedContentInsets``. The default value of this property is FloatingPanelController.ContentInsetAdjustmentBehavior.always.
|
||||||
@objc
|
@objc
|
||||||
public var contentInsetAdjustmentBehavior: ContentInsetAdjustmentBehavior = .always
|
public var contentInsetAdjustmentBehavior: ContentInsetAdjustmentBehavior = .always
|
||||||
|
|
||||||
@@ -208,7 +251,7 @@ open class FloatingPanelController: UIViewController {
|
|||||||
/// The NearbyState determines that finger's nearby state.
|
/// The NearbyState determines that finger's nearby state.
|
||||||
public var nearbyState: FloatingPanelState {
|
public var nearbyState: FloatingPanelState {
|
||||||
let currentY = surfaceLocation.y
|
let currentY = surfaceLocation.y
|
||||||
return floatingPanel.targetPosition(from: currentY, with: .zero)
|
return floatingPanel.targetState(from: currentY, with: .zero)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constants that define how a panel content fills in the surface.
|
/// Constants that define how a panel content fills in the surface.
|
||||||
@@ -288,19 +331,19 @@ open class FloatingPanelController: UIViewController {
|
|||||||
|
|
||||||
open override func viewDidLayoutSubviews() {
|
open override func viewDidLayoutSubviews() {
|
||||||
super.viewDidLayoutSubviews()
|
super.viewDidLayoutSubviews()
|
||||||
if #available(iOS 11.0, *) {
|
// Ensure to update the static constraint of a panel after rotating a device in static mode
|
||||||
// Ensure to update the static constraint of a panel after rotating a device in static mode
|
if contentMode == .static {
|
||||||
if contentMode == .static {
|
floatingPanel.layoutAdapter.updateStaticConstraint()
|
||||||
floatingPanel.layoutAdapter.updateStaticConstraint()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Because {top,bottom}LayoutGuide is managed as a view
|
|
||||||
if floatingPanel.isAttracting == false {
|
|
||||||
self.update(safeAreaInsets: fp_safeAreaInsets)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
// Need to call this method just after the view appears, as the safe area is not
|
||||||
|
// correctly set before this time, for example, `show(animated:completion:)`.
|
||||||
|
floatingPanel.adjustScrollContentInsetIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||||
super.viewWillTransition(to: size, with: coordinator)
|
super.viewWillTransition(to: size, with: coordinator)
|
||||||
|
|
||||||
@@ -311,7 +354,7 @@ open class FloatingPanelController: UIViewController {
|
|||||||
// Change a layout for the new view size
|
// Change a layout for the new view size
|
||||||
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: size) {
|
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: size) {
|
||||||
layout = newLayout
|
layout = newLayout
|
||||||
activateLayout(forceLayout: false)
|
activateLayout(forceLayout: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if view.translatesAutoresizingMaskIntoConstraints {
|
if view.translatesAutoresizingMaskIntoConstraints {
|
||||||
@@ -330,7 +373,7 @@ open class FloatingPanelController: UIViewController {
|
|||||||
// Change a layout for the new trait collection
|
// Change a layout for the new trait collection
|
||||||
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: newCollection) {
|
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: newCollection) {
|
||||||
self.layout = newLayout
|
self.layout = newLayout
|
||||||
activateLayout(forceLayout: false)
|
activateLayout(forceLayout: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,7 +413,7 @@ open class FloatingPanelController: UIViewController {
|
|||||||
preSafeAreaInsets != safeAreaInsets
|
preSafeAreaInsets != safeAreaInsets
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
log.debug("Update safeAreaInsets", safeAreaInsets)
|
os_log(msg, log: devLog, type: .debug, "Update safeAreaInsets = \(safeAreaInsets)")
|
||||||
|
|
||||||
// Prevent an infinite loop on iOS 10: setUpLayout() -> viewDidLayoutSubviews() -> setUpLayout()
|
// Prevent an infinite loop on iOS 10: setUpLayout() -> viewDidLayoutSubviews() -> setUpLayout()
|
||||||
preSafeAreaInsets = safeAreaInsets
|
preSafeAreaInsets = safeAreaInsets
|
||||||
@@ -382,6 +425,7 @@ open class FloatingPanelController: UIViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
floatingPanel.layoutAdapter.updateStaticConstraint()
|
floatingPanel.layoutAdapter.updateStaticConstraint()
|
||||||
|
floatingPanel.adjustScrollContentInsetIfNeeded()
|
||||||
|
|
||||||
if let contentOffset = contentOffset {
|
if let contentOffset = contentOffset {
|
||||||
trackingScrollView?.contentOffset = contentOffset
|
trackingScrollView?.contentOffset = contentOffset
|
||||||
@@ -396,20 +440,10 @@ open class FloatingPanelController: UIViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func activateLayout(forceLayout: Bool = false) {
|
private func activateLayout(forceLayout: Bool = false) {
|
||||||
floatingPanel.layoutAdapter.prepareLayout()
|
floatingPanel.activateLayout(
|
||||||
|
forceLayout: forceLayout,
|
||||||
// preserve the current content offset if contentInsetAdjustmentBehavior is `.always`
|
contentInsetAdjustmentBehavior: contentInsetAdjustmentBehavior
|
||||||
var contentOffset: CGPoint?
|
)
|
||||||
if contentInsetAdjustmentBehavior == .always {
|
|
||||||
contentOffset = trackingScrollView?.contentOffset
|
|
||||||
}
|
|
||||||
|
|
||||||
floatingPanel.layoutAdapter.updateStaticConstraint()
|
|
||||||
floatingPanel.layoutAdapter.activateLayout(for: floatingPanel.state, forceLayout: forceLayout)
|
|
||||||
|
|
||||||
if let contentOffset = contentOffset {
|
|
||||||
trackingScrollView?.contentOffset = contentOffset
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func remove() {
|
func remove() {
|
||||||
@@ -419,35 +453,46 @@ open class FloatingPanelController: UIViewController {
|
|||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.delegate?.floatingPanelDidRemove?(self)
|
self.delegate?.floatingPanelDidRemove?(self)
|
||||||
}
|
}
|
||||||
} else {
|
} else if parent != nil {
|
||||||
removePanelFromParent(animated: true)
|
removePanelFromParent(animated: true)
|
||||||
|
} else {
|
||||||
|
delegate?.floatingPanelWillRemove?(self)
|
||||||
|
hide(animated: true) { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.view.removeFromSuperview()
|
||||||
|
self.delegate?.floatingPanelDidRemove?(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Container view controller interface
|
// MARK: - Container view controller interface
|
||||||
|
|
||||||
/// Shows the surface view at the initial position defined by the current layout
|
/// Shows the surface view at the initial position defined by the current layout
|
||||||
|
/// - Parameters:
|
||||||
|
/// - animated: Pass true to animate the presentation; otherwise, pass false.
|
||||||
|
/// - completion: The block to execute after the presentation finishes. This block has no return value and takes no parameters. You may specify nil for this parameter.
|
||||||
@objc(show:completion:)
|
@objc(show:completion:)
|
||||||
public func show(animated: Bool = false, completion: (() -> Void)? = nil) {
|
public func show(animated: Bool = false, completion: (() -> Void)? = nil) {
|
||||||
// Must apply the current layout here
|
// Must apply the current layout here
|
||||||
activateLayout(forceLayout: true)
|
activateLayout(forceLayout: true)
|
||||||
|
|
||||||
if #available(iOS 11.0, *) {
|
// Must track the safeAreaInsets of `self.view` to update the layout.
|
||||||
// Must track the safeAreaInsets of `self.view` to update the layout.
|
// There are 2 reasons.
|
||||||
// There are 2 reasons.
|
// 1. This or the parent VC doesn't call viewSafeAreaInsetsDidChange() on the bottom
|
||||||
// 1. This or the parent VC doesn't call viewSafeAreaInsetsDidChange() on the bottom
|
// inset's update expectedly.
|
||||||
// inset's update expectedly.
|
// 2. The safe area top inset can be variable on the large title navigation bar(iOS11+).
|
||||||
// 2. The safe area top inset can be variable on the large title navigation bar(iOS11+).
|
// That's why it needs the observation to keep `adjustedContentInsets` correct.
|
||||||
// That's why it needs the observation to keep `adjustedContentInsets` correct.
|
safeAreaInsetsObservation = self.view.observe(\.safeAreaInsets, options: [.initial, .new, .old]) { [weak self] (_, change) in
|
||||||
safeAreaInsetsObservation = self.view.observe(\.safeAreaInsets, options: [.initial, .new, .old]) { [weak self] (_, change) in
|
// Use `self.view.safeAreaInsets` because `change.newValue` can be nil in particular case when
|
||||||
// Use `self.view.safeAreaInsets` because `change.newValue` can be nil in particular case when
|
// is reported in https://github.com/SCENEE/FloatingPanel/issues/330
|
||||||
// is reported in https://github.com/SCENEE/FloatingPanel/issues/330
|
guard let self = self, change.oldValue != self.view.safeAreaInsets else { return }
|
||||||
guard let self = self, change.oldValue != self.view.safeAreaInsets else { return }
|
|
||||||
self.update(safeAreaInsets: self.view.safeAreaInsets)
|
// 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
|
||||||
} else {
|
// an unsatisfied constraints error. So this method should not be called with those bounds.
|
||||||
// KVOs for topLayoutGuide & bottomLayoutGuide are not effective.
|
guard self.view.bounds.height > 0 && self.view.bounds.width > 0 else { return }
|
||||||
// Instead, update(safeAreaInsets:) is called at `viewDidLayoutSubviews()`
|
|
||||||
|
self.update(safeAreaInsets: self.view.safeAreaInsets)
|
||||||
}
|
}
|
||||||
|
|
||||||
move(to: floatingPanel.layoutAdapter.initialState,
|
move(to: floatingPanel.layoutAdapter.initialState,
|
||||||
@@ -468,10 +513,11 @@ open class FloatingPanelController: UIViewController {
|
|||||||
/// - parent: A parent view controller object that displays FloatingPanelController's view. A container view controller object isn't applicable.
|
/// - parent: A parent view controller object that displays FloatingPanelController's view. A container view controller object isn't applicable.
|
||||||
/// - viewIndex: Insert the surface view managed by the controller below the specified view index. By default, the surface view will be added to the end of the parent list of subviews.
|
/// - viewIndex: Insert the surface view managed by the controller below the specified view index. By default, the surface view will be added to the end of the parent list of subviews.
|
||||||
/// - animated: Pass true to animate the presentation; otherwise, pass false.
|
/// - animated: Pass true to animate the presentation; otherwise, pass false.
|
||||||
@objc(addPanelToParent:at:animated:)
|
/// - completion: The block to execute after the presentation finishes. This block has no return value and takes no parameters. You may specify nil for this parameter.
|
||||||
public func addPanel(toParent parent: UIViewController, at viewIndex: Int = -1, animated: Bool = false) {
|
@objc(addPanelToParent:at:animated:completion:)
|
||||||
|
public func addPanel(toParent parent: UIViewController, at viewIndex: Int = -1, animated: Bool = false, completion: (() -> Void)? = nil) {
|
||||||
guard self.parent == nil else {
|
guard self.parent == nil else {
|
||||||
log.warning("Already added to a parent(\(parent))")
|
os_log(msg, log: sysLog, type: .error, "Warning: already added to a parent(\(parent))")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert((parent is UINavigationController) == false, "UINavigationController displays only one child view controller at a time.")
|
assert((parent is UINavigationController) == false, "UINavigationController displays only one child view controller at a time.")
|
||||||
@@ -500,6 +546,7 @@ open class FloatingPanelController: UIViewController {
|
|||||||
show(animated: animated) { [weak self] in
|
show(animated: animated) { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.didMove(toParent: parent)
|
self.didMove(toParent: parent)
|
||||||
|
completion?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,6 +578,7 @@ open class FloatingPanelController: UIViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Moves the position to the specified position.
|
/// Moves the position to the specified position.
|
||||||
|
///
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - to: Pass a FloatingPanelPosition value to move the surface view to the position.
|
/// - to: Pass a FloatingPanelPosition value to move the surface view to the position.
|
||||||
/// - animated: Pass true to animate the presentation; otherwise, pass false.
|
/// - animated: Pass true to animate the presentation; otherwise, pass false.
|
||||||
@@ -552,7 +600,7 @@ open class FloatingPanelController: UIViewController {
|
|||||||
addChild(vc)
|
addChild(vc)
|
||||||
|
|
||||||
let surfaceView = floatingPanel.surfaceView
|
let surfaceView = floatingPanel.surfaceView
|
||||||
surfaceView.set(contentView: vc.view)
|
surfaceView.set(contentView: vc.view, mode: contentMode)
|
||||||
|
|
||||||
vc.didMove(toParent: self)
|
vc.didMove(toParent: self)
|
||||||
}
|
}
|
||||||
@@ -576,18 +624,22 @@ open class FloatingPanelController: UIViewController {
|
|||||||
|
|
||||||
switch contentInsetAdjustmentBehavior {
|
switch contentInsetAdjustmentBehavior {
|
||||||
case .always:
|
case .always:
|
||||||
if #available(iOS 11.0, *) {
|
scrollView.contentInsetAdjustmentBehavior = .never
|
||||||
scrollView.contentInsetAdjustmentBehavior = .never
|
|
||||||
} else {
|
|
||||||
children.forEach { (vc) in
|
|
||||||
vc.automaticallyAdjustsScrollViewInsets = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break
|
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.
|
/// Cancel tracking the specify scroll view.
|
||||||
///
|
///
|
||||||
@objc(untrackScrollView:)
|
@objc(untrackScrollView:)
|
||||||
@@ -607,15 +659,19 @@ open class FloatingPanelController: UIViewController {
|
|||||||
|
|
||||||
// MARK: - Utilities
|
// MARK: - Utilities
|
||||||
|
|
||||||
/// Updates the layout object from the delegate and lays out the views managed
|
/// Invalidates all layout information of the panel and apply the ``layout`` property into it immediately.
|
||||||
/// by the controller immediately.
|
|
||||||
///
|
///
|
||||||
/// This method updates the `FloatingPanelLayout` object from the delegate and
|
/// This lays out subviews of the view that the controller manages with the ``layout`` property by
|
||||||
/// then it calls `layoutIfNeeded()` of the root view to force the view
|
/// calling the view's `layoutIfNeeded()`. Thus this method can be called in an animation block to
|
||||||
/// to update the layout immediately. It can be called in an
|
/// animate the panel's changes.
|
||||||
/// animation block.
|
///
|
||||||
|
/// If the controller has a delegate object, this will lay them out using the layout object returned by
|
||||||
|
/// `floatingPanel(_:layoutFor:)` delegate method for the current `UITraitCollection`.
|
||||||
@objc
|
@objc
|
||||||
public func invalidateLayout() {
|
public func invalidateLayout() {
|
||||||
|
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: traitCollection) {
|
||||||
|
layout = newLayout
|
||||||
|
}
|
||||||
activateLayout(forceLayout: true)
|
activateLayout(forceLayout: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,30 +727,31 @@ extension FloatingPanelController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Swizzling
|
||||||
|
|
||||||
|
private var originalDismissImp: IMP?
|
||||||
|
private typealias DismissFunction = @convention(c) (AnyObject, Selector, Bool, (() -> Void)?) -> Void
|
||||||
extension FloatingPanelController {
|
extension FloatingPanelController {
|
||||||
private static let dismissSwizzling: Void = {
|
private static let dismissSwizzling: Void = {
|
||||||
|
guard originalDismissImp == nil else { return }
|
||||||
let aClass: AnyClass! = UIViewController.self //object_getClass(vc)
|
let aClass: AnyClass! = UIViewController.self //object_getClass(vc)
|
||||||
if let imp = class_getMethodImplementation(aClass, #selector(dismiss(animated:completion:))),
|
if let originalMethod = class_getInstanceMethod(aClass, #selector(dismiss(animated:completion:))),
|
||||||
let originalAltMethod = class_getInstanceMethod(aClass, #selector(fp_original_dismiss(animated:completion:))) {
|
let swizzledImp = class_getMethodImplementation(aClass, #selector(__swizzled_dismiss(animated:completion:))) {
|
||||||
method_setImplementation(originalAltMethod, imp)
|
originalDismissImp = method_setImplementation(originalMethod, swizzledImp)
|
||||||
}
|
|
||||||
let originalMethod = class_getInstanceMethod(aClass, #selector(dismiss(animated:completion:)))
|
|
||||||
let swizzledMethod = class_getInstanceMethod(aClass, #selector(fp_dismiss(animated:completion:)))
|
|
||||||
if let originalMethod = originalMethod, let swizzledMethod = swizzledMethod {
|
|
||||||
method_exchangeImplementations(originalMethod, swizzledMethod)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension UIViewController {
|
extension UIViewController {
|
||||||
@objc func fp_original_dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
@objc
|
||||||
// Implementation will be replaced by IMP of self.dismiss(animated:completion:)
|
fileprivate func __swizzled_dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||||
}
|
let dismissImp = unsafeBitCast(originalDismissImp, to: DismissFunction.self)
|
||||||
@objc func fp_dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
let sel = #selector(UIViewController.dismiss(animated:completion:))
|
||||||
|
|
||||||
// Call dismiss(animated:completion:) to a content view controller
|
// Call dismiss(animated:completion:) to a content view controller
|
||||||
if let fpc = parent as? FloatingPanelController {
|
if let fpc = parent as? FloatingPanelController {
|
||||||
if fpc.presentingViewController != nil {
|
if fpc.presentingViewController != nil {
|
||||||
self.fp_original_dismiss(animated: flag, completion: completion)
|
dismissImp(self, sel, flag, completion)
|
||||||
} else {
|
} else {
|
||||||
fpc.removePanelFromParent(animated: flag, completion: completion)
|
fpc.removePanelFromParent(animated: flag, completion: completion)
|
||||||
}
|
}
|
||||||
@@ -704,7 +761,7 @@ public extension UIViewController {
|
|||||||
if let fpc = self as? FloatingPanelController {
|
if let fpc = self as? FloatingPanelController {
|
||||||
// When a panel is presented modally and it's not a child view controller of the presented view controller.
|
// When a panel is presented modally and it's not a child view controller of the presented view controller.
|
||||||
if fpc.presentingViewController != nil, fpc.parent == nil {
|
if fpc.presentingViewController != nil, fpc.parent == nil {
|
||||||
self.fp_original_dismiss(animated: flag, completion: completion)
|
dismissImp(self, sel, flag, completion)
|
||||||
} else {
|
} else {
|
||||||
fpc.removePanelFromParent(animated: flag, completion: completion)
|
fpc.removePanelFromParent(animated: flag, completion: completion)
|
||||||
}
|
}
|
||||||
@@ -712,6 +769,6 @@ public extension UIViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For other view controllers
|
// For other view controllers
|
||||||
self.fp_original_dismiss(animated: flag, completion: completion)
|
dismissImp(self, sel, flag, completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+479
-253
File diff suppressed because it is too large
Load Diff
+11
-68
@@ -7,10 +7,12 @@ import UIKit
|
|||||||
extension CGFloat {
|
extension CGFloat {
|
||||||
/// Returns this value rounded to an logical pixel value by a display scale
|
/// Returns this value rounded to an logical pixel value by a display scale
|
||||||
func rounded(by displayScale: CGFloat) -> CGFloat {
|
func rounded(by displayScale: CGFloat) -> CGFloat {
|
||||||
return (self * displayScale).rounded(.toNearestOrAwayFromZero) / displayScale
|
let p = CGFloat(1.0e9)
|
||||||
|
let v = (self * p).rounded(.towardZero) / p
|
||||||
|
return (v * displayScale).rounded(.toNearestOrAwayFromZero) / displayScale
|
||||||
}
|
}
|
||||||
func isEqual(to: CGFloat, on displayScale: CGFloat) -> Bool {
|
func isEqual(to: CGFloat, on displayScale: CGFloat) -> Bool {
|
||||||
return self.rounded(by: displayScale) == to.rounded(by: displayScale)
|
return rounded(by: displayScale) == to.rounded(by: displayScale)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,65 +47,16 @@ protocol LayoutGuideProvider {
|
|||||||
extension UILayoutGuide: LayoutGuideProvider {}
|
extension UILayoutGuide: LayoutGuideProvider {}
|
||||||
extension UIView: LayoutGuideProvider {}
|
extension UIView: LayoutGuideProvider {}
|
||||||
|
|
||||||
private class CustomLayoutGuide: LayoutGuideProvider {
|
|
||||||
let topAnchor: NSLayoutYAxisAnchor
|
|
||||||
let leftAnchor: NSLayoutXAxisAnchor
|
|
||||||
let bottomAnchor: NSLayoutYAxisAnchor
|
|
||||||
let rightAnchor: NSLayoutXAxisAnchor
|
|
||||||
let widthAnchor: NSLayoutDimension
|
|
||||||
let heightAnchor: NSLayoutDimension
|
|
||||||
init(topAnchor: NSLayoutYAxisAnchor,
|
|
||||||
leftAnchor: NSLayoutXAxisAnchor,
|
|
||||||
bottomAnchor: NSLayoutYAxisAnchor,
|
|
||||||
rightAnchor: NSLayoutXAxisAnchor,
|
|
||||||
widthAnchor: NSLayoutDimension,
|
|
||||||
heightAnchor: NSLayoutDimension) {
|
|
||||||
self.topAnchor = topAnchor
|
|
||||||
self.leftAnchor = leftAnchor
|
|
||||||
self.bottomAnchor = bottomAnchor
|
|
||||||
self.rightAnchor = rightAnchor
|
|
||||||
self.widthAnchor = widthAnchor
|
|
||||||
self.heightAnchor = heightAnchor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UIViewController {
|
extension UIViewController {
|
||||||
|
/// The proxy property to be used in `LayoutAdapter`
|
||||||
|
///
|
||||||
|
/// This property is to allow the safe area inset to change in unit testing
|
||||||
@objc var fp_safeAreaInsets: UIEdgeInsets {
|
@objc var fp_safeAreaInsets: UIEdgeInsets {
|
||||||
if #available(iOS 11.0, *) {
|
return view.safeAreaInsets
|
||||||
return view.safeAreaInsets
|
|
||||||
} else {
|
|
||||||
return UIEdgeInsets(top: topLayoutGuide.length,
|
|
||||||
left: 0.0,
|
|
||||||
bottom: bottomLayoutGuide.length,
|
|
||||||
right: 0.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var fp_safeAreaLayoutGuide: LayoutGuideProvider {
|
|
||||||
if #available(iOS 11.0, *) {
|
|
||||||
return view!.safeAreaLayoutGuide
|
|
||||||
} else {
|
|
||||||
return CustomLayoutGuide(topAnchor: topLayoutGuide.bottomAnchor,
|
|
||||||
leftAnchor: view.leftAnchor,
|
|
||||||
bottomAnchor: bottomLayoutGuide.topAnchor,
|
|
||||||
rightAnchor: view.rightAnchor,
|
|
||||||
widthAnchor: view.widthAnchor,
|
|
||||||
heightAnchor: topLayoutGuide.bottomAnchor.anchorWithOffset(to: bottomLayoutGuide.topAnchor))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The reason why UIView has no extensions of safe area insets and top/bottom guides
|
|
||||||
// is for iOS10 compatibility.
|
|
||||||
extension UIView {
|
extension UIView {
|
||||||
var fp_safeAreaLayoutGuide: LayoutGuideProvider {
|
|
||||||
if #available(iOS 11.0, *) {
|
|
||||||
return safeAreaLayoutGuide
|
|
||||||
} else {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var presentationFrame: CGRect {
|
var presentationFrame: CGRect {
|
||||||
return layer.presentation()?.frame ?? frame
|
return layer.presentation()?.frame ?? frame
|
||||||
}
|
}
|
||||||
@@ -139,7 +92,7 @@ extension UIView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __FP_LOG
|
#if FP_LOG
|
||||||
extension UIGestureRecognizer.State: CustomDebugStringConvertible {
|
extension UIGestureRecognizer.State: CustomDebugStringConvertible {
|
||||||
public var debugDescription: String {
|
public var debugDescription: String {
|
||||||
switch self {
|
switch self {
|
||||||
@@ -156,19 +109,9 @@ extension UIGestureRecognizer.State: CustomDebugStringConvertible {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
extension UIScrollView {
|
extension UIScrollView {
|
||||||
var isLocked: Bool {
|
|
||||||
return !showsVerticalScrollIndicator && !bounces && isDirectionalLockEnabled
|
|
||||||
}
|
|
||||||
var fp_contentInset: UIEdgeInsets {
|
|
||||||
if #available(iOS 11.0, *) {
|
|
||||||
return adjustedContentInset
|
|
||||||
} else {
|
|
||||||
return contentInset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var fp_contentOffsetMax: CGPoint {
|
var fp_contentOffsetMax: CGPoint {
|
||||||
return CGPoint(x: max((contentSize.width + fp_contentInset.right) - bounds.width, 0.0),
|
return CGPoint(x: max((contentSize.width + adjustedContentInset.right) - bounds.width, 0.0),
|
||||||
y: max((contentSize.height + fp_contentInset.bottom) - bounds.height, 0.0))
|
y: max((contentSize.height + adjustedContentInset.bottom) - bounds.height, 0.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# ``FloatingPanel``
|
||||||
|
|
||||||
|
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 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
|
||||||
|
|
||||||
|
### Essentials
|
||||||
|
|
||||||
|
- ``FloatingPanelController``
|
||||||
|
- ``FloatingPanelControllerDelegate``
|
||||||
|
|
||||||
|
### Views
|
||||||
|
|
||||||
|
- ``SurfaceView``
|
||||||
|
- ``SurfaceAppearance``
|
||||||
|
- ``BackdropView``
|
||||||
|
- ``GrabberView``
|
||||||
|
|
||||||
|
### Gestures
|
||||||
|
|
||||||
|
- ``FloatingPanelPanGestureRecognizer``
|
||||||
|
|
||||||
|
### Layouts and Anchors
|
||||||
|
|
||||||
|
- ``FloatingPanelLayout``
|
||||||
|
- ``FloatingPanelBottomLayout``
|
||||||
|
- ``FloatingPanelLayoutAnchoring``
|
||||||
|
- ``FloatingPanelLayoutAnchor``
|
||||||
|
- ``FloatingPanelAdaptiveLayoutAnchor``
|
||||||
|
- ``FloatingPanelIntrinsicLayoutAnchor``
|
||||||
|
|
||||||
|
### States
|
||||||
|
|
||||||
|
- ``FloatingPanelState``
|
||||||
|
|
||||||
|
### Positions
|
||||||
|
|
||||||
|
- ``FloatingPanelPosition``
|
||||||
|
- ``FloatingPanelReferenceEdge``
|
||||||
|
- ``FloatingPanelLayoutReferenceGuide``
|
||||||
|
- ``FloatingPanelLayoutContentBoundingGuide``
|
||||||
|
|
||||||
|
### Behaviors
|
||||||
|
|
||||||
|
- ``FloatingPanelBehavior``
|
||||||
|
- ``FloatingPanelDefaultBehavior``
|
||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>FMWK</string>
|
<string>FMWK</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2.3.1</string>
|
<string>2.8.6</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
|||||||
+93
-59
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import os.log
|
||||||
|
|
||||||
/// An interface for generating layout information for a panel.
|
/// An interface for generating layout information for a panel.
|
||||||
@objc public protocol FloatingPanelLayout {
|
@objc public protocol FloatingPanelLayout {
|
||||||
@@ -44,8 +45,8 @@ open class FloatingPanelBottomLayout: NSObject, FloatingPanelLayout {
|
|||||||
|
|
||||||
open func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
open func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
||||||
return [
|
return [
|
||||||
surfaceView.leftAnchor.constraint(equalTo: view.fp_safeAreaLayoutGuide.leftAnchor, constant: 0.0),
|
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 0.0),
|
||||||
surfaceView.rightAnchor.constraint(equalTo: view.fp_safeAreaLayoutGuide.rightAnchor, constant: 0.0),
|
surfaceView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: 0.0),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,10 +93,19 @@ class LayoutAdapter {
|
|||||||
|
|
||||||
private var staticConstraint: NSLayoutConstraint?
|
private var staticConstraint: NSLayoutConstraint?
|
||||||
|
|
||||||
private var activeStates: Set<FloatingPanelState> {
|
/// A layout constraint to limit the content size in ``FloatingPanelAdaptiveLayoutAnchor``.
|
||||||
|
private var contentBoundingConstraint: NSLayoutConstraint?
|
||||||
|
|
||||||
|
private var anchorStates: Set<FloatingPanelState> {
|
||||||
return Set(layout.anchors.keys)
|
return Set(layout.anchors.keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var sortedAnchorStates: [FloatingPanelState] {
|
||||||
|
return anchorStates.sorted(by: {
|
||||||
|
return $0.order < $1.order
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
var initialState: FloatingPanelState {
|
var initialState: FloatingPanelState {
|
||||||
layout.initialState
|
layout.initialState
|
||||||
}
|
}
|
||||||
@@ -104,18 +114,12 @@ class LayoutAdapter {
|
|||||||
layout.position
|
layout.position
|
||||||
}
|
}
|
||||||
|
|
||||||
var orderedStates: [FloatingPanelState] {
|
|
||||||
return activeStates.sorted(by: {
|
|
||||||
return $0.order < $1.order
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var validStates: Set<FloatingPanelState> {
|
var validStates: Set<FloatingPanelState> {
|
||||||
return activeStates.union([.hidden])
|
return anchorStates.union([.hidden])
|
||||||
}
|
}
|
||||||
|
|
||||||
var sortedDirectionalStates: [FloatingPanelState] {
|
var sortedAnchorStatesByCoordinate: [FloatingPanelState] {
|
||||||
return activeStates.sorted(by: {
|
return anchorStates.sorted(by: {
|
||||||
switch position {
|
switch position {
|
||||||
case .top, .left:
|
case .top, .left:
|
||||||
return $0.order < $1.order
|
return $0.order < $1.order
|
||||||
@@ -125,30 +129,26 @@ class LayoutAdapter {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private var directionalLeastState: FloatingPanelState {
|
private var leastCoordinateState: FloatingPanelState {
|
||||||
return sortedDirectionalStates.first ?? .hidden
|
return sortedAnchorStatesByCoordinate.first ?? .hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
private var directionalMostState: FloatingPanelState {
|
private var mostCoordinateState: FloatingPanelState {
|
||||||
return sortedDirectionalStates.last ?? .hidden
|
return sortedAnchorStatesByCoordinate.last ?? .hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
var edgeLeastState: FloatingPanelState {
|
var leastExpandedState: FloatingPanelState {
|
||||||
if orderedStates.count == 1 {
|
if sortedAnchorStates.count == 1 {
|
||||||
return .hidden
|
return .hidden
|
||||||
}
|
}
|
||||||
return orderedStates.first ?? .hidden
|
return sortedAnchorStates.first ?? .hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
var edgeMostState: FloatingPanelState {
|
var mostExpandedState: FloatingPanelState {
|
||||||
if orderedStates.count == 1 {
|
if sortedAnchorStates.count == 1 {
|
||||||
return orderedStates[0]
|
return sortedAnchorStates[0]
|
||||||
}
|
}
|
||||||
return orderedStates.last ?? .hidden
|
return sortedAnchorStates.last ?? .hidden
|
||||||
}
|
|
||||||
|
|
||||||
var edgeMostY: CGFloat {
|
|
||||||
return position(for: edgeMostState)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var adjustedContentInsets: UIEdgeInsets {
|
var adjustedContentInsets: UIEdgeInsets {
|
||||||
@@ -265,13 +265,26 @@ class LayoutAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var offsetFromEdgeMost: CGFloat {
|
var offsetFromMostExpandedAnchor: CGFloat {
|
||||||
|
return offset(from: mostExpandedState)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The distance from the given state position to the current surface location.
|
||||||
|
///
|
||||||
|
/// If the returned value is positive, it indicates that the surface is moving from
|
||||||
|
/// the given state position to closer to the `hidden` state position. In other
|
||||||
|
/// words, the surface is within the given state position. Otherwise, it indicates
|
||||||
|
/// that the surface is outside this position and is moving away from the `hidden`
|
||||||
|
/// state position.
|
||||||
|
func offset(from state: FloatingPanelState) -> CGFloat {
|
||||||
|
let offset: CGFloat
|
||||||
switch position {
|
switch position {
|
||||||
case .top, .left:
|
case .top, .left:
|
||||||
return edgePosition(surfaceView.presentationFrame) - position(for: directionalMostState)
|
offset = position(for: state) - edgePosition(surfaceView.frame)
|
||||||
case .bottom, .right:
|
case .bottom, .right:
|
||||||
return position(for: directionalLeastState) - edgePosition(surfaceView.presentationFrame)
|
offset = edgePosition(surfaceView.frame) - position(for: state)
|
||||||
}
|
}
|
||||||
|
return offset.rounded(by: surfaceView.fp_displayScale)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var hiddenAnchor: FloatingPanelLayoutAnchoring {
|
private var hiddenAnchor: FloatingPanelLayoutAnchoring {
|
||||||
@@ -334,12 +347,27 @@ class LayoutAdapter {
|
|||||||
if anchor.referenceGuide == .safeArea {
|
if anchor.referenceGuide == .safeArea {
|
||||||
referenceBoundsLength += position.inset(safeAreaInsets)
|
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:
|
case .bottom, .right:
|
||||||
if anchor.referenceGuide == .safeArea {
|
if anchor.referenceGuide == .safeArea {
|
||||||
referenceBoundsLength -= position.inset(safeAreaInsets)
|
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:
|
case let anchor as FloatingPanelLayoutAnchor:
|
||||||
let referenceBounds = anchor.referenceGuide == .safeArea ? bounds.inset(by: safeAreaInsets) : bounds
|
let referenceBounds = anchor.referenceGuide == .safeArea ? bounds.inset(by: safeAreaInsets) : bounds
|
||||||
@@ -407,13 +435,13 @@ class LayoutAdapter {
|
|||||||
switch position {
|
switch position {
|
||||||
case .top, .bottom:
|
case .top, .bottom:
|
||||||
surfaceConstraints = [
|
surfaceConstraints = [
|
||||||
surfaceView.leftAnchor.constraint(equalTo: vc.fp_safeAreaLayoutGuide.leftAnchor, constant: 0.0),
|
surfaceView.leftAnchor.constraint(equalTo: vc.view.safeAreaLayoutGuide.leftAnchor, constant: 0.0),
|
||||||
surfaceView.rightAnchor.constraint(equalTo: vc.fp_safeAreaLayoutGuide.rightAnchor, constant: 0.0),
|
surfaceView.rightAnchor.constraint(equalTo: vc.view.safeAreaLayoutGuide.rightAnchor, constant: 0.0),
|
||||||
]
|
]
|
||||||
case .left, .right:
|
case .left, .right:
|
||||||
surfaceConstraints = [
|
surfaceConstraints = [
|
||||||
surfaceView.topAnchor.constraint(equalTo: vc.fp_safeAreaLayoutGuide.topAnchor, constant: 0.0),
|
surfaceView.topAnchor.constraint(equalTo: vc.view.safeAreaLayoutGuide.topAnchor, constant: 0.0),
|
||||||
surfaceView.bottomAnchor.constraint(equalTo: vc.fp_safeAreaLayoutGuide.bottomAnchor, constant: 0.0),
|
surfaceView.bottomAnchor.constraint(equalTo: vc.view.safeAreaLayoutGuide.bottomAnchor, constant: 0.0),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -521,7 +549,7 @@ class LayoutAdapter {
|
|||||||
let layoutGuideProvider: LayoutGuideProvider
|
let layoutGuideProvider: LayoutGuideProvider
|
||||||
switch anchor.referenceGuide {
|
switch anchor.referenceGuide {
|
||||||
case .safeArea:
|
case .safeArea:
|
||||||
layoutGuideProvider = vc.fp_safeAreaLayoutGuide
|
layoutGuideProvider = vc.view.safeAreaLayoutGuide
|
||||||
case .superview:
|
case .superview:
|
||||||
layoutGuideProvider = vc.view
|
layoutGuideProvider = vc.view
|
||||||
}
|
}
|
||||||
@@ -633,15 +661,16 @@ class LayoutAdapter {
|
|||||||
// The method is separated from prepareLayout(to:) for the rotation support
|
// The method is separated from prepareLayout(to:) for the rotation support
|
||||||
// It must be called in FloatingPanelController.traitCollectionDidChange(_:)
|
// It must be called in FloatingPanelController.traitCollectionDidChange(_:)
|
||||||
func updateStaticConstraint() {
|
func updateStaticConstraint() {
|
||||||
NSLayoutConstraint.deactivate(constraint: staticConstraint)
|
NSLayoutConstraint.deactivate([staticConstraint, contentBoundingConstraint].compactMap{ $0 })
|
||||||
staticConstraint = nil
|
staticConstraint = nil
|
||||||
|
contentBoundingConstraint = nil
|
||||||
|
|
||||||
if vc.contentMode == .fitToBounds {
|
if vc.contentMode == .fitToBounds {
|
||||||
surfaceView.containerOverflow = 0
|
surfaceView.containerOverflow = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let anchor = layout.anchors[self.edgeMostState]!
|
let anchor = layout.anchors[self.mostExpandedState]!
|
||||||
let surfaceAnchor = position.mainDimensionAnchor(surfaceView)
|
let surfaceAnchor = position.mainDimensionAnchor(surfaceView)
|
||||||
switch anchor {
|
switch anchor {
|
||||||
case let anchor as FloatingPanelIntrinsicLayoutAnchor:
|
case let anchor as FloatingPanelIntrinsicLayoutAnchor:
|
||||||
@@ -658,15 +687,26 @@ class LayoutAdapter {
|
|||||||
constant = 0.0
|
constant = 0.0
|
||||||
}
|
}
|
||||||
let baseAnchor = position.mainDimensionAnchor(anchor.contentLayoutGuide)
|
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:
|
default:
|
||||||
switch position {
|
switch position {
|
||||||
case .top, .left:
|
case .top, .left:
|
||||||
staticConstraint = surfaceAnchor.constraint(equalToConstant: position(for: self.directionalMostState))
|
staticConstraint = surfaceAnchor.constraint(equalToConstant: position(for: self.mostCoordinateState))
|
||||||
case .bottom, .right:
|
case .bottom, .right:
|
||||||
let rootViewAnchor = position.mainDimensionAnchor(vc.view)
|
let rootViewAnchor = position.mainDimensionAnchor(vc.view)
|
||||||
staticConstraint = rootViewAnchor.constraint(equalTo: surfaceAnchor,
|
staticConstraint = rootViewAnchor.constraint(equalTo: surfaceAnchor,
|
||||||
constant: position(for: self.directionalLeastState))
|
constant: position(for: self.leastCoordinateState))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,18 +717,18 @@ class LayoutAdapter {
|
|||||||
staticConstraint?.identifier = "FloatingPanel-static-width"
|
staticConstraint?.identifier = "FloatingPanel-static-width"
|
||||||
}
|
}
|
||||||
|
|
||||||
NSLayoutConstraint.activate(constraint: staticConstraint)
|
NSLayoutConstraint.activate([staticConstraint, contentBoundingConstraint].compactMap{ $0 })
|
||||||
|
|
||||||
surfaceView.containerOverflow = position.mainDimension(vc.view.bounds.size)
|
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 {
|
defer {
|
||||||
log.debug("update surface location = \(surfaceLocation)")
|
os_log(msg, log: devLog, type: .debug, "update surface location = \(surfaceLocation)")
|
||||||
}
|
}
|
||||||
|
|
||||||
let minConst: CGFloat = position(for: directionalLeastState)
|
let minConst: CGFloat = position(for: leastCoordinateState)
|
||||||
let maxConst: CGFloat = position(for: directionalMostState)
|
let maxConst: CGFloat = position(for: mostCoordinateState)
|
||||||
|
|
||||||
var const = initialConst + diff
|
var const = initialConst + diff
|
||||||
|
|
||||||
@@ -705,7 +745,7 @@ class LayoutAdapter {
|
|||||||
const = maxConst + rubberBandEffect(for: buffer, base: base)
|
const = maxConst + rubberBandEffect(for: buffer, base: base)
|
||||||
}
|
}
|
||||||
|
|
||||||
if overflow == false {
|
if scrollingContent {
|
||||||
const = min(max(const, minConst), maxConst)
|
const = min(max(const, minConst), maxConst)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,9 +764,9 @@ class LayoutAdapter {
|
|||||||
defer {
|
defer {
|
||||||
if forceLayout {
|
if forceLayout {
|
||||||
layoutSurfaceIfNeeded()
|
layoutSurfaceIfNeeded()
|
||||||
log.debug("activateLayout for \(state) -- surface.presentation = \(self.surfaceView.presentationFrame) surface.frame = \(self.surfaceView.frame)")
|
os_log(msg, log: devLog, type: .debug, "activateLayout for \(state) -- surface.presentation = \(self.surfaceView.presentationFrame) surface.frame = \(self.surfaceView.frame)")
|
||||||
} else {
|
} else {
|
||||||
log.debug("activateLayout for \(state)")
|
os_log(msg, log: devLog, type: .debug, "activateLayout for \(state)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,12 +782,6 @@ class LayoutAdapter {
|
|||||||
NSLayoutConstraint.activate(constraint: self.fitToBoundsConstraint)
|
NSLayoutConstraint.activate(constraint: self.fitToBoundsConstraint)
|
||||||
}
|
}
|
||||||
|
|
||||||
var state = state
|
|
||||||
|
|
||||||
if validStates.contains(state) == false {
|
|
||||||
state = layout.initialState
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recalculate the intrinsic size of a content view. This is because
|
// Recalculate the intrinsic size of a content view. This is because
|
||||||
// UIView.systemLayoutSizeFitting() returns a different size between an
|
// UIView.systemLayoutSizeFitting() returns a different size between an
|
||||||
// on-screen and off-screen view which includes
|
// on-screen and off-screen view which includes
|
||||||
@@ -761,7 +795,7 @@ class LayoutAdapter {
|
|||||||
if let constraints = stateConstraints[state] {
|
if let constraints = stateConstraints[state] {
|
||||||
NSLayoutConstraint.activate(constraints)
|
NSLayoutConstraint.activate(constraints)
|
||||||
} else {
|
} else {
|
||||||
log.error("Couldn't find any constraints for \(state)")
|
os_log(msg, log: sysLog, type: .fault, "Error: can not find any constraints for \(state)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -779,7 +813,7 @@ class LayoutAdapter {
|
|||||||
|
|
||||||
fileprivate func checkLayout() {
|
fileprivate func checkLayout() {
|
||||||
// Verify layout configurations
|
// Verify layout configurations
|
||||||
assert(activeStates.count > 0)
|
assert(anchorStates.count > 0)
|
||||||
assert(validStates.contains(layout.initialState),
|
assert(validStates.contains(layout.initialState),
|
||||||
"Does not include an initial state (\(layout.initialState)) in (\(validStates))")
|
"Does not include an initial state (\(layout.initialState)) in (\(validStates))")
|
||||||
/* This assertion does not work in a device rotating
|
/* This assertion does not work in a device rotating
|
||||||
@@ -799,7 +833,7 @@ extension LayoutAdapter {
|
|||||||
/// |-------|-------|===o===| |-------|===o===|-------|
|
/// |-------|-------|===o===| |-------|===o===|-------|
|
||||||
/// pos: o/x, segment: =
|
/// pos: o/x, segment: =
|
||||||
|
|
||||||
let sortedStates = sortedDirectionalStates
|
let sortedStates = sortedAnchorStatesByCoordinate
|
||||||
|
|
||||||
let upperIndex: Int?
|
let upperIndex: Int?
|
||||||
if forward {
|
if forward {
|
||||||
|
|||||||
@@ -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
|
/// The inset is an amount to inset a panel from an edge of the reference guide. The edge refers to a panel
|
||||||
/// positioning.
|
/// positioning.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - absoluteInset: An absolute distance to attach the panel from the specified 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) {
|
@objc public init(absoluteInset: CGFloat, edge: FloatingPanelReferenceEdge, referenceGuide: FloatingPanelLayoutReferenceGuide) {
|
||||||
self.inset = absoluteInset
|
self.inset = absoluteInset
|
||||||
self.referenceGuide = referenceGuide
|
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
|
/// 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
|
/// 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.
|
/// 1.0 represents a distance to the opposite edge.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - fractionalInset: A fractional value of the size of ``FloatingPanelController``'s view to attach the panel from the specified 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) {
|
@objc public init(fractionalInset: CGFloat, edge: FloatingPanelReferenceEdge, referenceGuide: FloatingPanelLayoutReferenceGuide) {
|
||||||
self.inset = fractionalInset
|
self.inset = fractionalInset
|
||||||
self.referenceGuide = referenceGuide
|
self.referenceGuide = referenceGuide
|
||||||
@@ -49,7 +59,7 @@ public extension FloatingPanelLayoutAnchor {
|
|||||||
case .left:
|
case .left:
|
||||||
return layoutConstraints(layoutGuide, for: vc.surfaceView.rightAnchor)
|
return layoutConstraints(layoutGuide, for: vc.surfaceView.rightAnchor)
|
||||||
case .bottom:
|
case .bottom:
|
||||||
return layoutConstraints(layoutGuide, for: vc.surfaceView.topAnchor)
|
return layoutConstraints(layoutGuide, for: vc.surfaceView.topAnchor)
|
||||||
case .right:
|
case .right:
|
||||||
return layoutConstraints(layoutGuide, for: vc.surfaceView.leftAnchor)
|
return layoutConstraints(layoutGuide, for: vc.surfaceView.leftAnchor)
|
||||||
}
|
}
|
||||||
@@ -101,8 +111,12 @@ public extension FloatingPanelLayoutAnchor {
|
|||||||
///
|
///
|
||||||
/// The offset is an amount to offset a position of panel that displays the entire content from an edge of
|
/// 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.
|
/// the reference guide. The edge refers to a panel positioning.
|
||||||
@objc public init(absoluteOffset offset: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
///
|
||||||
self.offset = offset
|
/// - 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: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
||||||
|
self.offset = absoluteOffset
|
||||||
self.referenceGuide = referenceGuide
|
self.referenceGuide = referenceGuide
|
||||||
self.isAbsolute = true
|
self.isAbsolute = true
|
||||||
}
|
}
|
||||||
@@ -111,8 +125,12 @@ 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
|
/// 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.
|
/// is displayed and 0.5 represents the half of content is displayed.
|
||||||
@objc public init(fractionalOffset offset: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
///
|
||||||
self.offset = offset
|
/// - 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: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
||||||
|
self.offset = fractionalOffset
|
||||||
self.referenceGuide = referenceGuide
|
self.referenceGuide = referenceGuide
|
||||||
self.isAbsolute = false
|
self.isAbsolute = false
|
||||||
}
|
}
|
||||||
@@ -144,37 +162,72 @@ public extension FloatingPanelIntrinsicLayoutAnchor {
|
|||||||
/// An object that defines how to settles a panel with a layout guide of a content view.
|
/// 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 */ {
|
@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 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.
|
/// the reference guide. The edge refers to a panel positioning.
|
||||||
@objc public init(absoluteOffset offset: CGFloat, contentLayout: UILayoutGuide, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
///
|
||||||
self.offset = offset
|
/// ``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: CGFloat,
|
||||||
|
contentLayout: UILayoutGuide,
|
||||||
|
referenceGuide: FloatingPanelLayoutReferenceGuide,
|
||||||
|
contentBoundingGuide: FloatingPanelLayoutContentBoundingGuide = .none
|
||||||
|
) {
|
||||||
|
self.offset = absoluteOffset
|
||||||
self.contentLayoutGuide = contentLayout
|
self.contentLayoutGuide = contentLayout
|
||||||
self.referenceGuide = referenceGuide
|
self.referenceGuide = referenceGuide
|
||||||
|
self.contentBoundingGuide = contentBoundingGuide
|
||||||
self.isAbsolute = true
|
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
|
/// 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.
|
/// is displayed and 0.5 represents the half of content is displayed.
|
||||||
@objc public init(fractionalOffset offset: CGFloat, contentLayout: UILayoutGuide, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
///
|
||||||
self.offset = offset
|
/// ``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: CGFloat,
|
||||||
|
contentLayout: UILayoutGuide,
|
||||||
|
referenceGuide: FloatingPanelLayoutReferenceGuide,
|
||||||
|
contentBoundingGuide: FloatingPanelLayoutContentBoundingGuide = .none
|
||||||
|
) {
|
||||||
|
self.offset = fractionalOffset
|
||||||
self.contentLayoutGuide = contentLayout
|
self.contentLayoutGuide = contentLayout
|
||||||
self.referenceGuide = referenceGuide
|
self.referenceGuide = referenceGuide
|
||||||
|
self.contentBoundingGuide = contentBoundingGuide
|
||||||
self.isAbsolute = false
|
self.isAbsolute = false
|
||||||
}
|
}
|
||||||
fileprivate let offset: CGFloat
|
let offset: CGFloat
|
||||||
fileprivate let isAbsolute: Bool
|
let isAbsolute: Bool
|
||||||
let contentLayoutGuide: UILayoutGuide
|
let contentLayoutGuide: UILayoutGuide
|
||||||
@objc public let referenceGuide: FloatingPanelLayoutReferenceGuide
|
@objc public let referenceGuide: FloatingPanelLayoutReferenceGuide
|
||||||
|
@objc public let contentBoundingGuide: FloatingPanelLayoutContentBoundingGuide
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension FloatingPanelAdaptiveLayoutAnchor {
|
public extension FloatingPanelAdaptiveLayoutAnchor {
|
||||||
func layoutConstraints(_ vc: FloatingPanelController, for position: FloatingPanelPosition) -> [NSLayoutConstraint] {
|
func layoutConstraints(_ vc: FloatingPanelController, for position: FloatingPanelPosition) -> [NSLayoutConstraint] {
|
||||||
|
var constraints = [NSLayoutConstraint]()
|
||||||
|
|
||||||
let layoutGuide = referenceGuide.layoutGuide(vc: vc)
|
let layoutGuide = referenceGuide.layoutGuide(vc: vc)
|
||||||
|
let offsetConstraint: NSLayoutConstraint
|
||||||
let offsetAnchor: NSLayoutDimension
|
let offsetAnchor: NSLayoutDimension
|
||||||
switch position {
|
switch position {
|
||||||
case .top:
|
case .top:
|
||||||
@@ -187,10 +240,13 @@ public extension FloatingPanelAdaptiveLayoutAnchor {
|
|||||||
offsetAnchor = vc.surfaceView.leftAnchor.anchorWithOffset(to: layoutGuide.rightAnchor)
|
offsetAnchor = vc.surfaceView.leftAnchor.anchorWithOffset(to: layoutGuide.rightAnchor)
|
||||||
}
|
}
|
||||||
if isAbsolute {
|
if isAbsolute {
|
||||||
return [offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), constant: -offset)]
|
offsetConstraint = offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), constant: -offset)
|
||||||
} else {
|
} 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 {
|
@objc public enum FloatingPanelLayoutReferenceGuide: Int {
|
||||||
case superview = 0
|
case superview = 0
|
||||||
case safeArea = 1
|
case safeArea = 1
|
||||||
@@ -37,9 +37,40 @@ extension FloatingPanelLayoutReferenceGuide {
|
|||||||
func layoutGuide(vc: UIViewController) -> LayoutGuideProvider {
|
func layoutGuide(vc: UIViewController) -> LayoutGuideProvider {
|
||||||
switch self {
|
switch self {
|
||||||
case .safeArea:
|
case .safeArea:
|
||||||
return vc.fp_safeAreaLayoutGuide
|
return vc.view.safeAreaLayoutGuide
|
||||||
case .superview:
|
case .superview:
|
||||||
return vc.view
|
return vc.view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.view.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import os.log
|
|
||||||
|
|
||||||
// Must be a variable to use `hook` property in testing
|
|
||||||
var log = {
|
|
||||||
return Logger()
|
|
||||||
}()
|
|
||||||
|
|
||||||
struct Logger {
|
|
||||||
private let osLog: OSLog
|
|
||||||
private let s = DispatchSemaphore(value: 1)
|
|
||||||
|
|
||||||
enum Level: Int, Comparable {
|
|
||||||
case debug = 0
|
|
||||||
case info = 1
|
|
||||||
case warning = 2
|
|
||||||
case error = 3
|
|
||||||
|
|
||||||
var displayName: String {
|
|
||||||
switch self {
|
|
||||||
case .debug:
|
|
||||||
return "Debug:"
|
|
||||||
case .info:
|
|
||||||
return "Info:"
|
|
||||||
case .warning:
|
|
||||||
return "Warning:"
|
|
||||||
case .error:
|
|
||||||
return "Error:"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@available(iOS 10.0, *)
|
|
||||||
var osLogType: OSLogType {
|
|
||||||
switch self {
|
|
||||||
case .debug: return .debug
|
|
||||||
case .info: return .info
|
|
||||||
case .warning: return .default
|
|
||||||
case .error: return .error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func < (lhs: Logger.Level, rhs: Logger.Level) -> Bool {
|
|
||||||
return lhs.rawValue < rhs.rawValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typealias Hook = ((String, Level) -> Void)
|
|
||||||
var hook: Hook?
|
|
||||||
|
|
||||||
fileprivate init() {
|
|
||||||
osLog = OSLog(subsystem: "com.scenee.FloatingPanel", category: "FloatingPanel")
|
|
||||||
}
|
|
||||||
|
|
||||||
private func log(_ level: Level, _ message: Any, _ arguments: [Any], tag: String, function: String, line: UInt) {
|
|
||||||
_ = s.wait(timeout: .now() + 0.033)
|
|
||||||
defer { s.signal() }
|
|
||||||
|
|
||||||
let extraMessage: String = arguments.map({ String(describing: $0) }).joined(separator: " ")
|
|
||||||
let _tag = tag.isEmpty ? "" : "\(tag):"
|
|
||||||
let log: String = {
|
|
||||||
switch level {
|
|
||||||
case .debug:
|
|
||||||
return "\(level.displayName)\(_tag) \(message) \(extraMessage) (\(function):\(line))"
|
|
||||||
default:
|
|
||||||
return "\(level.displayName)\(_tag) \(message) \(extraMessage)"
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
hook?(log, level)
|
|
||||||
|
|
||||||
os_log("%{public}@", log: osLog, type: level.osLogType, log)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func getPrettyFunction(_ function: String, _ file: String) -> String {
|
|
||||||
if let filename = file.split(separator: "/").last {
|
|
||||||
return filename + ":" + function
|
|
||||||
} else {
|
|
||||||
return file + ":" + function
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func debug(_ log: Any, _ arguments: Any..., tag: String = "", function: String = #function, file: String = #file, line: UInt = #line) {
|
|
||||||
#if __FP_LOG
|
|
||||||
self.log(.debug, log, arguments, tag: tag, function: getPrettyFunction(function, file), line: line)
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
func info(_ log: Any, _ arguments: Any..., tag: String = "", function: String = #function, file: String = #file, line: UInt = #line) {
|
|
||||||
self.log(.info, log, arguments, tag: tag, function: getPrettyFunction(function, file), line: line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func warning(_ log: Any, _ arguments: Any..., function: String = #function, file: String = #file, line: UInt = #line) {
|
|
||||||
self.log(.warning, log, arguments, tag: "", function: getPrettyFunction(function, file), line: line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func error(_ log: Any, _ arguments: Any..., function: String = #function, file: String = #file, line: UInt = #line) {
|
|
||||||
self.log(.error, log, arguments, tag: "", function: getPrettyFunction(function, file), line: line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import os.log
|
||||||
|
|
||||||
|
let msg = StaticString("%{public}@")
|
||||||
|
let sysLog = OSLog(subsystem: Logging.subsystem, category: Logging.category)
|
||||||
|
#if FP_LOG
|
||||||
|
let devLog = OSLog(subsystem: Logging.subsystem, category: "\(Logging.category):dev")
|
||||||
|
#else
|
||||||
|
let devLog = OSLog.disabled
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Logging {
|
||||||
|
static let subsystem = "com.scenee.FloatingPanel"
|
||||||
|
static let category = "FloatingPanel"
|
||||||
|
private init() {}
|
||||||
|
}
|
||||||
+73
-31
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import os.log
|
||||||
|
|
||||||
/// An object for customizing the appearance of a surface view
|
/// An object for customizing the appearance of a surface view
|
||||||
@objc(FloatingPanelSurfaceAppearance)
|
@objc(FloatingPanelSurfaceAppearance)
|
||||||
@@ -54,7 +55,12 @@ public class SurfaceAppearance: NSObject {
|
|||||||
///
|
///
|
||||||
/// Defaults to `.circular`.
|
/// Defaults to `.circular`.
|
||||||
@available(iOS 13.0, *)
|
@available(iOS 13.0, *)
|
||||||
public lazy var cornerCurve: CALayerCornerCurve = .circular
|
public var cornerCurve: CALayerCornerCurve {
|
||||||
|
get { _cornerCurve as? CALayerCornerCurve ?? .circular }
|
||||||
|
set { _cornerCurve = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _cornerCurve: Any?
|
||||||
|
|
||||||
/// An array of shadows used to create drop shadows underneath a surface view.
|
/// An array of shadows used to create drop shadows underneath a surface view.
|
||||||
public var shadows: [Shadow] = [Shadow()]
|
public var shadows: [Shadow] = [Shadow()]
|
||||||
@@ -85,7 +91,7 @@ public class SurfaceView: UIView {
|
|||||||
|
|
||||||
/// The grabber handle size
|
/// The grabber handle size
|
||||||
///
|
///
|
||||||
/// On left/right positioned panel the width dimension is used as the height of `grabberHandle`, and vice versa.
|
/// On left/right positioned panel the width dimension is used as the height of ``grabberHandle``, and vice versa.
|
||||||
public var grabberHandleSize: CGSize = CGSize(width: 36.0, height: 5.0) { didSet {
|
public var grabberHandleSize: CGSize = CGSize(width: 36.0, height: 5.0) { didSet {
|
||||||
setNeedsUpdateConstraints()
|
setNeedsUpdateConstraints()
|
||||||
} }
|
} }
|
||||||
@@ -313,7 +319,7 @@ public class SurfaceView: UIView {
|
|||||||
|
|
||||||
public override func layoutSubviews() {
|
public override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
log.debug("surface view frame = \(frame)")
|
os_log(msg, log: devLog, type: .debug, "surface view frame = \(frame)")
|
||||||
|
|
||||||
containerView.backgroundColor = appearance.backgroundColor
|
containerView.backgroundColor = appearance.backgroundColor
|
||||||
|
|
||||||
@@ -377,29 +383,24 @@ public class SurfaceView: UIView {
|
|||||||
}
|
}
|
||||||
containerView.layer.masksToBounds = true
|
containerView.layer.masksToBounds = true
|
||||||
if position.inset(containerMargins) != 0 {
|
if position.inset(containerMargins) != 0 {
|
||||||
if #available(iOS 11, *) {
|
containerView.layer.maskedCorners = [
|
||||||
containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner,
|
.layerMinXMinYCorner, .layerMaxXMinYCorner,
|
||||||
.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
.layerMinXMaxYCorner, .layerMaxXMaxYCorner
|
||||||
}
|
]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if #available(iOS 11, *) {
|
// Don't use `contentView.clipToBounds` because it prevents content view from expanding the height of a subview of it
|
||||||
// Don't use `contentView.clipToBounds` because it prevents content view from expanding the height of a subview of it
|
// for the bottom overflow like Auto Layout settings of UIVisualEffectView in Main.storyboard of Example/Maps.
|
||||||
// for the bottom overflow like Auto Layout settings of UIVisualEffectView in Main.storyboard of Example/Maps.
|
// Because the bottom of contentView must be fit to the bottom of a screen to work the `safeLayoutGuide` of a content VC.
|
||||||
// Because the bottom of contentView must be fit to the bottom of a screen to work the `safeLayoutGuide` of a content VC.
|
switch position {
|
||||||
switch position {
|
case .top:
|
||||||
case .top:
|
containerView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||||
containerView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
case .left:
|
||||||
case .left:
|
containerView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
|
||||||
containerView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
|
case .bottom:
|
||||||
case .bottom:
|
containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||||
containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
case .right:
|
||||||
case .right:
|
containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
|
||||||
containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Can't use `containerView.layer.mask` because of a UIVisualEffectView issue in iOS 10, https://forums.developer.apple.com/thread/50854
|
|
||||||
// Instead, a user should display rounding corners appropriately.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,25 +409,48 @@ public class SurfaceView: UIView {
|
|||||||
containerView.layer.borderWidth = appearance.borderWidth
|
containerView.layer.borderWidth = appearance.borderWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
func set(contentView: UIView) {
|
func set(contentView: UIView, mode: FloatingPanelController.ContentMode) {
|
||||||
containerView.addSubview(contentView)
|
containerView.addSubview(contentView)
|
||||||
self.contentView = contentView
|
self.contentView = contentView
|
||||||
/* contentView.frame = bounds */ // MUST NOT: Because the top safe area inset of a content VC will be incorrect.
|
/* contentView.frame = bounds */ // MUST NOT: Because the top safe area inset of a content VC will be incorrect.
|
||||||
contentView.translatesAutoresizingMaskIntoConstraints = false
|
contentView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
let topConstraint = contentView.topAnchor.constraint(equalTo: topAnchor, constant: containerMargins.top + contentPadding.top)
|
let topConstraint = contentView.topAnchor.constraint(equalTo: topAnchor, constant: containerMargins.top + contentPadding.top)
|
||||||
let leftConstraint = contentView.leftAnchor.constraint(equalTo: leftAnchor, constant: containerMargins.left + contentPadding.left)
|
let leftConstraint = contentView.leftAnchor.constraint(equalTo: leftAnchor, constant: containerMargins.left + contentPadding.left)
|
||||||
let rightConstraint = rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: containerMargins.right + contentPadding.right)
|
let rightConstraint = rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: containerMargins.right + contentPadding.right)
|
||||||
let bottomConstraint = bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: containerMargins.bottom + contentPadding.bottom)
|
let bottomConstraint = bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: containerMargins.bottom + contentPadding.bottom)
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
|
var constraints = [
|
||||||
topConstraint,
|
topConstraint,
|
||||||
leftConstraint,
|
leftConstraint,
|
||||||
rightConstraint,
|
rightConstraint,
|
||||||
bottomConstraint,
|
bottomConstraint
|
||||||
].map {
|
]
|
||||||
$0.priority = .required - 1;
|
|
||||||
|
// This constraint is for UICollectionView using UICollectionViewCompositionalLayout.
|
||||||
|
// It's seemingly obvious, but the UICollectionView doesn't work without setting it. (#628)
|
||||||
|
switch position {
|
||||||
|
case .top, .bottom:
|
||||||
|
constraints += [
|
||||||
|
heightAnchor.constraint(greaterThanOrEqualToConstant: 1.0)
|
||||||
|
]
|
||||||
|
case .left, .right:
|
||||||
|
constraints += [
|
||||||
|
widthAnchor.constraint(greaterThanOrEqualToConstant: 1.0)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate(
|
||||||
|
constraints
|
||||||
|
.map {
|
||||||
|
switch mode {
|
||||||
|
case .static:
|
||||||
|
$0.priority = .required
|
||||||
|
// The reason why this priority is set to .required - 1 is #359, which fixed #294.
|
||||||
|
case .fitToBounds:
|
||||||
|
$0.priority = .required - 1
|
||||||
|
}
|
||||||
$0.identifier = "FloatingPanel-surface-content"
|
$0.identifier = "FloatingPanel-surface-content"
|
||||||
return $0;
|
return $0
|
||||||
})
|
})
|
||||||
self.contentViewTopConstraint = topConstraint
|
self.contentViewTopConstraint = topConstraint
|
||||||
self.contentViewLeftConstraint = leftConstraint
|
self.contentViewLeftConstraint = leftConstraint
|
||||||
@@ -437,4 +461,22 @@ public class SurfaceView: UIView {
|
|||||||
func hasStackView() -> Bool {
|
func hasStackView() -> Bool {
|
||||||
return contentView?.subviews.reduce(false) { $0 || ($1 is UIStackView) } ?? false
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class ModalPresentTransition: NSObject, UIViewControllerAnimatedTransitioning {
|
|||||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||||
guard
|
guard
|
||||||
let fpc = transitionContext?.viewController(forKey: .to) as? FloatingPanelController
|
let fpc = transitionContext?.viewController(forKey: .to) as? FloatingPanelController
|
||||||
else { fatalError()}
|
else { return 0.0 }
|
||||||
|
|
||||||
let animator = fpc.animatorForPresenting(to: fpc.layout.initialState)
|
let animator = fpc.animatorForPresenting(to: fpc.layout.initialState)
|
||||||
return TimeInterval(animator.duration)
|
return TimeInterval(animator.duration)
|
||||||
@@ -104,7 +104,10 @@ class ModalPresentTransition: NSObject, UIViewControllerAnimatedTransitioning {
|
|||||||
fpc?.suspendTransitionAnimator(false)
|
fpc?.suspendTransitionAnimator(false)
|
||||||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
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) {
|
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||||
@@ -116,7 +119,7 @@ class ModalDismissTransition: NSObject, UIViewControllerAnimatedTransitioning {
|
|||||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||||
guard
|
guard
|
||||||
let fpc = transitionContext?.viewController(forKey: .from) as? FloatingPanelController
|
let fpc = transitionContext?.viewController(forKey: .from) as? FloatingPanelController
|
||||||
else { fatalError()}
|
else { return 0.0 }
|
||||||
|
|
||||||
let animator = fpc.animatorForDismissing(with: .zero)
|
let animator = fpc.animatorForDismissing(with: .zero)
|
||||||
return TimeInterval(animator.duration)
|
return TimeInterval(animator.duration)
|
||||||
|
|||||||
+73
-27
@@ -1,5 +1,6 @@
|
|||||||
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import OSLog
|
||||||
import XCTest
|
import XCTest
|
||||||
@testable import FloatingPanel
|
@testable import FloatingPanel
|
||||||
|
|
||||||
@@ -7,22 +8,32 @@ class ControllerTests: XCTestCase {
|
|||||||
override func setUp() {}
|
override func setUp() {}
|
||||||
override func tearDown() {}
|
override func tearDown() {}
|
||||||
|
|
||||||
func test_warningRetainCycle() {
|
#if swift(>=5.5) // Avoid the 'No exact matches in call to initializer' build failure for OSLogStore when running this test case on iOS 13.7 using Xcode 12.5.1
|
||||||
let exp = expectation(description: "Warning retain cycle")
|
func test_warningRetainCycle() throws {
|
||||||
exp.expectedFulfillmentCount = 2 // For layout & behavior logs
|
guard #available(iOS 15.0, *) else {
|
||||||
log.hook = {(log, level) in
|
throw XCTSkip("Unsupported iOS version: this test needs iOS 15 or later")
|
||||||
if log.contains("A memory leak will occur by a retain cycle because") {
|
|
||||||
XCTAssert(level == .warning)
|
|
||||||
exp.fulfill()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let myVC = MyZombieViewController(nibName: nil, bundle: nil)
|
let myVC = MyZombieViewController(nibName: nil, bundle: nil)
|
||||||
myVC.loadViewIfNeeded()
|
myVC.loadViewIfNeeded()
|
||||||
wait(for: [exp], timeout: 10)
|
let store = try OSLogStore(scope: .currentProcessIdentifier)
|
||||||
|
let found = try store
|
||||||
|
.getEntries(
|
||||||
|
at: store.position(timeIntervalSinceLatestBoot: 0),
|
||||||
|
matching: .init(format: "subsystem == '\(Logging.subsystem)'")
|
||||||
|
)
|
||||||
|
.contains {
|
||||||
|
$0.composedMessage.contains("A memory leak occurs due to a retain cycle, as")
|
||||||
|
}
|
||||||
|
XCTAssertTrue(found)
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
func test_addPanel() {
|
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()
|
let fpc = FloatingPanelController()
|
||||||
fpc.addPanel(toParent: rootVC)
|
fpc.addPanel(toParent: rootVC)
|
||||||
XCTAssertEqual(fpc.surfaceLocation.y, fpc.surfaceLocation(for: .half).y)
|
XCTAssertEqual(fpc.surfaceLocation.y, fpc.surfaceLocation(for: .half).y)
|
||||||
@@ -30,8 +41,13 @@ class ControllerTests: XCTestCase {
|
|||||||
XCTAssertEqual(fpc.surfaceLocation.y, fpc.surfaceLocation(for: .tip).y)
|
XCTAssertEqual(fpc.surfaceLocation.y, fpc.surfaceLocation(for: .tip).y)
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS 12.0, *)
|
func test_updateLayout_willTransition() throws {
|
||||||
func test_updateLayout_willTransition() {
|
guard #available(iOS 12, *) else {
|
||||||
|
throw XCTSkip("Unsupported iOS version: this test needs iOS 12 or later")
|
||||||
|
}
|
||||||
|
if #available(iOS 17, *) {
|
||||||
|
throw XCTSkip("Unsupported iOS version: this test doesn't support iOS 17 or later")
|
||||||
|
}
|
||||||
class MyDelegate: FloatingPanelControllerDelegate {
|
class MyDelegate: FloatingPanelControllerDelegate {
|
||||||
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
|
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
|
||||||
if newCollection.userInterfaceStyle == .dark {
|
if newCollection.userInterfaceStyle == .dark {
|
||||||
@@ -48,6 +64,7 @@ class ControllerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_moveTo() {
|
func test_moveTo() {
|
||||||
|
let timeout = 3.0
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
let fpc = FloatingPanelController(delegate: delegate)
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
XCTAssertEqual(delegate.position, .hidden)
|
XCTAssertEqual(delegate.position, .hidden)
|
||||||
@@ -86,7 +103,7 @@ class ControllerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
XCTAssertEqual(fpc.state, .full)
|
XCTAssertEqual(fpc.state, .full)
|
||||||
XCTAssertEqual(delegate.position, .full)
|
XCTAssertEqual(delegate.position, .full)
|
||||||
wait(for: [exp], timeout: 1.0)
|
wait(for: [exp], timeout: timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTContext.runActivity(named: "move to half(animated)") { act in
|
XCTContext.runActivity(named: "move to half(animated)") { act in
|
||||||
@@ -97,7 +114,7 @@ class ControllerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
XCTAssertEqual(fpc.state, .half)
|
XCTAssertEqual(fpc.state, .half)
|
||||||
XCTAssertEqual(delegate.position, .half)
|
XCTAssertEqual(delegate.position, .half)
|
||||||
wait(for: [exp], timeout: 1.0)
|
wait(for: [exp], timeout: timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTContext.runActivity(named: "move to tip(animated)") { act in
|
XCTContext.runActivity(named: "move to tip(animated)") { act in
|
||||||
@@ -108,7 +125,7 @@ class ControllerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
XCTAssertEqual(fpc.state, .tip)
|
XCTAssertEqual(fpc.state, .tip)
|
||||||
XCTAssertEqual(delegate.position, .tip)
|
XCTAssertEqual(delegate.position, .tip)
|
||||||
wait(for: [exp], timeout: 1.0)
|
wait(for: [exp], timeout: timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
fpc.move(to: .hidden, animated: true)
|
fpc.move(to: .hidden, animated: true)
|
||||||
@@ -121,6 +138,7 @@ class ControllerTests: XCTestCase {
|
|||||||
class MyFloatingPanelTop2BottomLayout: FloatingPanelTop2BottomTestLayout {
|
class MyFloatingPanelTop2BottomLayout: FloatingPanelTop2BottomTestLayout {
|
||||||
override var initialState: FloatingPanelState { return .half }
|
override var initialState: FloatingPanelState { return .half }
|
||||||
}
|
}
|
||||||
|
let timeout = 3.0
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
let fpc = FloatingPanelController(delegate: delegate)
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
fpc.layout = MyFloatingPanelTop2BottomLayout()
|
fpc.layout = MyFloatingPanelTop2BottomLayout()
|
||||||
@@ -159,7 +177,7 @@ class ControllerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
XCTAssertEqual(fpc.state, .full)
|
XCTAssertEqual(fpc.state, .full)
|
||||||
XCTAssertEqual(delegate.position, .full)
|
XCTAssertEqual(delegate.position, .full)
|
||||||
wait(for: [exp], timeout: 1.0)
|
wait(for: [exp], timeout: timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTContext.runActivity(named: "move to half(animated)") { act in
|
XCTContext.runActivity(named: "move to half(animated)") { act in
|
||||||
@@ -170,7 +188,7 @@ class ControllerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
XCTAssertEqual(fpc.state, .half)
|
XCTAssertEqual(fpc.state, .half)
|
||||||
XCTAssertEqual(delegate.position, .half)
|
XCTAssertEqual(delegate.position, .half)
|
||||||
wait(for: [exp], timeout: 1.0)
|
wait(for: [exp], timeout: timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
XCTContext.runActivity(named: "move to tip(animated)") { act in
|
XCTContext.runActivity(named: "move to tip(animated)") { act in
|
||||||
@@ -181,7 +199,7 @@ class ControllerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
XCTAssertEqual(fpc.state, .tip)
|
XCTAssertEqual(fpc.state, .tip)
|
||||||
XCTAssertEqual(delegate.position, .tip)
|
XCTAssertEqual(delegate.position, .tip)
|
||||||
wait(for: [exp], timeout: 1.0)
|
wait(for: [exp], timeout: timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
fpc.move(to: .hidden, animated: true)
|
fpc.move(to: .hidden, animated: true)
|
||||||
@@ -207,6 +225,7 @@ class ControllerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_moveTo_didMoveDelegate() {
|
func test_moveTo_didMoveDelegate() {
|
||||||
|
let timeout = 3.0
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
let fpc = FloatingPanelController(delegate: delegate)
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
XCTAssertEqual(delegate.position, .hidden)
|
XCTAssertEqual(delegate.position, .hidden)
|
||||||
@@ -221,7 +240,7 @@ class ControllerTests: XCTestCase {
|
|||||||
exp.fulfill()
|
exp.fulfill()
|
||||||
}
|
}
|
||||||
fpc.move(to: .full, animated: false)
|
fpc.move(to: .full, animated: false)
|
||||||
wait(for: [exp], timeout: 1.0)
|
wait(for: [exp], timeout: timeout)
|
||||||
|
|
||||||
XCTAssertEqual(count, 1)
|
XCTAssertEqual(count, 1)
|
||||||
}
|
}
|
||||||
@@ -237,7 +256,7 @@ class ControllerTests: XCTestCase {
|
|||||||
fpc.move(to: .half, animated: true) {
|
fpc.move(to: .half, animated: true) {
|
||||||
exp.fulfill()
|
exp.fulfill()
|
||||||
}
|
}
|
||||||
wait(for: [exp], timeout: 1.0)
|
wait(for: [exp], timeout: timeout)
|
||||||
|
|
||||||
XCTAssertGreaterThan(count, 1)
|
XCTAssertGreaterThan(count, 1)
|
||||||
}
|
}
|
||||||
@@ -254,7 +273,7 @@ class ControllerTests: XCTestCase {
|
|||||||
exp.fulfill()
|
exp.fulfill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wait(for: [exp], timeout: 1.0)
|
wait(for: [exp], timeout: timeout)
|
||||||
|
|
||||||
XCTAssertEqual(count, 1)
|
XCTAssertEqual(count, 1)
|
||||||
}
|
}
|
||||||
@@ -272,7 +291,7 @@ class ControllerTests: XCTestCase {
|
|||||||
exp.fulfill()
|
exp.fulfill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wait(for: [exp], timeout: 1.0)
|
wait(for: [exp], timeout: timeout)
|
||||||
|
|
||||||
XCTAssertGreaterThan(count, 1)
|
XCTAssertGreaterThan(count, 1)
|
||||||
}
|
}
|
||||||
@@ -314,11 +333,40 @@ class ControllerTests: XCTestCase {
|
|||||||
fpc.move(to: .full, animated: false)
|
fpc.move(to: .full, animated: false)
|
||||||
XCTAssertEqual(fpc.surfaceView.frame.height, fpc.view.bounds.height - fpc.surfaceLocation(for: .full).y)
|
XCTAssertEqual(fpc.surfaceView.frame.height, fpc.view.bounds.height - fpc.surfaceLocation(for: .full).y)
|
||||||
fpc.move(to: .half, animated: false)
|
fpc.move(to: .half, animated: false)
|
||||||
print(1 / fpc.surfaceView.fp_displayScale)
|
|
||||||
XCTAssertEqual(fpc.surfaceView.frame.height, fpc.view.bounds.height - fpc.surfaceLocation(for: .half).y)
|
XCTAssertEqual(fpc.surfaceView.frame.height, fpc.view.bounds.height - fpc.surfaceLocation(for: .half).y)
|
||||||
fpc.move(to: .tip, animated: false)
|
fpc.move(to: .tip, animated: false)
|
||||||
XCTAssertEqual(fpc.surfaceView.frame.height, fpc.view.bounds.height - fpc.surfaceLocation(for: .tip).y)
|
XCTAssertEqual(fpc.surfaceView.frame.height, fpc.view.bounds.height - fpc.surfaceLocation(for: .tip).y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test_switching_layout() {
|
||||||
|
final class FirstLayout: FloatingPanelLayout {
|
||||||
|
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)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
final class SecondLayout: FloatingPanelLayout {
|
||||||
|
let position: FloatingPanelPosition = .bottom
|
||||||
|
let initialState: FloatingPanelState = .half
|
||||||
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 262, edge: .top, referenceGuide: .safeArea)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.layout = FirstLayout()
|
||||||
|
fpc.showForTest()
|
||||||
|
|
||||||
|
fpc.move(to: .tip, animated: false)
|
||||||
|
|
||||||
|
// Switch to another layout
|
||||||
|
fpc.layout = SecondLayout()
|
||||||
|
fpc.invalidateLayout()
|
||||||
|
|
||||||
|
XCTAssertEqual(fpc.state, .half)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MyZombieViewController: UIViewController, FloatingPanelLayout, FloatingPanelBehavior, FloatingPanelControllerDelegate {
|
private class MyZombieViewController: UIViewController, FloatingPanelLayout, FloatingPanelBehavior, FloatingPanelControllerDelegate {
|
||||||
@@ -336,8 +384,7 @@ private class MyZombieViewController: UIViewController, FloatingPanelLayout, Flo
|
|||||||
return .half
|
return .half
|
||||||
}
|
}
|
||||||
|
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: UIScreen.main.bounds.height == 667.0 ? 18.0 : 16.0,
|
.full: FloatingPanelLayoutAnchor(absoluteInset: UIScreen.main.bounds.height == 667.0 ? 18.0 : 16.0,
|
||||||
edge: .top,
|
edge: .top,
|
||||||
referenceGuide: .superview),
|
referenceGuide: .superview),
|
||||||
@@ -347,6 +394,5 @@ private class MyZombieViewController: UIViewController, FloatingPanelLayout, Flo
|
|||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0,
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0,
|
||||||
edge: .bottom,
|
edge: .bottom,
|
||||||
referenceGuide: .superview),
|
referenceGuide: .superview),
|
||||||
]
|
]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+249
-80
@@ -7,7 +7,7 @@ class CoreTests: XCTestCase {
|
|||||||
override func setUp() {}
|
override func setUp() {}
|
||||||
override func tearDown() {}
|
override func tearDown() {}
|
||||||
|
|
||||||
func test_scrolllock() {
|
func test_scrollLock() {
|
||||||
let fpc = FloatingPanelController()
|
let fpc = FloatingPanelController()
|
||||||
|
|
||||||
let contentVC1 = UITableViewController(nibName: nil, bundle: nil)
|
let contentVC1 = UITableViewController(nibName: nil, bundle: nil)
|
||||||
@@ -61,9 +61,8 @@ class CoreTests: XCTestCase {
|
|||||||
class FloatingPanelLayout1Positions: FloatingPanelLayout {
|
class FloatingPanelLayout1Positions: FloatingPanelLayout {
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] =
|
||||||
return [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
|
[.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
@@ -106,12 +105,10 @@ class CoreTests: XCTestCase {
|
|||||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||||
let initialState: FloatingPanelState = .half
|
let initialState: FloatingPanelState = .half
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
@@ -198,51 +195,111 @@ 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 timeout = 3.0
|
||||||
|
|
||||||
|
let delegate = TestDelegate()
|
||||||
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
fpc.layout = BackdropTestLayout()
|
fpc.layout = BackdropTestLayout()
|
||||||
|
|
||||||
fpc.showForTest()
|
fpc.showForTest()
|
||||||
|
|
||||||
fpc.move(to: .full, animated: false)
|
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)
|
fpc.move(to: .half, animated: false)
|
||||||
XCTAssertEqual(fpc.backdropView.alpha, 0.0)
|
XCTAssertEqual(fpc.backdropView.alpha, 0.0)
|
||||||
|
|
||||||
fpc.move(to: .tip, animated: false)
|
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")
|
let exp1 = expectation(description: "move to full with animation")
|
||||||
fpc.move(to: .full, animated: true) {
|
fpc.move(to: .full, animated: true) {
|
||||||
exp1.fulfill()
|
exp1.fulfill()
|
||||||
}
|
}
|
||||||
wait(for: [exp1], timeout: 1.0)
|
wait(for: [exp1], timeout: timeout)
|
||||||
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")
|
let exp2 = expectation(description: "move to half with animation")
|
||||||
fpc.move(to: .half, animated: true) {
|
fpc.move(to: .half, animated: true) {
|
||||||
exp2.fulfill()
|
exp2.fulfill()
|
||||||
}
|
}
|
||||||
wait(for: [exp2], timeout: 1.0)
|
wait(for: [exp2], timeout: timeout)
|
||||||
XCTAssertEqual(fpc.backdropView.alpha, 0.0)
|
XCTAssertEqual(fpc.backdropView.alpha, 0.0)
|
||||||
|
|
||||||
|
// Test a content mode change of FloatingPanelController
|
||||||
|
|
||||||
let exp3 = expectation(description: "move to tip with animation")
|
let exp3 = expectation(description: "move to tip with animation")
|
||||||
fpc.move(to: .tip, animated: true) {
|
fpc.move(to: .tip, animated: true) {
|
||||||
exp3.fulfill()
|
exp3.fulfill()
|
||||||
}
|
}
|
||||||
fpc.contentMode = .fitToBounds
|
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)
|
wait(for: [exp3], timeout: timeout)
|
||||||
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_targetPosition_1positions() {
|
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_targetState_1positions() {
|
||||||
class FloatingPanelLayout1Positions: FloatingPanelLayout {
|
class FloatingPanelLayout1Positions: FloatingPanelLayout {
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring]
|
||||||
return [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
|
= [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
@@ -254,7 +311,7 @@ class CoreTests: XCTestCase {
|
|||||||
let fullPos = fpc.surfaceLocation(for: .full).y
|
let fullPos = fpc.surfaceLocation(for: .full).y
|
||||||
|
|
||||||
fpc.move(to: .full, animated: false)
|
fpc.move(to: .full, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 1000.0), .full), // redirect
|
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 1000.0), .full), // redirect
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
(#line, fullPos, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full), // redirect
|
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full), // redirect
|
||||||
@@ -265,16 +322,14 @@ class CoreTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_targetPosition_2positions() {
|
func test_targetState_2positions() {
|
||||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||||
let initialState: FloatingPanelState = .half
|
let initialState: FloatingPanelState = .half
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
@@ -287,7 +342,7 @@ class CoreTests: XCTestCase {
|
|||||||
let halfPos = fpc.surfaceLocation(for: .half).y
|
let halfPos = fpc.surfaceLocation(for: .half).y
|
||||||
|
|
||||||
fpc.move(to: .full, animated: false)
|
fpc.move(to: .full, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 1000.0), .half), // project to half
|
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 1000.0), .half), // project to half
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
(#line, fullPos, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
||||||
@@ -304,7 +359,7 @@ class CoreTests: XCTestCase {
|
|||||||
(#line, halfPos + 10.0, CGPoint(x: 0.0, y: -1000.0), .full), // project to full
|
(#line, halfPos + 10.0, CGPoint(x: 0.0, y: -1000.0), .full), // project to full
|
||||||
])
|
])
|
||||||
fpc.move(to: .half, animated: false)
|
fpc.move(to: .half, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 1000.0), .half), // project to half
|
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 1000.0), .half), // project to half
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
(#line, fullPos, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
||||||
@@ -322,16 +377,14 @@ class CoreTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_targetPosition_2positionsWithHidden() {
|
func test_targetState_2positionsWithHidden() {
|
||||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||||
let initialState: FloatingPanelState = .hidden
|
let initialState: FloatingPanelState = .hidden
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
@@ -344,7 +397,7 @@ class CoreTests: XCTestCase {
|
|||||||
let hiddenPos = fpc.surfaceLocation(for: .hidden).y
|
let hiddenPos = fpc.surfaceLocation(for: .hidden).y
|
||||||
|
|
||||||
fpc.move(to: .full, animated: false)
|
fpc.move(to: .full, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 1000.0), .hidden), // project to hidden
|
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 1000.0), .hidden), // project to hidden
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
(#line, fullPos, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
||||||
@@ -361,7 +414,7 @@ class CoreTests: XCTestCase {
|
|||||||
(#line, hiddenPos + 10.0, CGPoint(x: 0.0, y: -1000.0), .full), // project to full
|
(#line, hiddenPos + 10.0, CGPoint(x: 0.0, y: -1000.0), .full), // project to full
|
||||||
])
|
])
|
||||||
fpc.move(to: .hidden, animated: false)
|
fpc.move(to: .hidden, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 1000.0), .hidden), // project to hidden
|
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 1000.0), .hidden), // project to hidden
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
(#line, fullPos, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
||||||
@@ -379,7 +432,7 @@ class CoreTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_targetPosition_3positionsFromFull() {
|
func test_targetState_3positionsFromFull() {
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
let fpc = FloatingPanelController(delegate: delegate)
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
fpc.layout = FloatingPanelLayout3Positions()
|
fpc.layout = FloatingPanelLayout3Positions()
|
||||||
@@ -391,7 +444,7 @@ class CoreTests: XCTestCase {
|
|||||||
let tipPos = fpc.surfaceLocation(for: .tip).y
|
let tipPos = fpc.surfaceLocation(for: .tip).y
|
||||||
// From .full
|
// From .full
|
||||||
fpc.move(to: .full, animated: false)
|
fpc.move(to: .full, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: -100.0), .full), // far from topMostState
|
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: -100.0), .full), // far from topMostState
|
||||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 0.0), .full), // far from topMostState
|
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 0.0), .full), // far from topMostState
|
||||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 100.0), .full), // far from topMostState
|
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 100.0), .full), // far from topMostState
|
||||||
@@ -423,7 +476,7 @@ class CoreTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_targetPosition_3positionsFromFull_bottomEdge() {
|
func test_targetState_3positionsFromFull_bottomEdge() {
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
let fpc = FloatingPanelController(delegate: delegate)
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
fpc.layout = FloatingPanelLayout3PositionsBottomEdge()
|
fpc.layout = FloatingPanelLayout3PositionsBottomEdge()
|
||||||
@@ -435,7 +488,7 @@ class CoreTests: XCTestCase {
|
|||||||
let tipPos = fpc.surfaceLocation(for: .tip).y
|
let tipPos = fpc.surfaceLocation(for: .tip).y
|
||||||
// From .full
|
// From .full
|
||||||
fpc.move(to: .full, animated: false)
|
fpc.move(to: .full, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: -100.0), .tip), // far from topMostState
|
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: -100.0), .tip), // far from topMostState
|
||||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 0.0), .tip), // far from topMostState
|
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 0.0), .tip), // far from topMostState
|
||||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 100.0), .tip), // far from topMostState
|
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 100.0), .tip), // far from topMostState
|
||||||
@@ -467,7 +520,7 @@ class CoreTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_targetPosition_3positionsFromHalf() {
|
func test_targetState_3positionsFromHalf() {
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
let fpc = FloatingPanelController(delegate: delegate)
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
fpc.layout = FloatingPanelLayout3Positions()
|
fpc.layout = FloatingPanelLayout3Positions()
|
||||||
@@ -479,7 +532,7 @@ class CoreTests: XCTestCase {
|
|||||||
let tipPos = fpc.surfaceLocation(for: .tip).y
|
let tipPos = fpc.surfaceLocation(for: .tip).y
|
||||||
// From .half
|
// From .half
|
||||||
fpc.move(to: .half, animated: false)
|
fpc.move(to: .half, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: -100.0), .full), // far from topMostState
|
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: -100.0), .full), // far from topMostState
|
||||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 0.0), .full), // far from topMostState
|
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 0.0), .full), // far from topMostState
|
||||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 100.0), .full), // far from topMostState
|
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 100.0), .full), // far from topMostState
|
||||||
@@ -509,7 +562,7 @@ class CoreTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_targetPosition_3positionsFromHalf_bottomEdge() {
|
func test_targetState_3positionsFromHalf_bottomEdge() {
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
let fpc = FloatingPanelController(delegate: delegate)
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
fpc.layout = FloatingPanelLayout3PositionsBottomEdge()
|
fpc.layout = FloatingPanelLayout3PositionsBottomEdge()
|
||||||
@@ -521,7 +574,7 @@ class CoreTests: XCTestCase {
|
|||||||
let tipPos = fpc.surfaceLocation(for: .tip).y
|
let tipPos = fpc.surfaceLocation(for: .tip).y
|
||||||
// From .half
|
// From .half
|
||||||
fpc.move(to: .half, animated: false)
|
fpc.move(to: .half, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: -100.0), .tip), // far from topMostState
|
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: -100.0), .tip), // far from topMostState
|
||||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 0.0), .tip), // far from topMostState
|
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 0.0), .tip), // far from topMostState
|
||||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 100.0), .tip), // far from topMostState
|
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 100.0), .tip), // far from topMostState
|
||||||
@@ -551,7 +604,7 @@ class CoreTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_targetPosition_3positionsFromTip() {
|
func test_targetState_3positionsFromTip() {
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
let fpc = FloatingPanelController(delegate: delegate)
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
fpc.layout = FloatingPanelLayout3Positions()
|
fpc.layout = FloatingPanelLayout3Positions()
|
||||||
@@ -564,7 +617,7 @@ class CoreTests: XCTestCase {
|
|||||||
|
|
||||||
// From .tip
|
// From .tip
|
||||||
fpc.move(to: .tip, animated: false)
|
fpc.move(to: .tip, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: -100.0), .full), // far from topMostState
|
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: -100.0), .full), // far from topMostState
|
||||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 0.0), .full), // far from topMostState
|
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 0.0), .full), // far from topMostState
|
||||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 100.0), .full), // far from topMostState
|
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 100.0), .full), // far from topMostState
|
||||||
@@ -594,7 +647,7 @@ class CoreTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_targetPosition_3positionsFromTip_bottomEdge() {
|
func test_targetState_3positionsFromTip_bottomEdge() {
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
let fpc = FloatingPanelController(delegate: delegate)
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
fpc.layout = FloatingPanelLayout3PositionsBottomEdge()
|
fpc.layout = FloatingPanelLayout3PositionsBottomEdge()
|
||||||
@@ -607,7 +660,7 @@ class CoreTests: XCTestCase {
|
|||||||
|
|
||||||
// From .tip
|
// From .tip
|
||||||
fpc.move(to: .tip, animated: false)
|
fpc.move(to: .tip, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: -100.0), .tip), // far from topMostState
|
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: -100.0), .tip), // far from topMostState
|
||||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 0.0), .tip), // far from topMostState
|
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 0.0), .tip), // far from topMostState
|
||||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 100.0), .tip), // far from topMostState
|
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 100.0), .tip), // far from topMostState
|
||||||
@@ -637,7 +690,7 @@ class CoreTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_targetPosition_3positionsAllProjection() {
|
func test_targetState_3positionsAllProjection() {
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
let fpc = FloatingPanelController(delegate: delegate)
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
fpc.layout = FloatingPanelLayout3Positions()
|
fpc.layout = FloatingPanelLayout3Positions()
|
||||||
@@ -651,7 +704,7 @@ class CoreTests: XCTestCase {
|
|||||||
|
|
||||||
// From .full
|
// From .full
|
||||||
fpc.move(to: .full, animated: false)
|
fpc.move(to: .full, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 3000.0), .tip),
|
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 3000.0), .tip),
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: 1000.0), .tip),
|
(#line, fullPos, CGPoint(x: 0.0, y: 1000.0), .tip),
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: 3000.0), .tip),
|
(#line, fullPos, CGPoint(x: 0.0, y: 3000.0), .tip),
|
||||||
@@ -664,7 +717,7 @@ class CoreTests: XCTestCase {
|
|||||||
|
|
||||||
// From .half
|
// From .half
|
||||||
fpc.move(to: .tip, animated: false)
|
fpc.move(to: .tip, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: 1000.0), .tip),
|
(#line, fullPos, CGPoint(x: 0.0, y: 1000.0), .tip),
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: 3000.0), .tip),
|
(#line, fullPos, CGPoint(x: 0.0, y: 3000.0), .tip),
|
||||||
(#line, tipPos, CGPoint(x: 0.0, y: -3000.0), .full),
|
(#line, tipPos, CGPoint(x: 0.0, y: -3000.0), .full),
|
||||||
@@ -673,7 +726,7 @@ class CoreTests: XCTestCase {
|
|||||||
|
|
||||||
// From .tip
|
// From .tip
|
||||||
fpc.move(to: .tip, animated: false)
|
fpc.move(to: .tip, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 3000.0), .tip),
|
(#line, fullPos - 10.0, CGPoint(x: 0.0, y: 3000.0), .tip),
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: 1000.0), .tip),
|
(#line, fullPos, CGPoint(x: 0.0, y: 1000.0), .tip),
|
||||||
(#line, fullPos, CGPoint(x: 0.0, y: 3000.0), .tip),
|
(#line, fullPos, CGPoint(x: 0.0, y: 3000.0), .tip),
|
||||||
@@ -685,17 +738,15 @@ class CoreTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_targetPosition_3positionsWithHidden() {
|
func test_targetState_3positionsWithHidden() {
|
||||||
class FloatingPanelLayout3PositionsWithHidden: FloatingPanelLayout {
|
class FloatingPanelLayout3PositionsWithHidden: FloatingPanelLayout {
|
||||||
let initialState: FloatingPanelState = .hidden
|
let initialState: FloatingPanelState = .hidden
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
let fpc = FloatingPanelController(delegate: delegate)
|
let fpc = FloatingPanelController(delegate: delegate)
|
||||||
@@ -705,11 +756,11 @@ class CoreTests: XCTestCase {
|
|||||||
XCTAssertEqual(fpc.state, .hidden)
|
XCTAssertEqual(fpc.state, .hidden)
|
||||||
|
|
||||||
fpc.move(to: .full, animated: false)
|
fpc.move(to: .full, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fpc.surfaceView.frame.minY, CGPoint(x: 0.0, y: 1000.0), .half),
|
(#line, fpc.surfaceView.frame.minY, CGPoint(x: 0.0, y: 1000.0), .half),
|
||||||
])
|
])
|
||||||
fpc.move(to: .half, animated: false)
|
fpc.move(to: .half, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, fpc.surfaceView.frame.minY, CGPoint(x: 0.0, y: -100.0), .half),
|
(#line, fpc.surfaceView.frame.minY, CGPoint(x: 0.0, y: -100.0), .half),
|
||||||
(#line, fpc.surfaceView.frame.minY, CGPoint(x: 0.0, y: -1000.0), .full),
|
(#line, fpc.surfaceView.frame.minY, CGPoint(x: 0.0, y: -1000.0), .full),
|
||||||
(#line, fpc.surfaceView.frame.minY, CGPoint(x: 0.0, y: 0.0), .half),
|
(#line, fpc.surfaceView.frame.minY, CGPoint(x: 0.0, y: 0.0), .half),
|
||||||
@@ -717,17 +768,15 @@ class CoreTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_targetPosition_3positionsWithHiddenWithoutFull() {
|
func test_targetState_3positionsWithHiddenWithoutFull() {
|
||||||
class FloatingPanelLayout3Positions: FloatingPanelLayout {
|
class FloatingPanelLayout3Positions: FloatingPanelLayout {
|
||||||
let initialState: FloatingPanelState = .hidden
|
let initialState: FloatingPanelState = .hidden
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
|
||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
|
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let delegate = FloatingPanelTestDelegate()
|
let delegate = FloatingPanelTestDelegate()
|
||||||
@@ -743,7 +792,7 @@ class CoreTests: XCTestCase {
|
|||||||
//let hiddenPos = fpc.surfaceLocation(for: .hidden)
|
//let hiddenPos = fpc.surfaceLocation(for: .hidden)
|
||||||
|
|
||||||
fpc.move(to: .half, animated: false)
|
fpc.move(to: .half, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, halfPos, CGPoint(x: 0.0, y: -100.0), .half),
|
(#line, halfPos, CGPoint(x: 0.0, y: -100.0), .half),
|
||||||
(#line, halfPos, CGPoint(x: 0.0, y: 0.0), .half),
|
(#line, halfPos, CGPoint(x: 0.0, y: 0.0), .half),
|
||||||
(#line, halfPos, CGPoint(x: 0.0, y: 385.0), .tip), // projection
|
(#line, halfPos, CGPoint(x: 0.0, y: 385.0), .tip), // projection
|
||||||
@@ -759,13 +808,133 @@ class CoreTests: XCTestCase {
|
|||||||
(#line, tipPos - 10.0, CGPoint(x: 0.0, y: 10.0), .tip), // redirection
|
(#line, tipPos - 10.0, CGPoint(x: 0.0, y: 10.0), .tip), // redirection
|
||||||
])
|
])
|
||||||
fpc.move(to: .tip, animated: false)
|
fpc.move(to: .tip, animated: false)
|
||||||
assertTargetPosition(fpc.floatingPanel, with: [
|
assertTargetState(fpc.floatingPanel, with: [
|
||||||
(#line, tipPos, CGPoint(x: 0.0, y: -100.0), .tip),
|
(#line, tipPos, CGPoint(x: 0.0, y: -100.0), .tip),
|
||||||
(#line, tipPos, CGPoint(x: 0.0, y: -1000.0), .half),
|
(#line, tipPos, CGPoint(x: 0.0, y: -1000.0), .half),
|
||||||
(#line, tipPos, CGPoint(x: 0.0, y: 0.0), .tip),
|
(#line, tipPos, CGPoint(x: 0.0, y: 0.0), .tip),
|
||||||
(#line, tipPos, CGPoint(x: 0.0, y: 1000.0), .hidden),
|
(#line, tipPos, CGPoint(x: 0.0, y: 1000.0), .hidden),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test_keep_pan_gesture_disabled() {
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.panGestureRecognizer.isEnabled = false
|
||||||
|
fpc.showForTest()
|
||||||
|
XCTAssertFalse(fpc.panGestureRecognizer.isEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_is_scrollable() {
|
||||||
|
class Delegate: FloatingPanelControllerDelegate {
|
||||||
|
var shouldScroll = false
|
||||||
|
func floatingPanel(
|
||||||
|
_ fpc: FloatingPanelController,
|
||||||
|
shouldAllowToScroll scrollView: UIScrollView,
|
||||||
|
in state: FloatingPanelState
|
||||||
|
) -> Bool {
|
||||||
|
return shouldScroll
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
let scrollView = UIScrollView()
|
||||||
|
let delegate = Delegate()
|
||||||
|
fpc.layout = FloatingPanelBottomLayout()
|
||||||
|
fpc.track(scrollView: scrollView)
|
||||||
|
fpc.showForTest()
|
||||||
|
|
||||||
|
XCTAssertTrue(fpc.floatingPanel.isScrollable(state: .full))
|
||||||
|
XCTAssertFalse(fpc.floatingPanel.isScrollable(state: .half))
|
||||||
|
|
||||||
|
fpc.delegate = delegate
|
||||||
|
|
||||||
|
XCTAssertFalse(fpc.floatingPanel.isScrollable(state: .full))
|
||||||
|
XCTAssertFalse(fpc.floatingPanel.isScrollable(state: .half))
|
||||||
|
|
||||||
|
delegate.shouldScroll = true
|
||||||
|
|
||||||
|
XCTAssertTrue(fpc.floatingPanel.isScrollable(state: .full))
|
||||||
|
XCTAssertTrue(fpc.floatingPanel.isScrollable(state: .half))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_adjustScrollContentInsetIfNeeded() {
|
||||||
|
class CustomScrollView: UIScrollView {
|
||||||
|
var customSafeAreaInsets: UIEdgeInsets = .zero
|
||||||
|
override var safeAreaInsets: UIEdgeInsets {
|
||||||
|
customSafeAreaInsets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let scrollView = CustomScrollView()
|
||||||
|
scrollView.customSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 34, right: 0)
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.track(scrollView: scrollView)
|
||||||
|
fpc.layout = FloatingPanelBottomLayout()
|
||||||
|
fpc.contentInsetAdjustmentBehavior = .always
|
||||||
|
fpc.contentMode = .static
|
||||||
|
fpc.showForTest()
|
||||||
|
|
||||||
|
fpc.move(to: .half, animated: false)
|
||||||
|
fpc.floatingPanel.adjustScrollContentInsetIfNeeded()
|
||||||
|
|
||||||
|
let expect = 34 + (fpc.surfaceLocation(for: .half).y - fpc.surfaceLocation(for: .full).y)
|
||||||
|
XCTAssertEqual(
|
||||||
|
scrollView.contentInset,
|
||||||
|
UIEdgeInsets(top: 0, left: 0, bottom: expect, right: 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
fpc.contentMode = .fitToBounds
|
||||||
|
XCTAssertEqual(
|
||||||
|
scrollView.contentInset,
|
||||||
|
scrollView.customSafeAreaInsets
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let scrollView = CustomScrollView()
|
||||||
|
scrollView.customSafeAreaInsets = UIEdgeInsets(top: 91, left: 0, bottom: 0, right: 0)
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.track(scrollView: scrollView)
|
||||||
|
fpc.layout = FloatingPanelTopPositionedLayout()
|
||||||
|
fpc.contentInsetAdjustmentBehavior = .always
|
||||||
|
fpc.contentMode = .static
|
||||||
|
fpc.showForTest()
|
||||||
|
|
||||||
|
fpc.move(to: .half, animated: false)
|
||||||
|
fpc.floatingPanel.adjustScrollContentInsetIfNeeded()
|
||||||
|
|
||||||
|
let expect = 91 + (fpc.surfaceLocation(for: .full).y - fpc.surfaceLocation(for: .half).y)
|
||||||
|
XCTAssertEqual(
|
||||||
|
scrollView.contentInset,
|
||||||
|
UIEdgeInsets(top: expect, left: 0, bottom: 0, right: 0)
|
||||||
|
)
|
||||||
|
fpc.contentMode = .fitToBounds
|
||||||
|
XCTAssertEqual(
|
||||||
|
scrollView.contentInset,
|
||||||
|
scrollView.customSafeAreaInsets
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_handleGesture_endWithoutAttraction() throws {
|
||||||
|
class Delegate: FloatingPanelControllerDelegate {
|
||||||
|
var willAttract: Bool?
|
||||||
|
func floatingPanelDidEndDragging(_ fpc: FloatingPanelController, willAttract attract: Bool) {
|
||||||
|
willAttract = attract
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
let delegate = Delegate()
|
||||||
|
fpc.showForTest()
|
||||||
|
fpc.delegate = delegate
|
||||||
|
XCTAssertEqual(fpc.state, .half)
|
||||||
|
|
||||||
|
fpc.floatingPanel.endWithoutAttraction(.full)
|
||||||
|
|
||||||
|
XCTAssertEqual(fpc.state, .full)
|
||||||
|
XCTAssertEqual(fpc.surfaceLocation(for: .full).y, fpc.surfaceLocation.y)
|
||||||
|
XCTAssertEqual(delegate.willAttract, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FloatingPanelLayout3Positions: FloatingPanelTestLayout {
|
private class FloatingPanelLayout3Positions: FloatingPanelTestLayout {
|
||||||
@@ -781,9 +950,9 @@ private class FloatingPanelLayout3PositionsBottomEdge: FloatingPanelTop2BottomTe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private typealias TestParameter = (UInt, CGFloat, CGPoint, FloatingPanelState)
|
private typealias TestParameter = (UInt, CGFloat, CGPoint, FloatingPanelState)
|
||||||
private func assertTargetPosition(_ floatingPanel: Core, with params: [TestParameter]) {
|
private func assertTargetState(_ floatingPanel: Core, with params: [TestParameter]) {
|
||||||
params.forEach { (line, pos, velocity, result) in
|
params.forEach { (line, pos, velocity, result) in
|
||||||
floatingPanel.surfaceView.frame.origin.y = pos
|
floatingPanel.surfaceView.frame.origin.y = pos
|
||||||
XCTAssertEqual(floatingPanel.targetPosition(from: pos, with: velocity.y), result, line: line)
|
XCTAssertEqual(floatingPanel.targetState(from: pos, with: velocity.y), result, line: line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,20 @@ class ExtensionTests: XCTestCase {
|
|||||||
XCTAssertNotEqual(CGFloat(333.5).rounded(by: 3), 333.66666666666674)
|
XCTAssertNotEqual(CGFloat(333.5).rounded(by: 3), 333.66666666666674)
|
||||||
XCTAssertTrue(CGFloat(333.5).isEqual(to: 333.66666666666674, on: 3.0))
|
XCTAssertTrue(CGFloat(333.5).isEqual(to: 333.66666666666674, on: 3.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test_roundedByDisplayScale_2() {
|
||||||
|
XCTAssertEqual(CGFloat(-0.16666666666674246).rounded(by: 3), 0.0)
|
||||||
|
XCTAssertEqual(CGFloat(0.16666666666674246).rounded(by: 3), 0.0)
|
||||||
|
|
||||||
|
XCTAssertEqual(CGFloat(-0.3333333333374246).rounded(by: 3), -0.3333333333333333)
|
||||||
|
XCTAssertEqual(CGFloat(-0.3333333333074246).rounded(by: 3), -0.3333333333333333)
|
||||||
|
XCTAssertEqual(CGFloat(0.33333333333374246).rounded(by: 3), 0.3333333333333333)
|
||||||
|
XCTAssertEqual(CGFloat(0.33333333333074246).rounded(by: 3), 0.3333333333333333)
|
||||||
|
|
||||||
|
XCTAssertEqual(CGFloat(-0.16666666666674246).rounded(by: 2), 0.0)
|
||||||
|
XCTAssertEqual(CGFloat(0.16666666666674246).rounded(by: 2), 0.0)
|
||||||
|
|
||||||
|
XCTAssertEqual(CGFloat(-0.16666666666674246).rounded(by: 6), -0.16666666666666666)
|
||||||
|
XCTAssertEqual(CGFloat(0.16666666666674246).rounded(by: 6), 0.16666666666666666)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import FloatingPanel
|
||||||
|
|
||||||
|
final class GestureTests: XCTestCase {
|
||||||
|
|
||||||
|
func test_delegateProxy_shouldRecognizeSimultaneouslyWith() throws {
|
||||||
|
class GestureDelegateProxy: NSObject, UIGestureRecognizerDelegate {
|
||||||
|
var callsOfShouldRecognizeSimultaneouslyWith = 0
|
||||||
|
func gestureRecognizer(
|
||||||
|
_ gestureRecognizer: UIGestureRecognizer,
|
||||||
|
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
|
||||||
|
) -> Bool {
|
||||||
|
callsOfShouldRecognizeSimultaneouslyWith += 1
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.showForTest()
|
||||||
|
|
||||||
|
let delegateProxy = GestureDelegateProxy()
|
||||||
|
|
||||||
|
// Set a proxy delegate
|
||||||
|
fpc.panGestureRecognizer.delegateProxy = delegateProxy
|
||||||
|
|
||||||
|
_ = fpc.panGestureRecognizer.delegate!.gestureRecognizer?(
|
||||||
|
UIGestureRecognizer(),
|
||||||
|
shouldRecognizeSimultaneouslyWith: UIGestureRecognizer()
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertEqual(delegateProxy.callsOfShouldRecognizeSimultaneouslyWith, 1)
|
||||||
|
|
||||||
|
// Check whether the default delegate method is called when the proxy delegate doesn't implement it.
|
||||||
|
XCTAssertTrue(
|
||||||
|
fpc.panGestureRecognizer.delegate!.gestureRecognizer!(
|
||||||
|
fpc.panGestureRecognizer,
|
||||||
|
shouldRequireFailureOf: FloatingPanelPanGestureRecognizer()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Clear the proxy delegate
|
||||||
|
fpc.panGestureRecognizer.delegateProxy = nil
|
||||||
|
|
||||||
|
_ = fpc.panGestureRecognizer.delegate!.gestureRecognizer?(
|
||||||
|
UIGestureRecognizer(),
|
||||||
|
shouldRecognizeSimultaneouslyWith: UIGestureRecognizer()
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertEqual(delegateProxy.callsOfShouldRecognizeSimultaneouslyWith, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_delegateProxy_shouldRequireFailureOf() throws {
|
||||||
|
class GestureDelegateProxy: NSObject, UIGestureRecognizerDelegate {
|
||||||
|
var callsOfShouldRequireFailureOf = 0
|
||||||
|
func gestureRecognizer(
|
||||||
|
_ gestureRecognizer: UIGestureRecognizer,
|
||||||
|
shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer
|
||||||
|
) -> Bool {
|
||||||
|
callsOfShouldRequireFailureOf += 1
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.showForTest()
|
||||||
|
|
||||||
|
let delegateProxy = GestureDelegateProxy()
|
||||||
|
|
||||||
|
// Set a proxy delegate
|
||||||
|
fpc.panGestureRecognizer.delegateProxy = delegateProxy
|
||||||
|
|
||||||
|
_ = fpc.panGestureRecognizer.delegate!.gestureRecognizer?(
|
||||||
|
UIGestureRecognizer(),
|
||||||
|
shouldRequireFailureOf: UIGestureRecognizer()
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertEqual(delegateProxy.callsOfShouldRequireFailureOf, 1)
|
||||||
|
|
||||||
|
// Clear the proxy delegate
|
||||||
|
fpc.panGestureRecognizer.delegateProxy = nil
|
||||||
|
|
||||||
|
_ = fpc.panGestureRecognizer.delegate!.gestureRecognizer?(
|
||||||
|
UIGestureRecognizer(),
|
||||||
|
shouldRequireFailureOf: UIGestureRecognizer()
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertEqual(delegateProxy.callsOfShouldRequireFailureOf, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_delegateProxy_shouldBeRequiredToFailBy() throws {
|
||||||
|
class GestureDelegateProxy: NSObject, UIGestureRecognizerDelegate {
|
||||||
|
var callsOfShouldBeRequiredToFailBy = 0
|
||||||
|
func gestureRecognizer(
|
||||||
|
_ gestureRecognizer: UIGestureRecognizer,
|
||||||
|
shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer
|
||||||
|
) -> Bool {
|
||||||
|
callsOfShouldBeRequiredToFailBy += 1
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.showForTest()
|
||||||
|
|
||||||
|
let delegateProxy = GestureDelegateProxy()
|
||||||
|
|
||||||
|
fpc.panGestureRecognizer.delegateProxy = delegateProxy
|
||||||
|
|
||||||
|
_ = fpc.panGestureRecognizer.delegate!.gestureRecognizer?(
|
||||||
|
UIGestureRecognizer(),
|
||||||
|
shouldBeRequiredToFailBy: UIGestureRecognizer()
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertEqual(delegateProxy.callsOfShouldBeRequiredToFailBy, 1)
|
||||||
|
|
||||||
|
// Check whether the delegate method of the "proxy" object is called.
|
||||||
|
let otherPanGesture = UIPanGestureRecognizer()
|
||||||
|
otherPanGesture.name = "_UISheetInteractionBackgroundDismissRecognizer"
|
||||||
|
XCTAssertFalse(
|
||||||
|
fpc.panGestureRecognizer.delegate!.gestureRecognizer!(
|
||||||
|
fpc.panGestureRecognizer,
|
||||||
|
shouldBeRequiredToFailBy: otherPanGesture
|
||||||
|
)
|
||||||
|
)
|
||||||
|
XCTAssertEqual(delegateProxy.callsOfShouldBeRequiredToFailBy, 2)
|
||||||
|
|
||||||
|
fpc.panGestureRecognizer.delegateProxy = nil
|
||||||
|
|
||||||
|
// Check whether the delegate method of the "default" object is called.
|
||||||
|
let otherPanGesture2 = UIPanGestureRecognizer()
|
||||||
|
otherPanGesture2.name = "_UISheetInteractionBackgroundDismissRecognizer"
|
||||||
|
XCTAssertTrue(
|
||||||
|
fpc.panGestureRecognizer.delegate!.gestureRecognizer!(
|
||||||
|
fpc.panGestureRecognizer,
|
||||||
|
shouldBeRequiredToFailBy: otherPanGesture2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
XCTAssertEqual(delegateProxy.callsOfShouldBeRequiredToFailBy, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
+105
-90
@@ -13,37 +13,33 @@ class LayoutTests: XCTestCase {
|
|||||||
override func tearDown() {}
|
override func tearDown() {}
|
||||||
|
|
||||||
func test_layoutAdapter_topAndBottomMostState() {
|
func test_layoutAdapter_topAndBottomMostState() {
|
||||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeMostState, .full)
|
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.mostExpandedState, .full)
|
||||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeLeastState, .tip)
|
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.leastExpandedState, .tip)
|
||||||
|
|
||||||
class FloatingPanelLayoutWithHidden: FloatingPanelLayout {
|
class FloatingPanelLayoutWithHidden: FloatingPanelLayout {
|
||||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .top, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .top, referenceGuide: .safeArea),
|
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview)
|
||||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview)
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
let initialState: FloatingPanelState = .hidden
|
let initialState: FloatingPanelState = .hidden
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
}
|
}
|
||||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
let initialState: FloatingPanelState = .tip
|
let initialState: FloatingPanelState = .tip
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
}
|
}
|
||||||
fpc.layout = FloatingPanelLayoutWithHidden()
|
fpc.layout = FloatingPanelLayoutWithHidden()
|
||||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeMostState, .full)
|
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.mostExpandedState, .full)
|
||||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeLeastState, .hidden)
|
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.leastExpandedState, .hidden)
|
||||||
|
|
||||||
fpc.layout = FloatingPanelLayout2Positions()
|
fpc.layout = FloatingPanelLayout2Positions()
|
||||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeMostState, .half)
|
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.mostExpandedState, .half)
|
||||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeLeastState, .tip)
|
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.leastExpandedState, .tip)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_layoutSegment_3position() {
|
func test_layoutSegment_3position() {
|
||||||
@@ -138,31 +134,31 @@ class LayoutTests: XCTestCase {
|
|||||||
var next: CGFloat
|
var next: CGFloat
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||||
overflow: false,
|
scrollingContent: true,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, fullPos)
|
XCTAssertEqual(next, fullPos)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, fullPos + 100.0)
|
XCTAssertEqual(next, fullPos + 100.0)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, tipPos)
|
XCTAssertEqual(next, tipPos)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
|
||||||
overflow: false,
|
scrollingContent: true,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, tipPos)
|
XCTAssertEqual(next, tipPos)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, tipPos + 100.0)
|
XCTAssertEqual(next, tipPos + 100.0)
|
||||||
@@ -190,31 +186,31 @@ class LayoutTests: XCTestCase {
|
|||||||
var next: CGFloat
|
var next: CGFloat
|
||||||
pre = fpc.surfaceLocation.y
|
pre = fpc.surfaceLocation.y
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||||
overflow: false,
|
scrollingContent: true,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, pre)
|
XCTAssertEqual(next, pre)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, tipPos + 100.0)
|
XCTAssertEqual(next, tipPos + 100.0)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, fullPos)
|
XCTAssertEqual(next, fullPos)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos + 100,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos + 100,
|
||||||
overflow: false,
|
scrollingContent: true,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, fullPos)
|
XCTAssertEqual(next, fullPos)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos + 100,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos + 100,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, fullPos + 100.0)
|
XCTAssertEqual(next, fullPos + 100.0)
|
||||||
@@ -224,12 +220,10 @@ class LayoutTests: XCTestCase {
|
|||||||
|
|
||||||
func test_updateInteractiveEdgeConstraintWithHidden() {
|
func test_updateInteractiveEdgeConstraintWithHidden() {
|
||||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||||
return [
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
|
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
let initialState: FloatingPanelState = .hidden
|
let initialState: FloatingPanelState = .hidden
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
}
|
}
|
||||||
@@ -245,19 +239,19 @@ class LayoutTests: XCTestCase {
|
|||||||
var next: CGFloat
|
var next: CGFloat
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||||
overflow: false,
|
scrollingContent: true,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, fullPos)
|
XCTAssertEqual(next, fullPos)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, fullPos - 100.0)
|
XCTAssertEqual(next, fullPos - 100.0)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: hiddenPos - fullPos + 100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: hiddenPos - fullPos + 100.0,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, hiddenPos + 100.0)
|
XCTAssertEqual(next, hiddenPos + 100.0)
|
||||||
@@ -267,12 +261,10 @@ class LayoutTests: XCTestCase {
|
|||||||
|
|
||||||
func test_updateInteractiveEdgeConstraintWithHidden_bottomEdge() {
|
func test_updateInteractiveEdgeConstraintWithHidden_bottomEdge() {
|
||||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||||
[
|
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
|
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .top, referenceGuide: .superview),
|
||||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .top, referenceGuide: .superview),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
let initialState: FloatingPanelState = .hidden
|
let initialState: FloatingPanelState = .hidden
|
||||||
let position: FloatingPanelPosition = .top
|
let position: FloatingPanelPosition = .top
|
||||||
}
|
}
|
||||||
@@ -288,19 +280,19 @@ class LayoutTests: XCTestCase {
|
|||||||
var next: CGFloat
|
var next: CGFloat
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
||||||
overflow: false,
|
scrollingContent: true,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, fullPos)
|
XCTAssertEqual(next, fullPos)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, fullPos + 100.0)
|
XCTAssertEqual(next, fullPos + 100.0)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: hiddenPos - fullPos + 100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: hiddenPos - fullPos + 100.0,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, hiddenPos + 100.0)
|
XCTAssertEqual(next, hiddenPos + 100.0)
|
||||||
@@ -310,12 +302,10 @@ class LayoutTests: XCTestCase {
|
|||||||
|
|
||||||
func test_updateInteractiveTopConstraintWithMinusInsets() {
|
func test_updateInteractiveTopConstraintWithMinusInsets() {
|
||||||
class FloatingPanelLayoutMinusInsets: FloatingPanelLayout {
|
class FloatingPanelLayoutMinusInsets: FloatingPanelLayout {
|
||||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||||
[
|
.full: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .top, referenceGuide: .safeArea),
|
||||||
.full: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .top, referenceGuide: .safeArea),
|
.tip: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .bottom, referenceGuide: .safeArea),
|
||||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .bottom, referenceGuide: .safeArea),
|
]
|
||||||
]
|
|
||||||
}
|
|
||||||
let initialState: FloatingPanelState = .full
|
let initialState: FloatingPanelState = .full
|
||||||
let position: FloatingPanelPosition = .bottom
|
let position: FloatingPanelPosition = .bottom
|
||||||
}
|
}
|
||||||
@@ -328,19 +318,19 @@ class LayoutTests: XCTestCase {
|
|||||||
|
|
||||||
var next: CGFloat
|
var next: CGFloat
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||||
overflow: false,
|
scrollingContent: true,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, fullPos)
|
XCTAssertEqual(next, fullPos)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, fullPos - 100)
|
XCTAssertEqual(next, fullPos - 100)
|
||||||
|
|
||||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
|
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
|
||||||
overflow: true,
|
scrollingContent: false,
|
||||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||||
next = fpc.surfaceLocation.y
|
next = fpc.surfaceLocation.y
|
||||||
XCTAssertEqual(next, tipPos + 100)
|
XCTAssertEqual(next, tipPos + 100)
|
||||||
@@ -421,18 +411,18 @@ class LayoutTests: XCTestCase {
|
|||||||
for prop in [
|
for prop in [
|
||||||
// from top edge
|
// from top edge
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .top, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .top, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 0.0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.topAnchor)),
|
result: (#line, constant: 0.0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.topAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 100.0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.topAnchor)),
|
result: (#line, constant: 100.0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.topAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .top, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .top, referenceGuide: .superview),
|
||||||
result: (#line, constant: 0.0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
result: (#line, constant: 0.0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .superview),
|
||||||
result: (#line, constant: 100.0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
result: (#line, constant: 100.0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||||
// from bottom edge
|
// from bottom edge
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .bottom, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 0.0, firstAnchor: fpc.fp_safeAreaLayoutGuide.bottomAnchor, secondAnchor: fpc.surfaceView.bottomAnchor)),
|
result: (#line, constant: 0.0, firstAnchor: fpc.view.safeAreaLayoutGuide.bottomAnchor, secondAnchor: fpc.surfaceView.bottomAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .bottom, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 100.0, firstAnchor: fpc.fp_safeAreaLayoutGuide.bottomAnchor, secondAnchor: fpc.surfaceView.bottomAnchor)),
|
result: (#line, constant: 100.0, firstAnchor: fpc.view.safeAreaLayoutGuide.bottomAnchor, secondAnchor: fpc.surfaceView.bottomAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .bottom, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .bottom, referenceGuide: .superview),
|
||||||
result: (#line, constant: 0.0, firstAnchor: fpc.view.bottomAnchor, secondAnchor: fpc.surfaceView.bottomAnchor)),
|
result: (#line, constant: 0.0, firstAnchor: fpc.view.bottomAnchor, secondAnchor: fpc.surfaceView.bottomAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .bottom, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .bottom, referenceGuide: .superview),
|
||||||
@@ -450,7 +440,7 @@ class LayoutTests: XCTestCase {
|
|||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .top, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .top, referenceGuide: .safeArea),
|
||||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .top, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .top, referenceGuide: .safeArea),
|
||||||
result: (#line, multiplier: 0.5, secondAnchor: fpc.fp_safeAreaLayoutGuide.heightAnchor)),
|
result: (#line, multiplier: 0.5, secondAnchor: fpc.view.safeAreaLayoutGuide.heightAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .top, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .top, referenceGuide: .superview),
|
||||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .top, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .top, referenceGuide: .superview),
|
||||||
@@ -460,7 +450,7 @@ class LayoutTests: XCTestCase {
|
|||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .bottom, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||||
result: (#line, multiplier: 0.5, secondAnchor: fpc.fp_safeAreaLayoutGuide.heightAnchor)),
|
result: (#line, multiplier: 0.5, secondAnchor: fpc.view.safeAreaLayoutGuide.heightAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .bottom, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .bottom, referenceGuide: .superview),
|
||||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
|
||||||
@@ -469,12 +459,7 @@ class LayoutTests: XCTestCase {
|
|||||||
let c = prop.anchor.layoutConstraints(fpc, for: position)[0]
|
let c = prop.anchor.layoutConstraints(fpc, for: position)[0]
|
||||||
XCTAssertEqual(c.multiplier, CGFloat(prop.result.multiplier), line: UInt(prop.result.0))
|
XCTAssertEqual(c.multiplier, CGFloat(prop.result.multiplier), line: UInt(prop.result.0))
|
||||||
XCTAssertTrue(c.firstAnchor is NSLayoutAnchor<NSLayoutDimension>, line: UInt(prop.result.0))
|
XCTAssertTrue(c.firstAnchor is NSLayoutAnchor<NSLayoutDimension>, line: UInt(prop.result.0))
|
||||||
// On iOS 10, `c.secondAnchor` can't be equal object to `prop.result.secondAnchor`
|
XCTAssertEqual(c.secondAnchor, prop.result.secondAnchor, line: UInt(prop.result.0))
|
||||||
// because there is no safe area on iOS 10 and `fp_safeAreaLayoutGuide` emulates it.
|
|
||||||
if #available(iOS 11, *) {
|
|
||||||
XCTAssertEqual(c.secondAnchor, prop.result.secondAnchor, line: UInt(prop.result.0))
|
|
||||||
}
|
|
||||||
print(c)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func test_layoutAnchor_bottomPosition() {
|
func test_layoutAnchor_bottomPosition() {
|
||||||
@@ -487,9 +472,9 @@ class LayoutTests: XCTestCase {
|
|||||||
for prop in [
|
for prop in [
|
||||||
// from top edge
|
// from top edge
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .top, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .top, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 0.0, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.topAnchor)),
|
result: (#line, constant: 0.0, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.topAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 100.0, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.topAnchor)),
|
result: (#line, constant: 100.0, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.topAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .top, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .top, referenceGuide: .superview),
|
||||||
result: (#line, constant: 0.0, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.topAnchor)),
|
result: (#line, constant: 0.0, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .superview),
|
||||||
@@ -497,9 +482,9 @@ class LayoutTests: XCTestCase {
|
|||||||
|
|
||||||
// from bottom edge
|
// from bottom edge
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .bottom, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 0.0, firstAnchor: fpc.fp_safeAreaLayoutGuide.bottomAnchor, secondAnchor: fpc.surfaceView.topAnchor)),
|
result: (#line, constant: 0.0, firstAnchor: fpc.view.safeAreaLayoutGuide.bottomAnchor, secondAnchor: fpc.surfaceView.topAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .bottom, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 100.0, firstAnchor: fpc.fp_safeAreaLayoutGuide.bottomAnchor, secondAnchor: fpc.surfaceView.topAnchor)),
|
result: (#line, constant: 100.0, firstAnchor: fpc.view.safeAreaLayoutGuide.bottomAnchor, secondAnchor: fpc.surfaceView.topAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .bottom, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .bottom, referenceGuide: .superview),
|
||||||
result: (#line, constant: 0.0, firstAnchor: fpc.view.bottomAnchor, secondAnchor: fpc.surfaceView.topAnchor)),
|
result: (#line, constant: 0.0, firstAnchor: fpc.view.bottomAnchor, secondAnchor: fpc.surfaceView.topAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .bottom, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .bottom, referenceGuide: .superview),
|
||||||
@@ -517,7 +502,7 @@ class LayoutTests: XCTestCase {
|
|||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .top, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .top, referenceGuide: .safeArea),
|
||||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .top, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .top, referenceGuide: .safeArea),
|
||||||
result: (#line, multiplier: 0.5, secondAnchor: fpc.fp_safeAreaLayoutGuide.heightAnchor)),
|
result: (#line, multiplier: 0.5, secondAnchor: fpc.view.safeAreaLayoutGuide.heightAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .top, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .top, referenceGuide: .superview),
|
||||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .top, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .top, referenceGuide: .superview),
|
||||||
@@ -527,7 +512,7 @@ class LayoutTests: XCTestCase {
|
|||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .bottom, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .bottom, referenceGuide: .safeArea),
|
||||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||||
result: (#line, multiplier: 0.5, secondAnchor: fpc.fp_safeAreaLayoutGuide.heightAnchor)),
|
result: (#line, multiplier: 0.5, secondAnchor: fpc.view.safeAreaLayoutGuide.heightAnchor)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .bottom, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.0, edge: .bottom, referenceGuide: .superview),
|
||||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||||
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
|
(anchor: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
|
||||||
@@ -536,12 +521,7 @@ class LayoutTests: XCTestCase {
|
|||||||
let c = prop.anchor.layoutConstraints(fpc, for: position)[0]
|
let c = prop.anchor.layoutConstraints(fpc, for: position)[0]
|
||||||
XCTAssertEqual(c.multiplier, CGFloat(prop.result.multiplier), line: UInt(prop.result.0))
|
XCTAssertEqual(c.multiplier, CGFloat(prop.result.multiplier), line: UInt(prop.result.0))
|
||||||
XCTAssertTrue(c.firstAnchor is NSLayoutAnchor<NSLayoutDimension>, line: UInt(prop.result.0))
|
XCTAssertTrue(c.firstAnchor is NSLayoutAnchor<NSLayoutDimension>, line: UInt(prop.result.0))
|
||||||
// On iOS 10, `c.secondAnchor` can't be equal object to `prop.result.secondAnchor`
|
XCTAssertEqual(c.secondAnchor, prop.result.secondAnchor, line: UInt(prop.result.0))
|
||||||
// because there is no safe area on iOS 10 and `fp_safeAreaLayoutGuide` emulates it.
|
|
||||||
if #available(iOS 11, *) {
|
|
||||||
XCTAssertEqual(c.secondAnchor, prop.result.secondAnchor, line: UInt(prop.result.0))
|
|
||||||
}
|
|
||||||
print(c)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,20 +547,20 @@ class LayoutTests: XCTestCase {
|
|||||||
|
|
||||||
for prop in [
|
for prop in [
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .safeArea),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 420, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.topAnchor)),
|
result: (#line, constant: 420, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.topAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 42.0, referenceGuide: .safeArea),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 42.0, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 420 - 42, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.topAnchor)),
|
result: (#line, constant: 420 - 42, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.topAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .superview),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .superview),
|
||||||
result: (#line, constant: 420, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
result: (#line, constant: 420, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 42.0, referenceGuide: .superview),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 42.0, referenceGuide: .superview),
|
||||||
result: (#line, constant: 420 - 42, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
result: (#line, constant: 420 - 42, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||||
|
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 420, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.topAnchor)),
|
result: (#line, constant: 420, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.topAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 210, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.topAnchor)),
|
result: (#line, constant: 210, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.topAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 1.0, referenceGuide: .safeArea),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 1.0, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.topAnchor)),
|
result: (#line, constant: 0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.topAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .superview),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .superview),
|
||||||
result: (#line, constant: 420, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
result: (#line, constant: 420, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .superview),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .superview),
|
||||||
@@ -617,20 +597,20 @@ class LayoutTests: XCTestCase {
|
|||||||
|
|
||||||
for prop in [
|
for prop in [
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .safeArea),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: -420, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.bottomAnchor)),
|
result: (#line, constant: -420, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.bottomAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 42.0, referenceGuide: .safeArea),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 42.0, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: -420 + 42, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.bottomAnchor)),
|
result: (#line, constant: -420 + 42, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.bottomAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .superview),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .superview),
|
||||||
result: (#line, constant: -420, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.bottomAnchor)),
|
result: (#line, constant: -420, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.bottomAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 42.0, referenceGuide: .superview),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 42.0, referenceGuide: .superview),
|
||||||
result: (#line, constant: -420 + 42, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.bottomAnchor)),
|
result: (#line, constant: -420 + 42, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.bottomAnchor)),
|
||||||
|
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: -420, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.bottomAnchor)),
|
result: (#line, constant: -420, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.bottomAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: -210, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.bottomAnchor)),
|
result: (#line, constant: -210, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.bottomAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 1.0, referenceGuide: .safeArea),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 1.0, referenceGuide: .safeArea),
|
||||||
result: (#line, constant: 0, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.fp_safeAreaLayoutGuide.bottomAnchor)),
|
result: (#line, constant: 0, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.safeAreaLayoutGuide.bottomAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .superview),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .superview),
|
||||||
result: (#line, constant: -420, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.bottomAnchor)),
|
result: (#line, constant: -420, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.bottomAnchor)),
|
||||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .superview),
|
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .superview),
|
||||||
@@ -644,6 +624,41 @@ class LayoutTests: XCTestCase {
|
|||||||
XCTAssertEqual(c.secondAnchor, prop.result.secondAnchor, line: UInt(prop.result.0))
|
XCTAssertEqual(c.secondAnchor, prop.result.secondAnchor, line: UInt(prop.result.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test_offsetFromMostExpandedAnchor() {
|
||||||
|
do {
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.layout = FloatingPanelBottomLayout()
|
||||||
|
fpc.showForTest()
|
||||||
|
|
||||||
|
fpc.move(to: .full, animated: false)
|
||||||
|
|
||||||
|
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.offsetFromMostExpandedAnchor, 0.0)
|
||||||
|
|
||||||
|
fpc.move(to: .half, animated: false)
|
||||||
|
|
||||||
|
XCTAssertEqual(
|
||||||
|
fpc.floatingPanel.layoutAdapter.offsetFromMostExpandedAnchor,
|
||||||
|
(fpc.surfaceLocation(for: .half) - fpc.surfaceLocation(for: .full)).y
|
||||||
|
)
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
let fpc = FloatingPanelController()
|
||||||
|
fpc.layout = FloatingPanelTopPositionedLayout()
|
||||||
|
fpc.showForTest()
|
||||||
|
|
||||||
|
fpc.move(to: .full, animated: false)
|
||||||
|
|
||||||
|
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.offsetFromMostExpandedAnchor, 0.0)
|
||||||
|
|
||||||
|
fpc.move(to: .half, animated: false)
|
||||||
|
|
||||||
|
XCTAssertEqual(
|
||||||
|
fpc.floatingPanel.layoutAdapter.offsetFromMostExpandedAnchor,
|
||||||
|
-(fpc.surfaceLocation(for: .half) - fpc.surfaceLocation(for: .full)).y
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private typealias LayoutSegmentTestParameter = (UInt, pos: CGFloat, forwardY: Bool, lower: FloatingPanelState?, upper: FloatingPanelState?)
|
private typealias LayoutSegmentTestParameter = (UInt, pos: CGFloat, forwardY: Bool, lower: FloatingPanelState?, upper: FloatingPanelState?)
|
||||||
|
|||||||
+132
-47
@@ -48,35 +48,37 @@ class SurfaceViewTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_surfaceView_contentView() {
|
func test_surfaceView_contentView() {
|
||||||
XCTContext.runActivity(named: "Bottom sheet") { _ in
|
for (position, mode, line) in [
|
||||||
|
(.top, .static, #line),
|
||||||
|
(.top, .fitToBounds, #line),
|
||||||
|
(.bottom, .static, #line),
|
||||||
|
(.bottom, .fitToBounds, #line),
|
||||||
|
] as [(FloatingPanelPosition, FloatingPanelController.ContentMode, UInt)] {
|
||||||
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
||||||
|
surface.position = position
|
||||||
surface.layoutIfNeeded()
|
surface.layoutIfNeeded()
|
||||||
|
|
||||||
let contentView = UIView()
|
let contentView = UIView()
|
||||||
surface.set(contentView: contentView)
|
surface.set(contentView: contentView, mode: mode)
|
||||||
|
|
||||||
let height = surface.bounds.height * 2
|
let height = surface.bounds.height * 2
|
||||||
surface.containerOverflow = height
|
surface.containerOverflow = height
|
||||||
surface.setNeedsLayout()
|
surface.setNeedsLayout()
|
||||||
surface.layoutIfNeeded()
|
surface.layoutIfNeeded()
|
||||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds)
|
|
||||||
}
|
|
||||||
|
|
||||||
XCTContext.runActivity(named: "Top sheet") { _ in
|
switch position {
|
||||||
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
case .top:
|
||||||
surface.position = .top
|
XCTAssertEqual(surface.containerView.frame,
|
||||||
surface.layoutIfNeeded()
|
CGRect(x: 0.0, y: -height, width: 320.0, height: 480.0 * 3),
|
||||||
|
line: line)
|
||||||
let contentView = UIView()
|
XCTAssertEqual(surface.convert(surface.contentView?.frame ?? .zero, from: surface.containerView),
|
||||||
surface.set(contentView: contentView)
|
surface.bounds,
|
||||||
|
line: line)
|
||||||
let height = surface.bounds.height * 2
|
case .bottom:
|
||||||
surface.containerOverflow = height
|
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds, line: line)
|
||||||
surface.setNeedsLayout()
|
default:
|
||||||
surface.layoutIfNeeded()
|
break
|
||||||
XCTAssertEqual(surface.containerView.frame, CGRect(x: 0.0, y: -height, width: 320.0, height: 480.0 * 3))
|
}
|
||||||
XCTAssertEqual(surface.convert(surface.contentView?.frame ?? .zero, from: surface.containerView),
|
|
||||||
surface.bounds)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,46 +142,45 @@ class SurfaceViewTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_surfaceView_contentInsets() {
|
func test_surfaceView_contentInsets() {
|
||||||
XCTContext.runActivity(named: "Top sheet") { _ in
|
for (position, mode, line) in [
|
||||||
|
(.top, .static, #line),
|
||||||
|
(.top, .fitToBounds, #line),
|
||||||
|
(.bottom, .static, #line),
|
||||||
|
(.bottom, .fitToBounds, #line),
|
||||||
|
] as [(FloatingPanelPosition, FloatingPanelController.ContentMode, UInt)] {
|
||||||
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
||||||
surface.position = .top
|
surface.position = position
|
||||||
let contentView = UIView()
|
let contentView = UIView()
|
||||||
surface.set(contentView: contentView)
|
surface.set(contentView: contentView, mode: mode)
|
||||||
surface.layoutIfNeeded()
|
surface.layoutIfNeeded()
|
||||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds)
|
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds, line: line)
|
||||||
surface.contentPadding = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0)
|
surface.contentPadding = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0)
|
||||||
surface.setNeedsLayout()
|
surface.setNeedsLayout()
|
||||||
surface.layoutIfNeeded()
|
surface.layoutIfNeeded()
|
||||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds.inset(by: surface.contentPadding))
|
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds.inset(by: surface.contentPadding), line: line)
|
||||||
}
|
|
||||||
XCTContext.runActivity(named: "Bottom sheet") { _ in
|
|
||||||
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
|
||||||
let contentView = UIView()
|
|
||||||
surface.set(contentView: contentView)
|
|
||||||
surface.layoutIfNeeded()
|
|
||||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds)
|
|
||||||
surface.contentPadding = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0)
|
|
||||||
surface.setNeedsLayout()
|
|
||||||
surface.layoutIfNeeded()
|
|
||||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds.inset(by: surface.contentPadding))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_surfaceView_containerMargins_and_contentInsets() {
|
func test_surfaceView_containerMargins_and_contentInsets() {
|
||||||
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
for (mode, line) in [
|
||||||
let contentView = UIView()
|
(.static, #line),
|
||||||
surface.set(contentView: contentView)
|
(.fitToBounds, #line),
|
||||||
surface.layoutIfNeeded()
|
] as [(FloatingPanelController.ContentMode, UInt)] {
|
||||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds)
|
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
||||||
surface.containerMargins = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0)
|
let contentView = UIView()
|
||||||
surface.contentPadding = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0)
|
surface.set(contentView: contentView, mode: mode)
|
||||||
surface.setNeedsLayout()
|
surface.layoutIfNeeded()
|
||||||
surface.layoutIfNeeded()
|
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds, line: line)
|
||||||
XCTAssertEqual(surface.containerView.frame, surface.bounds.inset(by: surface.containerMargins))
|
surface.containerMargins = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0)
|
||||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.containerView.bounds.inset(by: surface.contentPadding))
|
surface.contentPadding = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0)
|
||||||
|
surface.setNeedsLayout()
|
||||||
|
surface.layoutIfNeeded()
|
||||||
|
XCTAssertEqual(surface.containerView.frame, surface.bounds.inset(by: surface.containerMargins), line: line)
|
||||||
|
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.containerView.bounds.inset(by: surface.contentPadding), line: line)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_surfaceView_cornderRaduis() {
|
func test_surfaceView_cornerRadius() {
|
||||||
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
||||||
XCTAssert(surface.containerView.layer.cornerRadius == 0.0)
|
XCTAssert(surface.containerView.layer.cornerRadius == 0.0)
|
||||||
XCTAssert(surface.containerView.layer.masksToBounds == false)
|
XCTAssert(surface.containerView.layer.masksToBounds == false)
|
||||||
@@ -227,4 +228,88 @@ class SurfaceViewTests: XCTestCase {
|
|||||||
XCTAssert(surface.containerView.layer.borderColor == UIColor.red.cgColor)
|
XCTAssert(surface.containerView.layer.borderColor == UIColor.red.cgColor)
|
||||||
XCTAssert(surface.containerView.layer.borderWidth == 3.0)
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ extension FloatingPanelController {
|
|||||||
class FloatingPanelTestDelegate: FloatingPanelControllerDelegate {
|
class FloatingPanelTestDelegate: FloatingPanelControllerDelegate {
|
||||||
var position: FloatingPanelState = .hidden
|
var position: FloatingPanelState = .hidden
|
||||||
var didMoveCallback: ((FloatingPanelController) -> Void)?
|
var didMoveCallback: ((FloatingPanelController) -> Void)?
|
||||||
func floatingPanelDidChangePosition(_ vc: FloatingPanelController) {
|
func floatingPanelDidChangeState(_ vc: FloatingPanelController) {
|
||||||
position = vc.state
|
position = vc.state
|
||||||
}
|
}
|
||||||
func floatingPanelDidMove(_ vc: FloatingPanelController) {
|
func floatingPanelDidMove(_ vc: FloatingPanelController) {
|
||||||
@@ -72,8 +72,40 @@ class FloatingPanelTop2BottomTestLayout: FloatingPanelLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FloatingPanelTopPositionedLayout: FloatingPanelLayout {
|
||||||
|
let position: FloatingPanelPosition = .top
|
||||||
|
let initialState: FloatingPanelState = .full
|
||||||
|
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 FloatingPanelProjectableBehavior: FloatingPanelBehavior {
|
class FloatingPanelProjectableBehavior: FloatingPanelBehavior {
|
||||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedTargetPosition: FloatingPanelState) -> Bool {
|
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedState: FloatingPanelState) -> Bool {
|
||||||
return true
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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>
|
|
||||||
@@ -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>
|
|
||||||
Reference in New Issue
Block a user