Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
@@ -0,0 +1,21 @@
|
||||
version: 2.1
|
||||
|
||||
jobs:
|
||||
test-ios14_5-iPhone_12_Pro:
|
||||
macos:
|
||||
xcode: 13.4.1
|
||||
steps:
|
||||
- checkout
|
||||
- run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=14.5,name=iPhone 12 Pro'
|
||||
test-ios13_7-iPhone_11_Pro:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
steps:
|
||||
- checkout
|
||||
- run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=13.7,name=iPhone 11 Pro'
|
||||
|
||||
workflows:
|
||||
test:
|
||||
jobs:
|
||||
- test-ios14_5-iPhone_12_Pro
|
||||
- test-ios13_7-iPhone_11_Pro
|
||||
+86
-69
@@ -4,100 +4,117 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macOS-11
|
||||
runs-on: ${{ matrix.runsOn }}
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- swift: "5.8"
|
||||
xcode: "14.3"
|
||||
runsOn: macos-13
|
||||
- swift: "5.7"
|
||||
xcode: "14.1"
|
||||
runsOn: macos-12
|
||||
- swift: "5.6"
|
||||
xcode: "13.4.1"
|
||||
runsOn: macos-12
|
||||
- swift: "5.5"
|
||||
xcode: "13.2.1"
|
||||
runsOn: macos-11
|
||||
- swift: "5.4"
|
||||
xcode: "12.5.1"
|
||||
runsOn: macos-11
|
||||
- swift: "5.3"
|
||||
xcode: "12.4"
|
||||
runsOn: macos-11
|
||||
- swift: "5.2"
|
||||
xcode: "11.7"
|
||||
runsOn: macos-11
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "Swift 5.4"
|
||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.4 clean build
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_12.5.1.app/Contents/Developer
|
||||
- name: "Swift 5.5"
|
||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.5 clean build
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer
|
||||
- uses: actions/checkout@v3
|
||||
- name: Building in Swift ${{ matrix.swift }}
|
||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=${{ matrix.swift }} clean build
|
||||
|
||||
build_compat:
|
||||
runs-on: macOS-10.15
|
||||
test:
|
||||
runs-on: ${{ matrix.runsOn }}
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: "16.4"
|
||||
xcode: "14.3.1"
|
||||
sim: "iPhone 14 Pro"
|
||||
parallel: NO # Stop random test job failures
|
||||
runsOn: macos-13
|
||||
- os: "15.5"
|
||||
xcode: "13.4.1"
|
||||
sim: "iPhone 13 Pro"
|
||||
parallel: NO # Stop random test job failures
|
||||
runsOn: macos-12
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "Swift 5.1"
|
||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.1 clean build
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.3.1.app/Contents/Developer
|
||||
- name: "Swift 5.2"
|
||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.2 clean build
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.7.app/Contents/Developer
|
||||
- name: "Swift 5.3"
|
||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.3 clean build
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer
|
||||
|
||||
testing:
|
||||
runs-on: macOS-11
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "Testing in iOS 14.5"
|
||||
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=14.5,name=iPhone 12 Pro'
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_12.5.1.app/Contents/Developer
|
||||
- name: "Testing in iOS 15.0"
|
||||
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=15.0,name=iPhone 13 Pro'
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer
|
||||
|
||||
testing_compat:
|
||||
runs-on: macOS-10.15
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "Testing in iOS 13.7"
|
||||
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=13.7,name=iPhone 11 Pro'
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.7.app/Contents/Developer
|
||||
- name: "Testing in iOS 14.4"
|
||||
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=14.4,name=iPhone 12 Pro'
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer
|
||||
- uses: actions/checkout@v3
|
||||
- name: Testing in iOS ${{ matrix.os }}
|
||||
run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=${{ matrix.os }},name=${{ matrix.sim }}' -parallel-testing-enabled '${{ matrix.parallel }}'
|
||||
timeout-minutes: 20
|
||||
|
||||
example:
|
||||
runs-on: macOS-11
|
||||
runs-on: macos-12
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer
|
||||
DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- example: "Maps"
|
||||
- example: "Maps-SwiftUI"
|
||||
- example: "Stocks"
|
||||
- example: "Samples"
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "Build Maps"
|
||||
run: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Maps -sdk iphonesimulator clean build
|
||||
- name: "Build Stocks"
|
||||
run: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Stocks -sdk iphonesimulator clean build
|
||||
- name: "Build Samples"
|
||||
run: xcodebuild -workspace FloatingPanel.xcworkspace -scheme Samples -sdk iphonesimulator clean build
|
||||
- uses: actions/checkout@v3
|
||||
- name: Building ${{ matrix.example }}
|
||||
run: xcodebuild -workspace FloatingPanel.xcworkspace -scheme ${{ matrix.example }} -sdk iphonesimulator clean build
|
||||
|
||||
swiftpm:
|
||||
runs-on: macOS-11
|
||||
runs-on: macos-12
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer
|
||||
DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# 15.7
|
||||
- target: "x86_64-apple-ios15.7-simulator"
|
||||
- target: "arm64-apple-ios15.7-simulator"
|
||||
# 16.1
|
||||
- target: "x86_64-apple-ios16.1-simulator"
|
||||
- target: "arm64-apple-ios16.1-simulator"
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: "Swift Package build"
|
||||
run: swift build -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" -Xswiftc "-target" -Xswiftc "x86_64-apple-ios14.3-simulator"
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Swift Package Manager build"
|
||||
run: swift build -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" -Xswiftc "-target" -Xswiftc "${{ matrix.target }}"
|
||||
|
||||
carthage:
|
||||
runs-on: macOS-11
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Carthage build"
|
||||
run: carthage build --use-xcframeworks --no-skip-current
|
||||
|
||||
cocoapods:
|
||||
runs-on: macOS-11
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
- name: "CocoaPods: pod lib lint"
|
||||
run: pod lib lint --allow-warnings
|
||||
- name: "CocoaPods: pod spec lint"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
54B51131216C3D860033A6F3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 54B51130216C3D860033A6F3 /* Assets.xcassets */; };
|
||||
54B51134216C3D860033A6F3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54B51132216C3D860033A6F3 /* LaunchScreen.storyboard */; };
|
||||
54E26CB624A989090066C720 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E26CB524A989090066C720 /* Utils.swift */; };
|
||||
54E26CB824A98E310066C720 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E26CB724A98E310066C720 /* DetailViewController.swift */; };
|
||||
5D82A6A728D18422006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6A628D1841E006A44BA /* libswiftCoreGraphics.tbd */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -40,13 +41,14 @@
|
||||
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; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
54B51135216C3D860033A6F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
54E26CB524A989090066C720 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
|
||||
54E26CB724A98E310066C720 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = "<group>"; };
|
||||
5D82A6A628D1841E006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -55,6 +57,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
543844BD23D2BE2000D5EDE4 /* MapKit.framework in Frameworks */,
|
||||
5D82A6A728D18422006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
|
||||
549D23D2233C77D5008EF4D7 /* FloatingPanel.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -65,6 +68,7 @@
|
||||
543844BB23D2BE1F00D5EDE4 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5D82A6A628D1841E006A44BA /* libswiftCoreGraphics.tbd */,
|
||||
543844BC23D2BE2000D5EDE4 /* MapKit.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
@@ -91,14 +95,14 @@
|
||||
54B51128216C3D840033A6F3 /* Maps */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
54B51130216C3D860033A6F3 /* Assets.xcassets */,
|
||||
54B51132216C3D860033A6F3 /* LaunchScreen.storyboard */,
|
||||
54B5112D216C3D840033A6F3 /* Main.storyboard */,
|
||||
54B51129216C3D840033A6F3 /* AppDelegate.swift */,
|
||||
54B5112B216C3D840033A6F3 /* ViewController.swift */,
|
||||
54B5112B216C3D840033A6F3 /* MainViewController.swift */,
|
||||
549A5F58244673FE0025F312 /* SearchViewController.swift */,
|
||||
54E26CB724A98E310066C720 /* DetailViewController.swift */,
|
||||
54E26CB524A989090066C720 /* Utils.swift */,
|
||||
54B5112D216C3D840033A6F3 /* Main.storyboard */,
|
||||
54B51130216C3D860033A6F3 /* Assets.xcassets */,
|
||||
54B51132216C3D860033A6F3 /* LaunchScreen.storyboard */,
|
||||
54B51135216C3D860033A6F3 /* Info.plist */,
|
||||
);
|
||||
path = Maps;
|
||||
@@ -132,7 +136,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1000;
|
||||
LastUpgradeCheck = 1000;
|
||||
LastUpgradeCheck = 1300;
|
||||
ORGANIZATIONNAME = scenee;
|
||||
TargetAttributes = {
|
||||
54B51125216C3D840033A6F3 = {
|
||||
@@ -177,7 +181,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
549A5F59244673FE0025F312 /* SearchViewController.swift in Sources */,
|
||||
54B5112C216C3D840033A6F3 /* ViewController.swift in Sources */,
|
||||
54B5112C216C3D840033A6F3 /* MainViewController.swift in Sources */,
|
||||
54E26CB824A98E310066C720 /* DetailViewController.swift in Sources */,
|
||||
54B5112A216C3D840033A6F3 /* AppDelegate.swift in Sources */,
|
||||
54E26CB624A989090066C720 /* Utils.swift in Sources */,
|
||||
@@ -232,6 +236,7 @@
|
||||
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;
|
||||
@@ -293,6 +298,7 @@
|
||||
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;
|
||||
@@ -334,7 +340,11 @@
|
||||
"$(inherited)",
|
||||
"@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)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -353,7 +363,11 @@
|
||||
"$(inherited)",
|
||||
"@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)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1000"
|
||||
LastUpgradeVersion = "1300"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -27,8 +27,6 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
@@ -38,8 +36,8 @@
|
||||
ReferencedContainer = "container:Maps.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -63,8 +61,6 @@
|
||||
ReferencedContainer = "container:Maps.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -5,9 +5,5 @@ import UIKit
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
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"?>
|
||||
<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">
|
||||
<device id="retina5_9" orientation="portrait" appearance="light"/>
|
||||
<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="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<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="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<!--Main View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<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">
|
||||
<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"/>
|
||||
<subviews>
|
||||
<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>
|
||||
<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">
|
||||
<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"/>
|
||||
</view>
|
||||
<blurEffect style="prominent"/>
|
||||
@@ -54,28 +54,28 @@
|
||||
<objects>
|
||||
<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">
|
||||
<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"/>
|
||||
<subviews>
|
||||
<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">
|
||||
<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"/>
|
||||
<subviews>
|
||||
<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"/>
|
||||
</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">
|
||||
<rect key="frame" x="0.0" y="66" width="375" height="746"/>
|
||||
<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="393" height="786"/>
|
||||
<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">
|
||||
<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"/>
|
||||
<subviews>
|
||||
<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>
|
||||
<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"/>
|
||||
@@ -96,7 +96,7 @@
|
||||
</subviews>
|
||||
</stackView>
|
||||
<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>
|
||||
<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"/>
|
||||
@@ -116,7 +116,7 @@
|
||||
</subviews>
|
||||
</stackView>
|
||||
<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>
|
||||
<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"/>
|
||||
@@ -136,7 +136,7 @@
|
||||
</subviews>
|
||||
</stackView>
|
||||
<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>
|
||||
<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"/>
|
||||
@@ -168,10 +168,10 @@
|
||||
</view>
|
||||
<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">
|
||||
<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"/>
|
||||
<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"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="like" translatesAutoresizingMaskIntoConstraints="NO" id="GEk-yE-lLq">
|
||||
@@ -183,16 +183,16 @@
|
||||
</constraints>
|
||||
</imageView>
|
||||
<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>
|
||||
<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"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</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">
|
||||
<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"/>
|
||||
<color key="textColor" red="0.57647058819999997" green="0.57647058819999997" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -257,18 +257,18 @@
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="DetailViewController" id="Tp2-MF-IFz" customClass="DetailViewController" customModule="Maps" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<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"/>
|
||||
<subviews>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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">
|
||||
<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"/>
|
||||
</tableView>
|
||||
</subviews>
|
||||
|
||||
+54
-67
@@ -4,7 +4,7 @@ import UIKit
|
||||
import MapKit
|
||||
import FloatingPanel
|
||||
|
||||
class ViewController: UIViewController {
|
||||
class MainViewController: UIViewController {
|
||||
typealias PanelDelegate = FloatingPanelControllerDelegate & UIGestureRecognizerDelegate
|
||||
|
||||
// Search Panel
|
||||
@@ -22,7 +22,9 @@ class ViewController: UIViewController {
|
||||
storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
|
||||
|
||||
@IBOutlet weak var mapView: MKMapView!
|
||||
}
|
||||
|
||||
extension MainViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
fpc.contentMode = .fitToBounds
|
||||
@@ -40,7 +42,7 @@ class ViewController: UIViewController {
|
||||
layoutPanelForPhone()
|
||||
}
|
||||
|
||||
setupMapView()
|
||||
setUpMapView()
|
||||
setUpSearchView()
|
||||
}
|
||||
|
||||
@@ -53,9 +55,11 @@ class ViewController: UIViewController {
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
teardownMapView()
|
||||
tearDownMapView()
|
||||
}
|
||||
}
|
||||
|
||||
extension MainViewController {
|
||||
func layoutPanelForPad() {
|
||||
fpc.behavior = SearchPaneliPadBehavior()
|
||||
fpc.panGestureRecognizer.delegateProxy = fpcDelegate
|
||||
@@ -76,15 +80,14 @@ class ViewController: UIViewController {
|
||||
self.didMove(toParent: self)
|
||||
}
|
||||
|
||||
fpc.setAppearanceForPad()
|
||||
detailFpc.setAppearanceForPad()
|
||||
[fpc, detailFpc].forEach { $0.setAppearanceForPad() }
|
||||
}
|
||||
|
||||
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.setAppearanceForPhone()
|
||||
detailFpc.setAppearanceForPhone()
|
||||
|
||||
[fpc, detailFpc].forEach { $0.setAppearanceForPhone()}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +119,7 @@ extension FloatingPanelController {
|
||||
|
||||
// MARK: - UISearchBarDelegate
|
||||
|
||||
extension ViewController: UISearchBarDelegate {
|
||||
extension MainViewController: UISearchBarDelegate {
|
||||
func activate(searchBar: UISearchBar) {
|
||||
searchBar.showsCancelButton = true
|
||||
searchVC.showHeader(animated: true)
|
||||
@@ -147,12 +150,20 @@ extension ViewController: UISearchBarDelegate {
|
||||
// MARK: - iPhone
|
||||
|
||||
class SearchPanelPhoneDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
||||
unowned let owner: ViewController
|
||||
unowned let owner: MainViewController
|
||||
|
||||
init(owner: ViewController) {
|
||||
init(owner: MainViewController) {
|
||||
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 {
|
||||
switch newCollection.verticalSizeClass {
|
||||
case .compact:
|
||||
@@ -201,37 +212,21 @@ class SearchPanelPhoneDelegate: NSObject, FloatingPanelControllerDelegate, UIGes
|
||||
if targetState.pointee != .full {
|
||||
owner.searchVC.hideHeader(animated: true)
|
||||
}
|
||||
if targetState.pointee == .tip {
|
||||
vc.contentMode = .static
|
||||
}
|
||||
}
|
||||
|
||||
func floatingPanelDidEndAttracting(_ fpc: FloatingPanelController) {
|
||||
fpc.contentMode = .fitToBounds
|
||||
}
|
||||
}
|
||||
|
||||
class SearchPanelLandscapeLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .tip
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
||||
if #available(iOS 11.0, *) {
|
||||
return [
|
||||
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 8.0),
|
||||
surfaceView.widthAnchor.constraint(equalToConstant: 291),
|
||||
]
|
||||
} else {
|
||||
return [
|
||||
surfaceView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8.0),
|
||||
surfaceView.widthAnchor.constraint(equalToConstant: 291),
|
||||
]
|
||||
}
|
||||
return [
|
||||
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 8.0),
|
||||
surfaceView.widthAnchor.constraint(equalToConstant: 291),
|
||||
]
|
||||
}
|
||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||
return 0.0
|
||||
@@ -239,20 +234,18 @@ class SearchPanelLandscapeLayout: FloatingPanelLayout {
|
||||
}
|
||||
|
||||
class DetailPanelPhoneDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
||||
unowned let owner: ViewController
|
||||
unowned let owner: MainViewController
|
||||
|
||||
init(owner: ViewController) {
|
||||
init(owner: MainViewController) {
|
||||
self.owner = owner
|
||||
}
|
||||
}
|
||||
|
||||
class DetailPanelPhoneLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
let initialState: FloatingPanelState = .full
|
||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||
return 0.0
|
||||
@@ -262,9 +255,9 @@ class DetailPanelPhoneLayout: FloatingPanelLayout {
|
||||
// MARK: - iPad
|
||||
|
||||
class SearchPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
||||
unowned let owner: ViewController
|
||||
unowned let owner: MainViewController
|
||||
|
||||
init(owner: ViewController) {
|
||||
init(owner: MainViewController) {
|
||||
self.owner = owner
|
||||
}
|
||||
|
||||
@@ -301,13 +294,11 @@ class SearchPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestu
|
||||
class SearchPanelPadLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .top
|
||||
let initialState: FloatingPanelState = .tip
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 80.0, edge: .top, referenceGuide: .superview),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 200.0, edge: .top, referenceGuide: .superview),
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 80.0, edge: .top, referenceGuide: .superview),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 200.0, edge: .top, referenceGuide: .superview),
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||
return 0.0
|
||||
}
|
||||
@@ -329,15 +320,15 @@ class SearchPaneliPadBehavior: FloatingPanelBehavior {
|
||||
var momentumProjectionRate: CGFloat {
|
||||
return UIScrollView.DecelerationRate.fast.rawValue
|
||||
}
|
||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedTargetPosition: FloatingPanelState) -> Bool {
|
||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedState: FloatingPanelState) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
class DetailPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
|
||||
unowned let owner: ViewController
|
||||
unowned let owner: MainViewController
|
||||
|
||||
init(owner: ViewController) {
|
||||
init(owner: MainViewController) {
|
||||
self.owner = owner
|
||||
}
|
||||
|
||||
@@ -361,11 +352,9 @@ class DetailPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestu
|
||||
|
||||
class DetailPanelPadLeftLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .left
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .left, referenceGuide: .superview)
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .left, referenceGuide: .superview)
|
||||
]
|
||||
let initialState: FloatingPanelState = .full
|
||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||
return 0.0
|
||||
@@ -374,11 +363,9 @@ class DetailPanelPadLeftLayout: FloatingPanelLayout {
|
||||
|
||||
class DetailPanelPadRightLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .right
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .right, referenceGuide: .superview)
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .right, referenceGuide: .superview)
|
||||
]
|
||||
let initialState: FloatingPanelState = .full
|
||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||
return 0.0
|
||||
@@ -387,8 +374,8 @@ class DetailPanelPadRightLayout: FloatingPanelLayout {
|
||||
|
||||
// MARK: - MKMapViewDelegate
|
||||
|
||||
extension ViewController: MKMapViewDelegate {
|
||||
func setupMapView() {
|
||||
extension MainViewController: MKMapViewDelegate {
|
||||
func setUpMapView() {
|
||||
let center = CLLocationCoordinate2D(latitude: 37.623198015869235,
|
||||
longitude: -122.43066818432008)
|
||||
let span = MKCoordinateSpan(latitudeDelta: 0.4425100023575723,
|
||||
@@ -400,7 +387,7 @@ extension ViewController: MKMapViewDelegate {
|
||||
mapView.delegate = self
|
||||
}
|
||||
|
||||
func teardownMapView() {
|
||||
func tearDownMapView() {
|
||||
// Prevent a crash
|
||||
mapView.delegate = nil
|
||||
mapView = nil
|
||||
@@ -4,7 +4,7 @@ import UIKit
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
extension ViewController: UITableViewDelegate {
|
||||
extension MainViewController: UITableViewDelegate {
|
||||
func setUpSearchView() {
|
||||
searchVC.loadViewIfNeeded()
|
||||
searchVC.tableView.delegate = self
|
||||
@@ -81,9 +81,6 @@ class SearchViewController: UIViewController, UITableViewDataSource {
|
||||
|
||||
var items: [LocationItem] = []
|
||||
|
||||
// For iOS 10 only
|
||||
private lazy var shadowLayer: CAShapeLayer = CAShapeLayer()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
tableView.dataSource = self
|
||||
@@ -92,29 +89,6 @@ class SearchViewController: UIViewController, UITableViewDataSource {
|
||||
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 {
|
||||
return items.count
|
||||
}
|
||||
@@ -130,16 +104,16 @@ class SearchViewController: UIViewController, UITableViewDataSource {
|
||||
}
|
||||
|
||||
func showHeader(animated: Bool) {
|
||||
changeHeader(height: 116.0, aniamted: animated)
|
||||
changeHeader(height: 116.0, animated: animated)
|
||||
}
|
||||
|
||||
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 }
|
||||
if aniamted == false {
|
||||
if animated == false {
|
||||
updateHeader(height: height)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -18,40 +18,23 @@
|
||||
5442E24A25FC53C100A26F43 /* DebugTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5442E24925FC53C100A26F43 /* DebugTextViewController.swift */; };
|
||||
5442E25225FC541700A26F43 /* NestedScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5442E25125FC541700A26F43 /* NestedScrollViewController.swift */; };
|
||||
54496C59263A7E5A0031E0C8 /* UseCaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54496C58263A7E5A0031E0C8 /* UseCaseController.swift */; };
|
||||
544BC56826CC918200D0A436 /* AdaptiveLayoutTestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 544BC56726CC918200D0A436 /* AdaptiveLayoutTestViewController.swift */; };
|
||||
545DB9EE21511E6300CA77B8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9ED21511E6300CA77B8 /* AppDelegate.swift */; };
|
||||
545DB9F021511E6300CA77B8 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9EF21511E6300CA77B8 /* MainViewController.swift */; };
|
||||
545DB9F321511E6300CA77B8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F121511E6300CA77B8 /* Main.storyboard */; };
|
||||
545DB9F521511E6400CA77B8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F421511E6400CA77B8 /* Assets.xcassets */; };
|
||||
545DB9F821511E6400CA77B8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F621511E6400CA77B8 /* LaunchScreen.storyboard */; };
|
||||
545DBA0321511E6400CA77B8 /* SampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DBA0221511E6400CA77B8 /* SampleTests.swift */; };
|
||||
545DBA0E21511E6400CA77B8 /* SampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DBA0D21511E6400CA77B8 /* SampleUITests.swift */; };
|
||||
546341A125C6415100CA0596 /* UseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546341A025C6415100CA0596 /* UseCase.swift */; };
|
||||
546341AC25C6426500CA0596 /* CustomState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 546341AB25C6426500CA0596 /* CustomState.swift */; };
|
||||
549D23CB233C7779008EF4D7 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23CA233C7779008EF4D7 /* FloatingPanel.framework */; };
|
||||
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 */; };
|
||||
54CDC5D8215BBE23007D205C /* Components.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D7215BBE23007D205C /* Components.swift */; };
|
||||
54EAD35B263A75EB006A36EA /* Layouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EAD35A263A75EB006A36EA /* Layouts.swift */; };
|
||||
54CDC5D8215BBE23007D205C /* SupplementaryViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D7215BBE23007D205C /* SupplementaryViews.swift */; };
|
||||
54EAD35B263A75EB006A36EA /* PanelLayouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EAD35A263A75EB006A36EA /* PanelLayouts.swift */; };
|
||||
54EAD365263A765F006A36EA /* PagePanelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EAD364263A765F006A36EA /* PagePanelController.swift */; };
|
||||
5D82A6AD28D1843C006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6AC28D18438006A44BA /* libswiftCoreGraphics.tbd */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
545DB9FF21511E6400CA77B8 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 545DB9E221511E6300CA77B8 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 545DB9E921511E6300CA77B8;
|
||||
remoteInfo = FloatingModalSample;
|
||||
};
|
||||
545DBA0A21511E6400CA77B8 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 545DB9E221511E6300CA77B8 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 545DB9E921511E6300CA77B8;
|
||||
remoteInfo = FloatingModalSample;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
549D23CD233C7779008EF4D7 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
@@ -78,6 +61,7 @@
|
||||
5442E24925FC53C100A26F43 /* DebugTextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugTextViewController.swift; sourceTree = "<group>"; };
|
||||
5442E25125FC541700A26F43 /* NestedScrollViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedScrollViewController.swift; sourceTree = "<group>"; };
|
||||
54496C58263A7E5A0031E0C8 /* UseCaseController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UseCaseController.swift; sourceTree = "<group>"; };
|
||||
544BC56726CC918200D0A436 /* AdaptiveLayoutTestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveLayoutTestViewController.swift; sourceTree = "<group>"; };
|
||||
545DB9EA21511E6300CA77B8 /* Samples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Samples.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
545DB9ED21511E6300CA77B8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
545DB9EF21511E6300CA77B8 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
|
||||
@@ -85,19 +69,14 @@
|
||||
545DB9F421511E6400CA77B8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
545DB9F721511E6400CA77B8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
545DB9F921511E6400CA77B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
545DB9FE21511E6400CA77B8 /* SamplesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SamplesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
545DBA0221511E6400CA77B8 /* SampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleTests.swift; sourceTree = "<group>"; };
|
||||
545DBA0421511E6400CA77B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
545DBA0921511E6400CA77B8 /* SamplesUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SamplesUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
545DBA0D21511E6400CA77B8 /* SampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleUITests.swift; sourceTree = "<group>"; };
|
||||
545DBA0F21511E6400CA77B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
546341A025C6415100CA0596 /* UseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UseCase.swift; sourceTree = "<group>"; };
|
||||
546341AB25C6426500CA0596 /* CustomState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomState.swift; sourceTree = "<group>"; };
|
||||
549D23CA233C7779008EF4D7 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
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>"; };
|
||||
54EAD35A263A75EB006A36EA /* Layouts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layouts.swift; sourceTree = "<group>"; };
|
||||
54CDC5D7215BBE23007D205C /* SupplementaryViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupplementaryViews.swift; sourceTree = "<group>"; };
|
||||
54EAD35A263A75EB006A36EA /* PanelLayouts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PanelLayouts.swift; sourceTree = "<group>"; };
|
||||
54EAD364263A765F006A36EA /* PagePanelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagePanelController.swift; sourceTree = "<group>"; };
|
||||
5D82A6AC28D18438006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -105,28 +84,15 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5D82A6AD28D1843C006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
|
||||
549D23CB233C7779008EF4D7 /* FloatingPanel.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
545DB9FB21511E6400CA77B8 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
545DBA0621511E6400CA77B8 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
5442E22225FC519700A26F43 /* ViewControllers */ = {
|
||||
5442E22225FC519700A26F43 /* ContentViewControllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5442E23F25FC533800A26F43 /* DebugTableViewController.swift */,
|
||||
@@ -139,8 +105,9 @@
|
||||
5442E22725FC51E200A26F43 /* MultiPanelController.swift */,
|
||||
5442E22B25FC521F00A26F43 /* SettingsViewController.swift */,
|
||||
5442E22F25FC525200A26F43 /* TabBarViewController.swift */,
|
||||
544BC56726CC918200D0A436 /* AdaptiveLayoutTestViewController.swift */,
|
||||
);
|
||||
path = ViewControllers;
|
||||
path = ContentViewControllers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
545DB9E121511E6300CA77B8 = {
|
||||
@@ -148,9 +115,8 @@
|
||||
children = (
|
||||
549D23CA233C7779008EF4D7 /* FloatingPanel.framework */,
|
||||
545DB9EC21511E6300CA77B8 /* Sources */,
|
||||
545DBA0121511E6400CA77B8 /* Tests */,
|
||||
545DBA0C21511E6400CA77B8 /* UITests */,
|
||||
545DB9EB21511E6300CA77B8 /* Products */,
|
||||
5D82A6AB28D18438006A44BA /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -158,8 +124,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
545DB9EA21511E6300CA77B8 /* Samples.app */,
|
||||
545DB9FE21511E6400CA77B8 /* SamplesTests.xctest */,
|
||||
545DBA0921511E6400CA77B8 /* SamplesUITests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -172,45 +136,35 @@
|
||||
545DB9F121511E6300CA77B8 /* Main.storyboard */,
|
||||
545DB9ED21511E6300CA77B8 /* AppDelegate.swift */,
|
||||
545DB9EF21511E6300CA77B8 /* MainViewController.swift */,
|
||||
54496C58263A7E5A0031E0C8 /* UseCaseController.swift */,
|
||||
546341AA25C6421000CA0596 /* UseCases */,
|
||||
5442E22225FC519700A26F43 /* ViewControllers */,
|
||||
54EAD35A263A75EB006A36EA /* Layouts.swift */,
|
||||
5442E22225FC519700A26F43 /* ContentViewControllers */,
|
||||
54EAD35A263A75EB006A36EA /* PanelLayouts.swift */,
|
||||
546341AB25C6426500CA0596 /* CustomState.swift */,
|
||||
54CDC5D7215BBE23007D205C /* SupplementaryViews.swift */,
|
||||
54B51115216AFE5F0033A6F3 /* Extensions.swift */,
|
||||
54CDC5D7215BBE23007D205C /* Components.swift */,
|
||||
545DB9F921511E6400CA77B8 /* Info.plist */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
545DBA0121511E6400CA77B8 /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
545DBA0221511E6400CA77B8 /* SampleTests.swift */,
|
||||
545DBA0421511E6400CA77B8 /* Info.plist */,
|
||||
);
|
||||
path = Tests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
545DBA0C21511E6400CA77B8 /* UITests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
545DBA0D21511E6400CA77B8 /* SampleUITests.swift */,
|
||||
545DBA0F21511E6400CA77B8 /* Info.plist */,
|
||||
);
|
||||
path = UITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
546341AA25C6421000CA0596 /* UseCases */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
546341A025C6415100CA0596 /* UseCase.swift */,
|
||||
546341AB25C6426500CA0596 /* CustomState.swift */,
|
||||
54496C58263A7E5A0031E0C8 /* UseCaseController.swift */,
|
||||
54EAD364263A765F006A36EA /* PagePanelController.swift */,
|
||||
);
|
||||
path = UseCases;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5D82A6AB28D18438006A44BA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5D82A6AC28D18438006A44BA /* libswiftCoreGraphics.tbd */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -218,7 +172,6 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 545DBA1221511E6400CA77B8 /* Build configuration list for PBXNativeTarget "Samples" */;
|
||||
buildPhases = (
|
||||
54D7209621D4DB970054A255 /* ShellScript */,
|
||||
545DB9E621511E6300CA77B8 /* Sources */,
|
||||
545DB9E721511E6300CA77B8 /* Frameworks */,
|
||||
545DB9E821511E6300CA77B8 /* Resources */,
|
||||
@@ -233,42 +186,6 @@
|
||||
productReference = 545DB9EA21511E6300CA77B8 /* Samples.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
545DB9FD21511E6400CA77B8 /* SamplesTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 545DBA1521511E6400CA77B8 /* Build configuration list for PBXNativeTarget "SamplesTests" */;
|
||||
buildPhases = (
|
||||
545DB9FA21511E6400CA77B8 /* Sources */,
|
||||
545DB9FB21511E6400CA77B8 /* Frameworks */,
|
||||
545DB9FC21511E6400CA77B8 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
545DBA0021511E6400CA77B8 /* PBXTargetDependency */,
|
||||
);
|
||||
name = SamplesTests;
|
||||
productName = FloatingModalSampleTests;
|
||||
productReference = 545DB9FE21511E6400CA77B8 /* SamplesTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
545DBA0821511E6400CA77B8 /* SamplesUITests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 545DBA1821511E6400CA77B8 /* Build configuration list for PBXNativeTarget "SamplesUITests" */;
|
||||
buildPhases = (
|
||||
545DBA0521511E6400CA77B8 /* Sources */,
|
||||
545DBA0621511E6400CA77B8 /* Frameworks */,
|
||||
545DBA0721511E6400CA77B8 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
545DBA0B21511E6400CA77B8 /* PBXTargetDependency */,
|
||||
);
|
||||
name = SamplesUITests;
|
||||
productName = FloatingModalSampleUITests;
|
||||
productReference = 545DBA0921511E6400CA77B8 /* SamplesUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@@ -276,20 +193,12 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1000;
|
||||
LastUpgradeCheck = 1000;
|
||||
LastUpgradeCheck = 1300;
|
||||
ORGANIZATIONNAME = scenee;
|
||||
TargetAttributes = {
|
||||
545DB9E921511E6300CA77B8 = {
|
||||
CreatedOnToolsVersion = 10.0;
|
||||
};
|
||||
545DB9FD21511E6400CA77B8 = {
|
||||
CreatedOnToolsVersion = 10.0;
|
||||
TestTargetID = 545DB9E921511E6300CA77B8;
|
||||
};
|
||||
545DBA0821511E6400CA77B8 = {
|
||||
CreatedOnToolsVersion = 10.0;
|
||||
TestTargetID = 545DB9E921511E6300CA77B8;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 545DB9E521511E6300CA77B8 /* Build configuration list for PBXProject "Samples" */;
|
||||
@@ -306,8 +215,6 @@
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
545DB9E921511E6300CA77B8 /* Samples */,
|
||||
545DB9FD21511E6400CA77B8 /* SamplesTests */,
|
||||
545DBA0821511E6400CA77B8 /* SamplesUITests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -323,42 +230,8 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
545DB9FC21511E6400CA77B8 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
545DBA0721511E6400CA77B8 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* 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 */
|
||||
545DB9E621511E6300CA77B8 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
@@ -366,8 +239,9 @@
|
||||
files = (
|
||||
5442E23425FC528400A26F43 /* DetailViewController.swift in Sources */,
|
||||
54496C59263A7E5A0031E0C8 /* UseCaseController.swift in Sources */,
|
||||
54CDC5D8215BBE23007D205C /* Components.swift in Sources */,
|
||||
54CDC5D8215BBE23007D205C /* SupplementaryViews.swift in Sources */,
|
||||
54B51116216AFE5F0033A6F3 /* Extensions.swift in Sources */,
|
||||
544BC56826CC918200D0A436 /* AdaptiveLayoutTestViewController.swift in Sources */,
|
||||
5442E24A25FC53C100A26F43 /* DebugTextViewController.swift in Sources */,
|
||||
546341AC25C6426500CA0596 /* CustomState.swift in Sources */,
|
||||
5442E23A25FC52CD00A26F43 /* ModalViewController.swift in Sources */,
|
||||
@@ -379,44 +253,15 @@
|
||||
545DB9F021511E6300CA77B8 /* MainViewController.swift in Sources */,
|
||||
545DB9EE21511E6300CA77B8 /* AppDelegate.swift in Sources */,
|
||||
5442E23025FC525200A26F43 /* TabBarViewController.swift in Sources */,
|
||||
54EAD35B263A75EB006A36EA /* Layouts.swift in Sources */,
|
||||
54EAD35B263A75EB006A36EA /* PanelLayouts.swift in Sources */,
|
||||
54EAD365263A765F006A36EA /* PagePanelController.swift in Sources */,
|
||||
5442E24425FC538200A26F43 /* InspectorViewController.swift in Sources */,
|
||||
5442E22C25FC521F00A26F43 /* SettingsViewController.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;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
545DBA0021511E6400CA77B8 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 545DB9E921511E6300CA77B8 /* Samples */;
|
||||
targetProxy = 545DB9FF21511E6400CA77B8 /* PBXContainerItemProxy */;
|
||||
};
|
||||
545DBA0B21511E6400CA77B8 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 545DB9E921511E6300CA77B8 /* Samples */;
|
||||
targetProxy = 545DBA0A21511E6400CA77B8 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
545DB9F121511E6300CA77B8 /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
@@ -463,6 +308,7 @@
|
||||
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;
|
||||
@@ -524,6 +370,7 @@
|
||||
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;
|
||||
@@ -560,12 +407,16 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = Sources/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelSample;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SDKROOT)/usr/lib/swift",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = example.Samples;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -579,102 +430,22 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = Sources/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelSample;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SDKROOT)/usr/lib/swift",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = example.Samples;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
545DBA1621511E6400CA77B8 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalSampleTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Samples.app/Samples";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
545DBA1721511E6400CA77B8 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalSampleTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Samples.app/Samples";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
545DBA1921511E6400CA77B8 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = J3D7L9FHSS;
|
||||
INFOPLIST_FILE = UITests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalSampleUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_TARGET_NAME = FloatingModalSample;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
545DBA1A21511E6400CA77B8 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = J3D7L9FHSS;
|
||||
INFOPLIST_FILE = UITests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalSampleUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_TARGET_NAME = FloatingModalSample;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
@@ -696,24 +467,6 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
545DBA1521511E6400CA77B8 /* Build configuration list for PBXNativeTarget "SamplesTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
545DBA1621511E6400CA77B8 /* Debug */,
|
||||
545DBA1721511E6400CA77B8 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
545DBA1821511E6400CA77B8 /* Build configuration list for PBXNativeTarget "SamplesUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
545DBA1921511E6400CA77B8 /* Debug */,
|
||||
545DBA1A21511E6400CA77B8 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 545DB9E221511E6300CA77B8 /* Project object */;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1000"
|
||||
LastUpgradeVersion = "1300"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -27,8 +27,6 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
@@ -38,8 +36,8 @@
|
||||
ReferencedContainer = "container:Samples.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -61,8 +59,6 @@
|
||||
ReferencedContainer = "container:Samples.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?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="21701" 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>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="Stack View standard spacing" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
@@ -13,7 +15,7 @@
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="RootNavigationController" id="RoN-h0-uBD" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="hNW-5m-Omi">
|
||||
<rect key="frame" x="0.0" y="44" width="375" height="96"/>
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="96"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
@@ -29,22 +31,22 @@
|
||||
<objects>
|
||||
<viewController id="jF4-A0-Eq6" customClass="MainViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Smh-Bd-AAc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="7IS-PU-x0P">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="M0G-C8-hAO" style="IBUITableViewCellStyleDefault" id="ySY-oA-g81">
|
||||
<rect key="frame" x="0.0" y="28" width="600" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="50" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ySY-oA-g81" id="sXB-nH-2g2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="M0G-C8-hAO">
|
||||
<rect key="frame" x="16" y="0.0" width="568" height="43.5"/>
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
@@ -85,7 +87,7 @@
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="SettingsViewController" id="C1X-9Z-TyQ" customClass="SettingsViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="af9-Zr-Ppc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="197.33000000000001"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="197.5"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillProportionally" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="n93-ZL-fmC">
|
||||
@@ -123,7 +125,7 @@
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="js8-Qv-lUC">
|
||||
<rect key="frame" x="262" y="0.5" width="51" height="31"/>
|
||||
<connections>
|
||||
<action selector="toggleLargeTitle:" destination="C1X-9Z-TyQ" eventType="valueChanged" id="FJS-Ty-mCY"/>
|
||||
<action selector="toggleLargeTitle:" destination="C1X-9Z-TyQ" eventType="valueChanged" id="CxM-wn-r09"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
@@ -140,7 +142,7 @@
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="s6b-j9-8Kw">
|
||||
<rect key="frame" x="262" y="0.5" width="51" height="31"/>
|
||||
<connections>
|
||||
<action selector="toggleTranslucent:" destination="C1X-9Z-TyQ" eventType="valueChanged" id="nL4-3L-9hh"/>
|
||||
<action selector="toggleTranslucent:" destination="C1X-9Z-TyQ" eventType="valueChanged" id="w7r-AV-RqX"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
@@ -161,8 +163,8 @@
|
||||
</view>
|
||||
<size key="freeformSize" width="375" height="197.33000000000001"/>
|
||||
<connections>
|
||||
<outlet property="largeTitlesSwicth" destination="js8-Qv-lUC" id="FOm-6k-ffi"/>
|
||||
<outlet property="translucentSwicth" destination="s6b-j9-8Kw" id="jmf-WH-bzZ"/>
|
||||
<outlet property="largeTitlesSwitch" destination="js8-Qv-lUC" id="szl-pU-uRE"/>
|
||||
<outlet property="translucentSwitch" destination="s6b-j9-8Kw" id="8yK-Du-jkM"/>
|
||||
<outlet property="versionLabel" destination="WmC-Tq-NDN" id="Woh-kK-U0m"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
@@ -175,11 +177,11 @@
|
||||
<objects>
|
||||
<viewController id="RpE-lI-27a" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="JER-jz-KSq">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IvG-yp-yzI">
|
||||
<rect key="frame" x="20" y="0.0" width="39" height="30"/>
|
||||
<rect key="frame" x="20" y="48" width="39" height="30"/>
|
||||
<state key="normal" title="Close"/>
|
||||
<connections>
|
||||
<action selector="closeWithSender:" destination="RpE-lI-27a" eventType="touchUpInside" id="hj3-Xv-6Gq"/>
|
||||
@@ -206,11 +208,11 @@
|
||||
<objects>
|
||||
<viewController id="pOk-Zm-vD9" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="85d-ub-G8k">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NbG-e8-HdI">
|
||||
<rect key="frame" x="20" y="0.0" width="39" height="30"/>
|
||||
<rect key="frame" x="20" y="48" width="39" height="30"/>
|
||||
<state key="normal" title="Close"/>
|
||||
<connections>
|
||||
<action selector="closeWithSender:" destination="pOk-Zm-vD9" eventType="touchUpInside" id="111-PD-Pop"/>
|
||||
@@ -237,11 +239,11 @@
|
||||
<objects>
|
||||
<viewController id="lto-Zc-Vtp" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="ji9-Ez-N7i">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eFN-tN-4Ct">
|
||||
<rect key="frame" x="20" y="0.0" width="39" height="30"/>
|
||||
<rect key="frame" x="20" y="48" width="39" height="30"/>
|
||||
<state key="normal" title="Close"/>
|
||||
<connections>
|
||||
<action selector="closeWithSender:" destination="bYI-y3-Rzb" eventType="touchUpInside" id="YL4-GP-ZEZ"/>
|
||||
@@ -381,11 +383,11 @@
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="ModalViewController" id="bYI-y3-Rzb" customClass="ModalViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="qwo-GK-p1U">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="778"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="768"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vut-mK-Y4t" customClass="SafeAreaView" customModule="Samples" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="758" width="375" height="0.0"/>
|
||||
<rect key="frame" x="0.0" y="768" width="375" height="0.0"/>
|
||||
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sbF-Az-7sy">
|
||||
@@ -596,11 +598,11 @@
|
||||
</constraints>
|
||||
</view>
|
||||
<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"/>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="noi-1a-5bZ" customClass="CloseButton" customModule="Samples" customModuleProvider="target">
|
||||
<rect key="frame" x="319" y="0.0" width="44" height="44"/>
|
||||
<rect key="frame" x="319" y="48" width="44" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="0jg-5D-A1F"/>
|
||||
<constraint firstAttribute="width" constant="44" id="1Cq-PA-wgW"/>
|
||||
@@ -610,7 +612,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillProportionally" alignment="center" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="qux-uG-4o2">
|
||||
<rect key="frame" x="8" y="8" width="148" height="31"/>
|
||||
<rect key="frame" x="8" y="56" width="148" height="31"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="fitToBounds" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7lq-d3-PKi">
|
||||
<rect key="frame" x="0.0" y="5.5" width="91" height="20.5"/>
|
||||
@@ -658,19 +660,19 @@
|
||||
<color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<gestureRecognizers/>
|
||||
<constraints>
|
||||
<constraint firstItem="noi-1a-5bZ" firstAttribute="top" secondItem="aOK-7l-cA6" secondAttribute="top" id="EQy-cr-F2Y"/>
|
||||
<constraint firstItem="Kva-Z7-0qY" firstAttribute="top" secondItem="g7l-kO-y7q" secondAttribute="top" id="A4b-Le-m4I"/>
|
||||
<constraint firstItem="noi-1a-5bZ" firstAttribute="top" secondItem="aOK-7l-cA6" secondAttribute="top" priority="750" id="EQy-cr-F2Y"/>
|
||||
<constraint firstItem="tP3-oJ-4EB" firstAttribute="centerX" secondItem="aOK-7l-cA6" secondAttribute="centerX" id="EsD-Vf-dNZ"/>
|
||||
<constraint firstItem="Kva-Z7-0qY" firstAttribute="top" secondItem="aOK-7l-cA6" secondAttribute="top" id="Fff-HL-4mo"/>
|
||||
<constraint firstItem="8yw-OC-Ubk" firstAttribute="bottom" secondItem="g7l-kO-y7q" secondAttribute="bottom" id="JOL-wC-w74"/>
|
||||
<constraint firstItem="8yw-OC-Ubk" firstAttribute="bottom" secondItem="g7l-kO-y7q" secondAttribute="bottom" priority="750" id="JOL-wC-w74"/>
|
||||
<constraint firstItem="8yw-OC-Ubk" firstAttribute="leading" secondItem="aOK-7l-cA6" secondAttribute="leading" id="RiJ-Hb-OOZ"/>
|
||||
<constraint firstItem="8yw-OC-Ubk" firstAttribute="trailing" secondItem="aOK-7l-cA6" secondAttribute="trailing" id="Sof-yL-mwK"/>
|
||||
<constraint firstItem="tP3-oJ-4EB" firstAttribute="top" secondItem="g7l-kO-y7q" secondAttribute="top" constant="88" id="Zhb-Ss-epe"/>
|
||||
<constraint firstItem="tP3-oJ-4EB" firstAttribute="top" secondItem="g7l-kO-y7q" secondAttribute="top" priority="750" constant="88" id="Zhb-Ss-epe"/>
|
||||
<constraint firstItem="Kva-Z7-0qY" firstAttribute="trailing" secondItem="aOK-7l-cA6" secondAttribute="trailing" id="kkp-Yo-FQW"/>
|
||||
<constraint firstItem="aOK-7l-cA6" firstAttribute="trailing" secondItem="noi-1a-5bZ" secondAttribute="trailing" constant="12" id="lv9-Nf-HNB"/>
|
||||
<constraint firstItem="qux-uG-4o2" firstAttribute="top" secondItem="aOK-7l-cA6" secondAttribute="top" constant="8" id="naa-cf-ZIc"/>
|
||||
<constraint firstItem="Kva-Z7-0qY" firstAttribute="leading" secondItem="aOK-7l-cA6" secondAttribute="leading" id="oVC-i1-TwS"/>
|
||||
<constraint firstItem="aOK-7l-cA6" firstAttribute="bottom" secondItem="Kva-Z7-0qY" secondAttribute="bottom" id="rW2-mF-5DR"/>
|
||||
<constraint firstItem="8yw-OC-Ubk" firstAttribute="top" relation="greaterThanOrEqual" secondItem="tP3-oJ-4EB" secondAttribute="bottom" constant="88" id="vKQ-h9-uKt"/>
|
||||
<constraint firstItem="aOK-7l-cA6" firstAttribute="bottom" secondItem="Kva-Z7-0qY" secondAttribute="bottom" priority="750" id="rW2-mF-5DR"/>
|
||||
<constraint firstItem="8yw-OC-Ubk" firstAttribute="top" relation="greaterThanOrEqual" secondItem="tP3-oJ-4EB" secondAttribute="bottom" priority="750" constant="88" id="vKQ-h9-uKt"/>
|
||||
<constraint firstItem="qux-uG-4o2" firstAttribute="leading" secondItem="g7l-kO-y7q" secondAttribute="leading" constant="8" id="zXb-R9-bMO"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
@@ -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"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</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>
|
||||
<viewLayoutGuide key="safeArea" id="5ET-zC-lCb"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<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 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="wUo-kb-NIn" firstAttribute="top" secondItem="9YG-0j-Zzg" secondAttribute="top" constant="16" id="ogC-1W-upw"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<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>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="x1h-y1-h8q" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-1" y="734"/>
|
||||
<point key="canvasLocation" x="-2.1739130434782612" y="733.92857142857144"/>
|
||||
</scene>
|
||||
<!--Adaptive Layout Test View Controller-->
|
||||
<scene sceneID="rDI-lU-wEx">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="AdaptiveLayoutTestViewController" id="5nC-6E-bXf" customClass="AdaptiveLayoutTestViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="jXL-Ss-NCJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="778"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="W7W-ET-Wco" customClass="IntrinsicTableView" customModule="Samples" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="778"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" restorationIdentifier="Cell" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="Mqi-zK-WA7">
|
||||
<rect key="frame" x="0.0" y="50" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Mqi-zK-WA7" id="X46-Fp-6Hr">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="5nC-6E-bXf" id="RHg-aY-HNW"/>
|
||||
<outlet property="delegate" destination="5nC-6E-bXf" id="0YX-fh-bB8"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="ZfG-sd-dcQ"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="W7W-ET-Wco" firstAttribute="trailing" secondItem="ZfG-sd-dcQ" secondAttribute="trailing" id="3kP-rg-7c6"/>
|
||||
<constraint firstAttribute="bottom" secondItem="W7W-ET-Wco" secondAttribute="bottom" id="FdS-X9-D1D"/>
|
||||
<constraint firstItem="W7W-ET-Wco" firstAttribute="leading" secondItem="ZfG-sd-dcQ" secondAttribute="leading" id="HXa-oO-jag"/>
|
||||
<constraint firstItem="W7W-ET-Wco" firstAttribute="top" secondItem="jXL-Ss-NCJ" secondAttribute="top" id="gMX-Wq-7G8"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<size key="freeformSize" width="375" height="778"/>
|
||||
<connections>
|
||||
<outlet property="tableView" destination="W7W-ET-Wco" id="N54-Fv-2Jq"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="7hJ-XW-9az" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="4005" y="734"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<designables>
|
||||
<designable name="noi-1a-5bZ">
|
||||
<size key="intrinsicContentSize" width="30" height="30"/>
|
||||
</designable>
|
||||
</designables>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="r1P-2i-NDe"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
@@ -796,7 +867,7 @@ Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
|
||||
<color red="0.68627450980392157" green="0.32156862745098042" blue="0.87058823529411766" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemTealColor">
|
||||
<color red="0.35294117647058826" green="0.78431372549019607" blue="0.98039215686274506" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.18823529411764706" green="0.69019607843137254" blue="0.7803921568627451" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||
|
||||
import UIKit
|
||||
import FloatingPanel
|
||||
|
||||
final class AdaptiveLayoutTestViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
|
||||
class PanelLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .full
|
||||
|
||||
private unowned var targetGuide: UILayoutGuide
|
||||
|
||||
init(targetGuide: UILayoutGuide) {
|
||||
self.targetGuide = targetGuide
|
||||
}
|
||||
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelAdaptiveLayoutAnchor(
|
||||
absoluteOffset: 0.0,
|
||||
contentLayout: targetGuide,
|
||||
referenceGuide: .superview,
|
||||
contentBoundingGuide: .safeArea
|
||||
),
|
||||
.half: FloatingPanelAdaptiveLayoutAnchor(
|
||||
fractionalOffset: 0.5,
|
||||
contentLayout: targetGuide,
|
||||
referenceGuide: .superview,
|
||||
contentBoundingGuide: .safeArea
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@IBOutlet weak var tableView: IntrinsicTableView!
|
||||
private let cellID = "Cell"
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
1
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
50
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
|
||||
cell.textLabel?.text = "\(indexPath.row)"
|
||||
return cell
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
let headerView = UIView()
|
||||
headerView.backgroundColor = .orange
|
||||
return headerView
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
44.0
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
40
|
||||
}
|
||||
}
|
||||
|
||||
class IntrinsicTableView: UITableView {
|
||||
|
||||
override var contentSize:CGSize {
|
||||
didSet {
|
||||
invalidateIntrinsicContentSize()
|
||||
}
|
||||
}
|
||||
|
||||
override var intrinsicContentSize: CGSize {
|
||||
layoutIfNeeded()
|
||||
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
|
||||
}
|
||||
}
|
||||
+63
-33
@@ -18,7 +18,7 @@ class DebugTableViewController: InspectableViewController {
|
||||
stackView.axis = .vertical
|
||||
stackView.distribution = .fillEqually
|
||||
stackView.alignment = .trailing
|
||||
stackView.spacing = 10.0
|
||||
stackView.spacing = 4
|
||||
return stackView
|
||||
}()
|
||||
private lazy var reorderButton: UIButton = {
|
||||
@@ -28,39 +28,48 @@ class DebugTableViewController: InspectableViewController {
|
||||
button.addTarget(self, action: #selector(reorderItems), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
private lazy var trackingSwitchWrapper: UIStackView = {
|
||||
let stackView = UIStackView()
|
||||
stackView.axis = .horizontal
|
||||
stackView.distribution = .fillProportionally
|
||||
stackView.alignment = .center
|
||||
stackView.spacing = 8.0
|
||||
stackView.addArrangedSubview(trackingLabel)
|
||||
stackView.addArrangedSubview(trackingSwitch)
|
||||
return stackView
|
||||
private lazy var trackingButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.setTitle(Menu.trackScrolling.rawValue, for: .normal)
|
||||
button.setTitleColor(view.tintColor, for: .normal)
|
||||
button.addTarget(self, action: #selector(toggleTrackingScroll), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
private lazy var trackingLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.text = "Tracking"
|
||||
label.font = UIFont.systemFont(ofSize: 17.0, weight: .regular)
|
||||
return label
|
||||
}()
|
||||
private lazy var trackingSwitch: UISwitch = {
|
||||
let trackingSwitch = UISwitch()
|
||||
trackingSwitch.isOn = true
|
||||
trackingSwitch.addTarget(self, action: #selector(turnTrackingOn), for: .touchUpInside)
|
||||
return trackingSwitch
|
||||
private lazy var followingButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.setTitleColor(view.tintColor, for: .normal)
|
||||
button.addTarget(self, action: #selector(toggleFollowingScroll), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
var kvoObservers: [NSKeyValueObservation] = []
|
||||
|
||||
private var fpc: FloatingPanelController? { parent as? FloatingPanelController }
|
||||
private lazy var items: [String] = {
|
||||
let items = (0..<100).map { "Items \($0)" }
|
||||
return Command.replace(items: items)
|
||||
}()
|
||||
private var itemHeight: CGFloat = 66.0
|
||||
|
||||
// MARK: Flags
|
||||
private var tracksScrollView: Bool = false {
|
||||
didSet {
|
||||
let title = "\(Menu.trackScrolling.rawValue): \(tracksScrollView ? "on" : "off")"
|
||||
trackingButton.setTitle(title, for: .normal)
|
||||
}
|
||||
}
|
||||
private var followsScrollViewBouncing: Bool = false {
|
||||
didSet {
|
||||
let title = "\(Menu.followScrolling.rawValue): \(followsScrollViewBouncing ? "on" : "off")"
|
||||
followingButton.setTitle(title, for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
enum Menu: String, CaseIterable {
|
||||
case turnOffTracking = "Tracking"
|
||||
case trackScrolling = "Tracking"
|
||||
case followScrolling = "Following"
|
||||
case reorder = "Reorder"
|
||||
}
|
||||
|
||||
@@ -87,12 +96,12 @@ class DebugTableViewController: InspectableViewController {
|
||||
}
|
||||
}
|
||||
|
||||
func execute(for vc: DebugTableViewController) {
|
||||
func execute(for vc: DebugTableViewController, sourceView: UIView) {
|
||||
switch self {
|
||||
case .animateScroll:
|
||||
vc.animateScroll()
|
||||
case .changeContentSize:
|
||||
vc.changeContentSize()
|
||||
vc.changeContentSize(sourceView: sourceView)
|
||||
case .moveToFull:
|
||||
vc.moveToFull()
|
||||
case .moveToHalf:
|
||||
@@ -135,13 +144,19 @@ class DebugTableViewController: InspectableViewController {
|
||||
switch menu {
|
||||
case .reorder:
|
||||
buttonStackView.addArrangedSubview(reorderButton)
|
||||
case .turnOffTracking:
|
||||
buttonStackView.addArrangedSubview(trackingSwitchWrapper)
|
||||
case .trackScrolling:
|
||||
buttonStackView.addArrangedSubview(trackingButton)
|
||||
case .followScrolling:
|
||||
buttonStackView.addArrangedSubview(followingButton)
|
||||
}
|
||||
}
|
||||
// Set titles
|
||||
tracksScrollView = true
|
||||
followsScrollViewBouncing = false
|
||||
}
|
||||
|
||||
// MARK: - Menu
|
||||
|
||||
@objc
|
||||
private func reorderItems() {
|
||||
if reorderButton.titleLabel?.text == Menu.reorder.rawValue {
|
||||
@@ -154,19 +169,25 @@ class DebugTableViewController: InspectableViewController {
|
||||
}
|
||||
|
||||
@objc
|
||||
private func turnTrackingOn(_ sender: UISwitch) {
|
||||
guard let fpc = self.parent as? FloatingPanelController else { return }
|
||||
if sender.isOn {
|
||||
private func toggleTrackingScroll() {
|
||||
tracksScrollView.toggle()
|
||||
guard let fpc = fpc else { return }
|
||||
if tracksScrollView {
|
||||
fpc.track(scrollView: tableView)
|
||||
} else {
|
||||
fpc.untrack(scrollView: tableView)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
private func toggleFollowingScroll() {
|
||||
followsScrollViewBouncing.toggle()
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
private func execute(command: Command) {
|
||||
command.execute(for: self)
|
||||
private func execute(command: Command, sourceView: UIView) {
|
||||
command.execute(for: self, sourceView: sourceView)
|
||||
}
|
||||
|
||||
@objc
|
||||
@@ -177,7 +198,7 @@ class DebugTableViewController: InspectableViewController {
|
||||
}
|
||||
|
||||
@objc
|
||||
private func changeContentSize() {
|
||||
private func changeContentSize(sourceView: UIView) {
|
||||
let actionSheet = UIAlertController(title: "Change content size", message: "", preferredStyle: .actionSheet)
|
||||
actionSheet.addAction(UIAlertAction(title: "Large", style: .default, handler: { (_) in
|
||||
self.itemHeight = 66.0
|
||||
@@ -201,6 +222,11 @@ class DebugTableViewController: InspectableViewController {
|
||||
self.changeItems(3)
|
||||
}))
|
||||
|
||||
if let popoverController = actionSheet.popoverPresentationController {
|
||||
popoverController.sourceView = sourceView
|
||||
popoverController.sourceRect = sourceView.bounds
|
||||
}
|
||||
|
||||
self.present(actionSheet, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@@ -244,13 +270,17 @@ extension DebugTableViewController: UITableViewDataSource {
|
||||
|
||||
extension DebugTableViewController: UITableViewDelegate {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if followsScrollViewBouncing {
|
||||
fpc?.followScrollViewBouncing()
|
||||
}
|
||||
print("TableView --- ", scrollView.contentOffset, scrollView.contentInset)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
print("DebugTableViewController -- select row \(indexPath.row)")
|
||||
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]? {
|
||||
+9
-5
@@ -11,9 +11,7 @@ final class DebugTextViewController: UIViewController, UITextViewDelegate {
|
||||
textView.delegate = self
|
||||
print("viewDidLoad: TextView --- ", textView.contentOffset, textView.contentInset)
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
textView.contentInsetAdjustmentBehavior = .never
|
||||
}
|
||||
textView.contentInsetAdjustmentBehavior = .never
|
||||
}
|
||||
|
||||
override func viewWillLayoutSubviews() {
|
||||
@@ -32,8 +30,14 @@ final class DebugTextViewController: UIViewController, UITextViewDelegate {
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+14
-19
@@ -5,29 +5,25 @@ import FloatingPanel
|
||||
|
||||
final class ImageViewController: UIViewController {
|
||||
class PanelLayout: FloatingPanelLayout {
|
||||
weak var targetGuide: UILayoutGuide?
|
||||
init(targetGuide: UILayoutGuide?) {
|
||||
private unowned var targetGuide: UILayoutGuide
|
||||
init(targetGuide: UILayoutGuide) {
|
||||
self.targetGuide = targetGuide
|
||||
}
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .full
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
if #available(iOS 11.0, *), let targetGuide = targetGuide {
|
||||
return [
|
||||
.full: FloatingPanelAdaptiveLayoutAnchor(absoluteOffset: 0,
|
||||
contentLayout: targetGuide,
|
||||
referenceGuide: .superview),
|
||||
.half: FloatingPanelAdaptiveLayoutAnchor(fractionalOffset: 0.5,
|
||||
contentLayout: targetGuide,
|
||||
referenceGuide: .superview)
|
||||
]
|
||||
} else {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 500,
|
||||
edge: .bottom,
|
||||
referenceGuide: .superview)
|
||||
]
|
||||
}
|
||||
return [
|
||||
.full: FloatingPanelAdaptiveLayoutAnchor(
|
||||
absoluteOffset: 0,
|
||||
contentLayout: targetGuide,
|
||||
referenceGuide: .superview
|
||||
),
|
||||
.half: FloatingPanelAdaptiveLayoutAnchor(
|
||||
fractionalOffset: 0.5,
|
||||
contentLayout: targetGuide,
|
||||
referenceGuide: .superview
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +37,6 @@ final class ImageViewController: UIViewController {
|
||||
case withHeaderFooter
|
||||
}
|
||||
|
||||
@available(iOS 11.0, *)
|
||||
func layoutGuideFor(mode: Mode) -> UILayoutGuide {
|
||||
switch mode {
|
||||
case .onlyImage:
|
||||
+7
-10
@@ -56,7 +56,6 @@ final class ModalViewController: UIViewController, FloatingPanelControllerDelega
|
||||
@IBAction func updateLayout(_ sender: Any) {
|
||||
isNewlayout = !isNewlayout
|
||||
UIView.animate(withDuration: 0.5) {
|
||||
self.fpc.layout = (self.isNewlayout) ? ModalSecondLayout() : FloatingPanelBottomLayout()
|
||||
self.fpc.invalidateLayout()
|
||||
}
|
||||
}
|
||||
@@ -66,14 +65,12 @@ final class ModalViewController: UIViewController, FloatingPanelControllerDelega
|
||||
}
|
||||
|
||||
class ModalSecondLayout: FloatingPanelLayout {
|
||||
var position: FloatingPanelPosition = .bottom
|
||||
var initialState: FloatingPanelState { .half }
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 262, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .half
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 262, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
}
|
||||
+3
-5
@@ -57,11 +57,9 @@ final class MultiPanelController: FloatingPanelController, FloatingPanelControll
|
||||
private final class FirstViewLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .full
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 40.0, edge: .top, referenceGuide: .superview)
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 40.0, edge: .top, referenceGuide: .superview)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
if #available(iOS 15, *) {
|
||||
tabBarController?.tabBar.scrollEdgeAppearance = UITabBarAppearance()
|
||||
}
|
||||
|
||||
switch tabBarItem.tag {
|
||||
case 1:
|
||||
fpc.behavior = TwoTabBarPanelBehavior()
|
||||
@@ -191,14 +195,12 @@ class OneTabBarPanelLayout: FloatingPanelLayout {
|
||||
}
|
||||
|
||||
class TwoTabBarPanelLayout: FloatingPanelLayout {
|
||||
var initialState: FloatingPanelState { .half }
|
||||
var position: FloatingPanelPosition { .bottom }
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 261.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
let initialState: FloatingPanelState = .half
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 261.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
|
||||
class TwoTabBarPanelBehavior: FloatingPanelBehavior {
|
||||
@@ -236,13 +238,8 @@ class ThreeTabBarPanelLayout: FloatingPanelLayout {
|
||||
}
|
||||
|
||||
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
||||
if #available(iOS 11.0, *) {
|
||||
leftConstraint = surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, 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)
|
||||
}
|
||||
leftConstraint = surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 0.0)
|
||||
rightConstraint = surfaceView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: 0.0)
|
||||
return [ leftConstraint, rightConstraint ]
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,20 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
func makeBoundsLayoutGuide() -> UILayoutGuide {
|
||||
let guide = UILayoutGuide()
|
||||
addLayoutGuide(guide)
|
||||
NSLayoutConstraint.activate([
|
||||
guide.topAnchor.constraint(equalTo: topAnchor),
|
||||
guide.leftAnchor.constraint(equalTo: leftAnchor),
|
||||
guide.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
guide.rightAnchor.constraint(equalTo: rightAnchor),
|
||||
])
|
||||
return guide
|
||||
}
|
||||
}
|
||||
|
||||
protocol LayoutGuideProvider {
|
||||
var topAnchor: NSLayoutYAxisAnchor { get }
|
||||
var bottomAnchor: NSLayoutYAxisAnchor { get }
|
||||
@@ -19,22 +33,10 @@ class CustomLayoutGuide: LayoutGuideProvider {
|
||||
|
||||
extension UIViewController {
|
||||
var layoutInsets: UIEdgeInsets {
|
||||
if #available(iOS 11.0, *) {
|
||||
return view.safeAreaInsets
|
||||
} else {
|
||||
return UIEdgeInsets(top: topLayoutGuide.length,
|
||||
left: 0.0,
|
||||
bottom: bottomLayoutGuide.length,
|
||||
right: 0.0)
|
||||
}
|
||||
return view.safeAreaInsets
|
||||
}
|
||||
|
||||
var layoutGuide: LayoutGuideProvider {
|
||||
if #available(iOS 11.0, *) {
|
||||
return view!.safeAreaLayoutGuide
|
||||
} else {
|
||||
return CustomLayoutGuide(topAnchor: topLayoutGuide.bottomAnchor,
|
||||
bottomAnchor: bottomLayoutGuide.topAnchor)
|
||||
}
|
||||
return view.safeAreaLayoutGuide
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,24 +6,20 @@ import FloatingPanel
|
||||
final class MainViewController: UIViewController {
|
||||
@IBOutlet weak var tableView: UITableView!
|
||||
private var observations: [NSKeyValueObservation] = []
|
||||
|
||||
private lazy var useCaseController = UseCaseController(mainVC: self)
|
||||
}
|
||||
|
||||
extension MainViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
tableView.dataSource = self
|
||||
tableView.delegate = self
|
||||
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
|
||||
automaticallyAdjustsScrollViewInsets = false
|
||||
|
||||
let searchController = UISearchController(searchResultsController: nil)
|
||||
if #available(iOS 11.0, *) {
|
||||
navigationItem.searchController = searchController
|
||||
navigationItem.hidesSearchBarWhenScrolling = false
|
||||
navigationItem.largeTitleDisplayMode = .automatic
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
navigationItem.searchController = searchController
|
||||
navigationItem.hidesSearchBarWhenScrolling = false
|
||||
navigationItem.largeTitleDisplayMode = .automatic
|
||||
var insets = UIEdgeInsets.zero
|
||||
insets.bottom += 69.0
|
||||
tableView.contentInset = insets
|
||||
@@ -34,12 +30,10 @@ final class MainViewController: UIViewController {
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
if #available(iOS 11.0, *) {
|
||||
if let observation = navigationController?.navigationBar.observe(\.prefersLargeTitles, changeHandler: { (bar, _) in
|
||||
self.tableView.reloadData()
|
||||
}) {
|
||||
observations.append(observation)
|
||||
}
|
||||
if let observation = navigationController?.navigationBar.observe(\.prefersLargeTitles, changeHandler: { (bar, _) in
|
||||
self.tableView.reloadData()
|
||||
}) {
|
||||
observations.append(observation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +41,9 @@ final class MainViewController: UIViewController {
|
||||
super.viewWillDisappear(animated)
|
||||
observations.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- Actions
|
||||
extension MainViewController {
|
||||
@IBAction func showDebugMenu(_ sender: UIBarButtonItem) {
|
||||
useCaseController.setUpSettingsPanel(for: self)
|
||||
}
|
||||
@@ -56,12 +51,8 @@ final class MainViewController: UIViewController {
|
||||
|
||||
extension MainViewController: UITableViewDataSource {
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if #available(iOS 11.0, *) {
|
||||
if navigationController?.navigationBar.prefersLargeTitles == true {
|
||||
return UseCase.allCases.count + 30
|
||||
} else {
|
||||
return UseCase.allCases.count
|
||||
}
|
||||
if navigationController?.navigationBar.prefersLargeTitles == true {
|
||||
return UseCase.allCases.count + 30
|
||||
} else {
|
||||
return UseCase.allCases.count
|
||||
}
|
||||
|
||||
+14
-26
@@ -23,14 +23,11 @@ extension MainViewController: FloatingPanelLayout {
|
||||
class TopPositionedPanelLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .top
|
||||
let initialState: FloatingPanelState = .full
|
||||
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 88.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .top, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 88.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .top, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
|
||||
class IntrinsicPanelLayout: FloatingPanelBottomLayout {
|
||||
@@ -45,13 +42,10 @@ class IntrinsicPanelLayout: FloatingPanelBottomLayout {
|
||||
class RemovablePanelLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .half
|
||||
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 130.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||
return 0.3
|
||||
@@ -61,13 +55,10 @@ class RemovablePanelLayout: FloatingPanelLayout {
|
||||
class RemovablePanelLandscapeLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .full
|
||||
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
|
||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||
return 0.3
|
||||
@@ -77,12 +68,9 @@ class RemovablePanelLandscapeLayout: FloatingPanelLayout {
|
||||
class ModalPanelLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .full
|
||||
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .safeArea),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .safeArea),
|
||||
]
|
||||
|
||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||
return 0.3
|
||||
+7
-7
@@ -4,6 +4,9 @@ import UIKit
|
||||
|
||||
@IBDesignable
|
||||
final class CloseButton: UIButton {
|
||||
override var isHighlighted: Bool { didSet { setNeedsDisplay() } }
|
||||
override var isSelected: Bool { didSet { setNeedsDisplay() } }
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
render()
|
||||
@@ -17,14 +20,11 @@ final class CloseButton: UIButton {
|
||||
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) {
|
||||
func p(_ p: CGFloat) -> CGFloat {
|
||||
return p * (2.0 / 3.0)
|
||||
}
|
||||
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
|
||||
context.setLineWidth(p(1.0))
|
||||
@@ -4,14 +4,10 @@ import UIKit
|
||||
import FloatingPanel
|
||||
|
||||
final class PagePanelController: NSObject {
|
||||
lazy var 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
|
||||
})
|
||||
var pages: [UIViewController] = []
|
||||
}
|
||||
|
||||
extension PagePanelController {
|
||||
func makePageViewControllerForContent() -> UIPageViewController {
|
||||
pages = [DebugTableViewController(), DebugTableViewController(), DebugTableViewController()]
|
||||
let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:])
|
||||
@@ -22,6 +18,13 @@ final class PagePanelController: NSObject {
|
||||
}
|
||||
|
||||
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 closeButton = UIButton(type: .custom)
|
||||
pageVC.view.addSubview(closeButton)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
enum UseCase: Int, CaseIterable {
|
||||
case trackingTableView
|
||||
@@ -10,6 +10,7 @@ enum UseCase: Int, CaseIterable {
|
||||
case showPanelModal
|
||||
case showMultiPanelModal
|
||||
case showPanelInSheetModal
|
||||
case showOnWindow
|
||||
case showTabBar
|
||||
case showPageView
|
||||
case showPageContentView
|
||||
@@ -23,7 +24,9 @@ enum UseCase: Int, CaseIterable {
|
||||
case showAdaptivePanel
|
||||
case showAdaptivePanelWithCustomGuide
|
||||
case showCustomStatePanel
|
||||
}
|
||||
|
||||
extension UseCase {
|
||||
var name: String {
|
||||
switch self {
|
||||
case .trackingTableView: return "Scroll tracking(TableView)"
|
||||
@@ -32,6 +35,7 @@ enum UseCase: Int, CaseIterable {
|
||||
case .showModal: return "Show Modal"
|
||||
case .showPanelModal: return "Show 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 .showTabBar: return "Show Tab Bar"
|
||||
case .showPageView: return "Show Page View"
|
||||
@@ -48,31 +52,47 @@ enum UseCase: Int, CaseIterable {
|
||||
case .showCustomStatePanel: return "Show Panel with Custom state"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var storyboardID: String? {
|
||||
extension UseCase {
|
||||
private enum Content {
|
||||
case storyboard(String)
|
||||
case viewController(UIViewController)
|
||||
}
|
||||
|
||||
private var content: Content {
|
||||
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
|
||||
case .trackingTableView: return .viewController(DebugTableViewController())
|
||||
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 .showAdaptivePanelWithCustomGuide: return .storyboard(String(describing: AdaptiveLayoutTestViewController.self))
|
||||
case .showCustomStatePanel: return .viewController(DebugTableViewController())
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+222
-129
@@ -5,48 +5,84 @@ import FloatingPanel
|
||||
|
||||
final class UseCaseController: NSObject {
|
||||
unowned let mainVC: MainViewController
|
||||
private(set) var useCase: UseCase = .trackingTableView
|
||||
private(set) var useCase: UseCase
|
||||
|
||||
fileprivate var mainPanelVC: FloatingPanelController!
|
||||
private var mainPanelVC: FloatingPanelController!
|
||||
private var detailPanelVC: FloatingPanelController!
|
||||
private var settingsPanelVC: FloatingPanelController!
|
||||
|
||||
private lazy var pagePanelController = PagePanelController()
|
||||
|
||||
private var mainPanelObserves: [NSKeyValueObservation] = []
|
||||
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!)
|
||||
|
||||
detailPanelVC?.removePanelFromParent(animated: true, completion: nil)
|
||||
detailPanelVC = nil
|
||||
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 .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:
|
||||
detailPanelVC?.removePanelFromParent(animated: false)
|
||||
|
||||
// Initialize FloatingPanelController
|
||||
detailPanelVC = FloatingPanelController()
|
||||
detailPanelVC.delegate = self
|
||||
|
||||
let appearance = SurfaceAppearance()
|
||||
appearance.cornerRadius = 6.0
|
||||
detailPanelVC.surfaceView.appearance = appearance
|
||||
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.delegate = self
|
||||
fpc.surfaceView.appearance = {
|
||||
let appearance = SurfaceAppearance()
|
||||
appearance.cornerRadius = 6.0
|
||||
return appearance
|
||||
}()
|
||||
// Set a content view controller
|
||||
detailPanelVC.set(contentViewController: contentVC)
|
||||
|
||||
detailPanelVC.contentMode = .fitToBounds
|
||||
fpc.set(contentViewController: contentVC)
|
||||
fpc.contentMode = .fitToBounds
|
||||
(contentVC as? DetailViewController)?.intrinsicHeightConstraint.isActive = false
|
||||
|
||||
detailPanelVC = fpc
|
||||
// Add FloatingPanel to self.view
|
||||
detailPanelVC.addPanel(toParent: mainVC, animated: true)
|
||||
fpc.addPanel(toParent: mainVC, animated: true)
|
||||
|
||||
case .showModal, .showTabBar:
|
||||
let modalVC = contentVC
|
||||
modalVC.modalPresentationStyle = .fullScreen
|
||||
@@ -57,8 +93,50 @@ final class UseCaseController: NSObject {
|
||||
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()
|
||||
self.addMainPanel(with: pageVC)
|
||||
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")
|
||||
@@ -80,6 +158,20 @@ final class UseCaseController: NSObject {
|
||||
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()
|
||||
@@ -96,6 +188,7 @@ final class UseCaseController: NSObject {
|
||||
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
|
||||
@@ -110,7 +203,6 @@ final class UseCaseController: NSObject {
|
||||
|
||||
case .showContainerMargins:
|
||||
let fpc = FloatingPanelController()
|
||||
|
||||
let appearance = SurfaceAppearance()
|
||||
appearance.cornerRadius = 38.5
|
||||
fpc.surfaceView.appearance = appearance
|
||||
@@ -126,81 +218,61 @@ final class UseCaseController: NSObject {
|
||||
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()
|
||||
case .showNavigationController:
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.contentInsetAdjustmentBehavior = .never
|
||||
fpc.set(contentViewController: contentVC)
|
||||
fpc.ext_trackScrollView(in: contentVC)
|
||||
addMain(panel: fpc)
|
||||
|
||||
let oldMainPanelVC = mainPanelVC
|
||||
case .showTopPositionedPanel: // For debug
|
||||
let fpc = FloatingPanelController(delegate: self)
|
||||
let contentVC = UIViewController()
|
||||
contentVC.view.backgroundColor = .red
|
||||
fpc.set(contentViewController: contentVC)
|
||||
addMain(panel: fpc)
|
||||
|
||||
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, *) {
|
||||
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 == .showAdaptivePanelWithCustomGuide) ? .withHeaderFooter : .onlyImage
|
||||
let layoutGuide = contentVC.layoutGuideFor(mode: mode)
|
||||
mainPanelVC.layout = ImageViewController.PanelLayout(targetGuide: layoutGuide)
|
||||
} else {
|
||||
mainPanelVC.layout = ImageViewController.PanelLayout(targetGuide: nil)
|
||||
fpc.layout = ImageViewController.PanelLayout(targetGuide: layoutGuide)
|
||||
}
|
||||
mainPanelVC.delegate = nil
|
||||
mainPanelVC.isRemovalInteractionEnabled = true
|
||||
mainPanelVC.track(scrollView: contentVC.scrollView)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
addMain(panel: fpc)
|
||||
|
||||
@objc
|
||||
fileprivate func handleSurface(tapGesture: UITapGestureRecognizer) {
|
||||
switch mainPanelVC.state {
|
||||
case .full:
|
||||
mainPanelVC.move(to: .half, animated: true)
|
||||
default:
|
||||
mainPanelVC.move(to: .full, animated: true)
|
||||
case .showAdaptivePanelWithCustomGuide:
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.isRemovalInteractionEnabled = true
|
||||
fpc.contentInsetAdjustmentBehavior = .always
|
||||
fpc.surfaceView.appearance = {
|
||||
let appearance = SurfaceAppearance()
|
||||
appearance.cornerRadius = 6.0
|
||||
return appearance
|
||||
}()
|
||||
|
||||
|
||||
fpc.set(contentViewController: contentVC)
|
||||
fpc.ext_trackScrollView(in: contentVC)
|
||||
fpc.layout = AdaptiveLayoutTestViewController.PanelLayout(targetGuide: contentVC.view.makeBoundsLayoutGuide())
|
||||
addMain(panel: fpc)
|
||||
|
||||
case .showCustomStatePanel:
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.delegate = self
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,11 +297,41 @@ final class UseCaseController: NSObject {
|
||||
// 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, #available(iOS 11.0, *) {
|
||||
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)
|
||||
}
|
||||
@@ -274,54 +376,45 @@ extension UseCaseController: FloatingPanelControllerDelegate {
|
||||
|
||||
extension UseCaseController: UIGestureRecognizerDelegate {
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
switch useCase {
|
||||
case .showNestedScrollView:
|
||||
if case .showNestedScrollView = useCase {
|
||||
return true
|
||||
default:
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return false
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
extension UseCase {
|
||||
func makeContentViewController(with storyboard: UIStoryboard) -> UIViewController {
|
||||
guard let storyboardID = self.storyboardID else { return DebugTableViewController() }
|
||||
return storyboard.instantiateViewController(withIdentifier: storyboardID)
|
||||
}
|
||||
private extension FloatingPanelController {
|
||||
func ext_trackScrollView(in contentVC: UIViewController) {
|
||||
switch contentVC {
|
||||
case let consoleVC as DebugTextViewController:
|
||||
track(scrollView: consoleVC.textView)
|
||||
|
||||
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 let contentVC as DebugTableViewController:
|
||||
let ob = contentVC.tableView.observe(\.isEditing) { [weak self] (tableView, _) in
|
||||
self?.panGestureRecognizer.isEnabled = !tableView.isEditing
|
||||
}
|
||||
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
|
||||
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 AdaptiveLayoutTestViewController:
|
||||
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 */
|
||||
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 */; };
|
||||
545BA70E21BA3217007F7846 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 545BA70D21BA3217007F7846 /* Assets.xcassets */; };
|
||||
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; };
|
||||
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>"; };
|
||||
545BA70721BA3214007F7846 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
|
||||
545BA70821BA3214007F7846 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
|
||||
545BA70721BA3214007F7846 /* MainViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; 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>"; };
|
||||
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>"; };
|
||||
@@ -44,6 +44,7 @@
|
||||
545BA71321BA3217007F7846 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
545BA72221BA3867007F7846 /* SamplesObjC-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SamplesObjC-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
545BA72521BA3BAF007F7846 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5D82A6AF28D18443006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -64,6 +65,7 @@
|
||||
545BA72521BA3BAF007F7846 /* FloatingPanel.framework */,
|
||||
545BA70321BA3214007F7846 /* SamplesObjC */,
|
||||
545BA70221BA3214007F7846 /* Products */,
|
||||
5D82A6AE28D18443006A44BA /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -78,13 +80,13 @@
|
||||
545BA70321BA3214007F7846 /* SamplesObjC */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
545BA70421BA3214007F7846 /* AppDelegate.h */,
|
||||
545BA70521BA3214007F7846 /* AppDelegate.m */,
|
||||
545BA70721BA3214007F7846 /* ViewController.h */,
|
||||
545BA70821BA3214007F7846 /* ViewController.m */,
|
||||
545BA70A21BA3214007F7846 /* Main.storyboard */,
|
||||
545BA70D21BA3217007F7846 /* Assets.xcassets */,
|
||||
545BA70F21BA3217007F7846 /* LaunchScreen.storyboard */,
|
||||
545BA70A21BA3214007F7846 /* Main.storyboard */,
|
||||
545BA70421BA3214007F7846 /* AppDelegate.h */,
|
||||
545BA70521BA3214007F7846 /* AppDelegate.m */,
|
||||
545BA70721BA3214007F7846 /* MainViewController.h */,
|
||||
545BA70821BA3214007F7846 /* MainViewController.m */,
|
||||
545BA71221BA3217007F7846 /* Info.plist */,
|
||||
545BA71321BA3217007F7846 /* main.m */,
|
||||
545BA72221BA3867007F7846 /* SamplesObjC-Bridging-Header.h */,
|
||||
@@ -92,6 +94,14 @@
|
||||
path = SamplesObjC;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5D82A6AE28D18443006A44BA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5D82A6AF28D18443006A44BA /* libswiftCoreGraphics.tbd */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -119,7 +129,7 @@
|
||||
545BA6F921BA3214007F7846 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1110;
|
||||
LastUpgradeCheck = 1300;
|
||||
ORGANIZATIONNAME = "Shin Yamamoto";
|
||||
TargetAttributes = {
|
||||
545BA70021BA3214007F7846 = {
|
||||
@@ -164,7 +174,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
545BA70921BA3214007F7846 /* ViewController.m in Sources */,
|
||||
545BA70921BA3214007F7846 /* MainViewController.m in Sources */,
|
||||
545BA71421BA3217007F7846 /* main.m in Sources */,
|
||||
545BA70621BA3214007F7846 /* AppDelegate.m in Sources */,
|
||||
);
|
||||
@@ -218,6 +228,7 @@
|
||||
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;
|
||||
@@ -277,6 +288,7 @@
|
||||
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;
|
||||
@@ -318,7 +330,11 @@
|
||||
"$(inherited)",
|
||||
"@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)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "SamplesObjC/SamplesObjC-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
@@ -340,7 +356,11 @@
|
||||
"$(inherited)",
|
||||
"@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)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "SamplesObjC/SamplesObjC-Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.2;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1110"
|
||||
LastUpgradeVersion = "1300"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<?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>
|
||||
<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="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<!--Main View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<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">
|
||||
<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"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
@import FloatingPanel;
|
||||
|
||||
@interface ViewController : UIViewController
|
||||
@interface MainViewController : UIViewController
|
||||
@end
|
||||
|
||||
@interface MyFloatingPanelLayout : NSObject <FloatingPanelLayout>
|
||||
+3
-4
@@ -1,6 +1,6 @@
|
||||
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||
|
||||
#import "ViewController.h"
|
||||
#import "MainViewController.h"
|
||||
@import FloatingPanel;
|
||||
|
||||
// Defining a custom FloatingPanelState
|
||||
@@ -19,17 +19,16 @@ static FloatingPanelState *_lastQuart;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface ViewController()<FloatingPanelControllerDelegate>
|
||||
@interface MainViewController()<FloatingPanelControllerDelegate>
|
||||
@end
|
||||
|
||||
@implementation ViewController
|
||||
@implementation MainViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
FloatingPanelController *fpc = [[FloatingPanelController alloc] init];
|
||||
[fpc setContentViewController:nil];
|
||||
[fpc trackScrollView:nil];
|
||||
[fpc setDelegate:self];
|
||||
|
||||
[fpc setLayout: [MyFloatingPanelLayout new]];
|
||||
@@ -8,12 +8,13 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
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 */; };
|
||||
548DF95B21705BE10041922A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 548DF95A21705BE10041922A /* Assets.xcassets */; };
|
||||
548DF95E21705BE10041922A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 548DF95C21705BE10041922A /* LaunchScreen.storyboard */; };
|
||||
549D23CF233C77CF008EF4D7 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23CE233C77CF008EF4D7 /* FloatingPanel.framework */; };
|
||||
549D23D0233C77CF008EF4D7 /* FloatingPanel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 549D23CE233C77CF008EF4D7 /* FloatingPanel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
5D82A6AA28D18432006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6A928D1842B006A44BA /* libswiftCoreGraphics.tbd */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -33,12 +34,13 @@
|
||||
/* Begin PBXFileReference section */
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
548DF95F21705BE10041922A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
549D23CE233C77CF008EF4D7 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5D82A6A928D1842B006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -46,6 +48,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5D82A6AA28D18432006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
|
||||
549D23CF233C77CF008EF4D7 /* FloatingPanel.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -59,6 +62,7 @@
|
||||
549D23CE233C77CF008EF4D7 /* FloatingPanel.framework */,
|
||||
548DF95221705BE00041922A /* Stocks */,
|
||||
548DF95121705BE00041922A /* Products */,
|
||||
5D82A6A828D1842A006A44BA /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -73,16 +77,24 @@
|
||||
548DF95221705BE00041922A /* Stocks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
548DF95321705BE00041922A /* AppDelegate.swift */,
|
||||
548DF95521705BE00041922A /* ViewController.swift */,
|
||||
548DF95721705BE00041922A /* Main.storyboard */,
|
||||
548DF95A21705BE10041922A /* Assets.xcassets */,
|
||||
548DF95C21705BE10041922A /* LaunchScreen.storyboard */,
|
||||
548DF95721705BE00041922A /* Main.storyboard */,
|
||||
548DF95321705BE00041922A /* AppDelegate.swift */,
|
||||
548DF95521705BE00041922A /* MainViewController.swift */,
|
||||
548DF95F21705BE10041922A /* Info.plist */,
|
||||
);
|
||||
path = Stocks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5D82A6A828D1842A006A44BA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5D82A6A928D1842B006A44BA /* libswiftCoreGraphics.tbd */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -111,7 +123,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1000;
|
||||
LastUpgradeCheck = 1000;
|
||||
LastUpgradeCheck = 1300;
|
||||
ORGANIZATIONNAME = scenee;
|
||||
TargetAttributes = {
|
||||
548DF94F21705BE00041922A = {
|
||||
@@ -155,7 +167,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
548DF95621705BE00041922A /* ViewController.swift in Sources */,
|
||||
548DF95621705BE00041922A /* MainViewController.swift in Sources */,
|
||||
548DF95421705BE00041922A /* AppDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -208,6 +220,7 @@
|
||||
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;
|
||||
@@ -269,6 +282,7 @@
|
||||
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;
|
||||
@@ -310,7 +324,11 @@
|
||||
"$(inherited)",
|
||||
"@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)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -329,7 +347,11 @@
|
||||
"$(inherited)",
|
||||
"@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)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1000"
|
||||
LastUpgradeVersion = "1300"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -27,8 +27,6 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
@@ -38,8 +36,8 @@
|
||||
ReferencedContainer = "container:Stocks.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -61,8 +59,6 @@
|
||||
ReferencedContainer = "container:Stocks.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
<?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">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<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>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/>
|
||||
<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-->
|
||||
<!--Main View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<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">
|
||||
<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"/>
|
||||
<subviews>
|
||||
<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>
|
||||
<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"/>
|
||||
@@ -34,10 +30,10 @@
|
||||
</constraints>
|
||||
</scrollView>
|
||||
<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>
|
||||
<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"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="44.5" id="B5t-ZF-qUj"/>
|
||||
@@ -53,10 +49,10 @@
|
||||
</constraints>
|
||||
</view>
|
||||
<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>
|
||||
<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"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -70,6 +66,7 @@
|
||||
</subviews>
|
||||
</stackView>
|
||||
</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"/>
|
||||
<constraints>
|
||||
<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="6Tk-OE-BBY" firstAttribute="bottom" secondItem="NKr-gS-mpx" secondAttribute="bottom" id="yeu-NH-Pmp"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="bottomToolView" destination="dFl-81-6ok" id="NXn-af-lFv"/>
|
||||
@@ -122,6 +118,7 @@
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="INo-op-FLO"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<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="leading" secondItem="INo-op-FLO" secondAttribute="leading" id="na8-TO-WiG"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="INo-op-FLO"/>
|
||||
</view>
|
||||
<size key="freeformSize" width="375" height="600"/>
|
||||
<connections>
|
||||
|
||||
+7
-9
@@ -3,7 +3,7 @@
|
||||
import UIKit
|
||||
import FloatingPanel
|
||||
|
||||
class ViewController: UIViewController, FloatingPanelControllerDelegate {
|
||||
class MainViewController: UIViewController, FloatingPanelControllerDelegate {
|
||||
@IBOutlet var topBannerView: UIImageView!
|
||||
@IBOutlet weak var labelStackView: UIStackView!
|
||||
@IBOutlet weak var bottomToolView: UIView!
|
||||
@@ -101,14 +101,12 @@ class FloatingPanelStocksLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .tip
|
||||
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 56.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 262.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
/* Visible + ToolView */
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 85.0 + 44.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 56.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 262.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
/* Visible + ToolView */
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 85.0 + 44.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
|
||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||
return 0.0
|
||||
@@ -1,20 +1,20 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "FloatingPanel"
|
||||
s.version = "2.4.1"
|
||||
s.version = "2.8.1"
|
||||
s.summary = "FloatingPanel is a clean and easy-to-use UI component of a floating panel interface."
|
||||
s.description = <<-DESC
|
||||
FloatingPanel is a clean and easy-to-use UI component for a new interface introduced in Apple Maps, Shortcuts and Stocks app.
|
||||
The new interface displays the related contents and utilities in parallel as a user wants.
|
||||
DESC
|
||||
s.homepage = "https://github.com/SCENEE/FloatingPanel"
|
||||
s.homepage = "https://github.com/scenee/FloatingPanel"
|
||||
s.author = "Shin Yamamoto"
|
||||
s.social_media_url = "https://twitter.com/scenee"
|
||||
|
||||
s.platform = :ios, "10.0"
|
||||
s.source = { :git => "https://github.com/SCENEE/FloatingPanel.git", :tag => s.version.to_s }
|
||||
s.platform = :ios, "11.0"
|
||||
s.source = { :git => "https://github.com/scenee/FloatingPanel.git", :tag => s.version.to_s }
|
||||
s.source_files = "Sources/*.swift"
|
||||
s.swift_versions = ['5.1', '5.2', '5.3']
|
||||
s.swift_version = '5.0'
|
||||
|
||||
s.framework = "UIKit"
|
||||
|
||||
|
||||
@@ -17,22 +17,23 @@
|
||||
545DB9E021511AC100CA77B8 /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9DF21511AC100CA77B8 /* Controller.swift */; };
|
||||
545DBA2B2152383100CA77B8 /* GrabberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DBA2A2152383100CA77B8 /* GrabberView.swift */; };
|
||||
546055BF2333C4740069F400 /* TestSupports.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542753C722C49A8F00D17955 /* TestSupports.swift */; };
|
||||
5469F4A224B003EF00537F8A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5469F49F24B003EF00537F8A /* LaunchScreen.storyboard */; };
|
||||
5469F4A324B003EF00537F8A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4A024B003EF00537F8A /* AppDelegate.swift */; };
|
||||
5469F4AE24B30D7E00537F8A /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4AD24B30D7E00537F8A /* State.swift */; };
|
||||
5469F4B024B30E1500537F8A /* LayoutAnchoring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4AF24B30E1500537F8A /* LayoutAnchoring.swift */; };
|
||||
5469F4B224B30F1100537F8A /* Position.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4B124B30F1100537F8A /* Position.swift */; };
|
||||
5469F4B424B30F3500537F8A /* LayoutReferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4B324B30F3500537F8A /* LayoutReferences.swift */; };
|
||||
5469F4B424B30F3500537F8A /* LayoutProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5469F4B324B30F3500537F8A /* LayoutProperties.swift */; };
|
||||
547F7A9C2A6E946000303905 /* GestureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 547F7A9B2A6E946000303905 /* GestureTests.swift */; };
|
||||
549C371F2361E15E007D8058 /* ExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549C371E2361E15D007D8058 /* ExtensionTests.swift */; };
|
||||
549E944522CF295D0050AECF /* StateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549E944422CF295D0050AECF /* StateTests.swift */; };
|
||||
54A6B6B122968B530077F348 /* CoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A6B6B022968B530077F348 /* CoreTests.swift */; };
|
||||
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 */; };
|
||||
54CDC5D5215B6D8D007D205C /* BackdropView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D4215B6D8D007D205C /* BackdropView.swift */; };
|
||||
54CFBFC3215CD045006B5735 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CFBFC2215CD045006B5735 /* Layout.swift */; };
|
||||
54CFBFC5215CD09C006B5735 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CFBFC4215CD09C006B5735 /* Core.swift */; };
|
||||
54DBA3DC262E938500D75969 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54DBA3DB262E938500D75969 /* Extensions.swift */; };
|
||||
54E3992727141F5100A8F9ED /* FloatingPanel.docc in Sources */ = {isa = PBXBuildFile; fileRef = 54E3992627141F5100A8F9ED /* FloatingPanel.docc */; };
|
||||
5D82A6B528D18464006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6B428D18461006A44BA /* libswiftCoreGraphics.tbd */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -43,13 +44,6 @@
|
||||
remoteGlobalIDString = 545DB9C02151169500CA77B8;
|
||||
remoteInfo = FloatingModalController;
|
||||
};
|
||||
54E740DC218AFE9F005C1A34 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 545DB9B82151169500CA77B8 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 54E740C9218AFD67005C1A34;
|
||||
remoteInfo = TestingHost;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -66,24 +60,23 @@
|
||||
545DB9D12151169500CA77B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
545DB9DF21511AC100CA77B8 /* Controller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Controller.swift; sourceTree = "<group>"; };
|
||||
545DBA2A2152383100CA77B8 /* GrabberView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrabberView.swift; sourceTree = "<group>"; };
|
||||
5469F49F24B003EF00537F8A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
5469F4A024B003EF00537F8A /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
5469F4A124B003EF00537F8A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
5469F4AD24B30D7E00537F8A /* State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = "<group>"; };
|
||||
5469F4AF24B30E1500537F8A /* LayoutAnchoring.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutAnchoring.swift; sourceTree = "<group>"; };
|
||||
5469F4B124B30F1100537F8A /* Position.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Position.swift; sourceTree = "<group>"; };
|
||||
5469F4B324B30F3500537F8A /* LayoutReferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutReferences.swift; sourceTree = "<group>"; };
|
||||
5469F4B324B30F3500537F8A /* LayoutProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutProperties.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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 */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -91,6 +84,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5D82A6B528D18464006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -102,13 +96,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
54E740C7218AFD67005C1A34 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
@@ -118,6 +105,7 @@
|
||||
545DB9C32151169500CA77B8 /* Sources */,
|
||||
545DB9CE2151169500CA77B8 /* Tests */,
|
||||
545DB9C22151169500CA77B8 /* Products */,
|
||||
5D82A6B328D18460006A44BA /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -126,7 +114,6 @@
|
||||
children = (
|
||||
545DB9C12151169500CA77B8 /* FloatingPanel.framework */,
|
||||
545DB9CA2151169500CA77B8 /* FloatingPanelTests.xctest */,
|
||||
54E740CA218AFD67005C1A34 /* FloatingPanelTesting.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -139,7 +126,7 @@
|
||||
5469F4B124B30F1100537F8A /* Position.swift */,
|
||||
54CFBFC4215CD09C006B5735 /* Core.swift */,
|
||||
54CFBFC2215CD045006B5735 /* Layout.swift */,
|
||||
5469F4B324B30F3500537F8A /* LayoutReferences.swift */,
|
||||
5469F4B324B30F3500537F8A /* LayoutProperties.swift */,
|
||||
5469F4AF24B30E1500537F8A /* LayoutAnchoring.swift */,
|
||||
5450EEE321646DF500135936 /* Behavior.swift */,
|
||||
54352E9721A521CA00CBCA08 /* PassthroughView.swift */,
|
||||
@@ -148,9 +135,10 @@
|
||||
545DBA2A2152383100CA77B8 /* GrabberView.swift */,
|
||||
54352E9521A51A2500CBCA08 /* Transitioning.swift */,
|
||||
54DBA3DB262E938500D75969 /* Extensions.swift */,
|
||||
54ABD7AE216CCFF7002E6C13 /* Logger.swift */,
|
||||
54ABD7AE216CCFF7002E6C13 /* Logging.swift */,
|
||||
545DB9C42151169500CA77B8 /* FloatingPanel.h */,
|
||||
545DB9C52151169500CA77B8 /* Info.plist */,
|
||||
54E3992627141F5100A8F9ED /* FloatingPanel.docc */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
@@ -158,9 +146,9 @@
|
||||
545DB9CE2151169500CA77B8 /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5469F49E24B003EF00537F8A /* TestingApp */,
|
||||
54A6B6B022968B530077F348 /* CoreTests.swift */,
|
||||
545DB9CF2151169500CA77B8 /* ControllerTests.swift */,
|
||||
547F7A9B2A6E946000303905 /* GestureTests.swift */,
|
||||
542753C522C49A6E00D17955 /* LayoutTests.swift */,
|
||||
54A6B6B72296A8520077F348 /* SurfaceViewTests.swift */,
|
||||
549E944422CF295D0050AECF /* StateTests.swift */,
|
||||
@@ -171,14 +159,12 @@
|
||||
path = Tests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5469F49E24B003EF00537F8A /* TestingApp */ = {
|
||||
5D82A6B328D18460006A44BA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5469F49F24B003EF00537F8A /* LaunchScreen.storyboard */,
|
||||
5469F4A024B003EF00537F8A /* AppDelegate.swift */,
|
||||
5469F4A124B003EF00537F8A /* Info.plist */,
|
||||
5D82A6B428D18461006A44BA /* libswiftCoreGraphics.tbd */,
|
||||
);
|
||||
path = TestingApp;
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
@@ -225,30 +211,12 @@
|
||||
);
|
||||
dependencies = (
|
||||
545DB9CD2151169500CA77B8 /* PBXTargetDependency */,
|
||||
54E740DD218AFE9F005C1A34 /* PBXTargetDependency */,
|
||||
);
|
||||
name = FloatingPanelTests;
|
||||
productName = FloatingModalControllerTests;
|
||||
productReference = 545DB9CA2151169500CA77B8 /* FloatingPanelTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
54E740C9218AFD67005C1A34 /* TestingApp */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 54E740D9218AFD6A005C1A34 /* Build configuration list for PBXNativeTarget "TestingApp" */;
|
||||
buildPhases = (
|
||||
54E740C6218AFD67005C1A34 /* Sources */,
|
||||
54E740C7218AFD67005C1A34 /* Frameworks */,
|
||||
54E740C8218AFD67005C1A34 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = TestingApp;
|
||||
productName = TestingHost;
|
||||
productReference = 54E740CA218AFD67005C1A34 /* FloatingPanelTesting.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@@ -256,7 +224,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1010;
|
||||
LastUpgradeCheck = 1000;
|
||||
LastUpgradeCheck = 1300;
|
||||
ORGANIZATIONNAME = scenee;
|
||||
TargetAttributes = {
|
||||
545DB9C02151169500CA77B8 = {
|
||||
@@ -265,10 +233,6 @@
|
||||
};
|
||||
545DB9C92151169500CA77B8 = {
|
||||
CreatedOnToolsVersion = 10.0;
|
||||
TestTargetID = 54E740C9218AFD67005C1A34;
|
||||
};
|
||||
54E740C9218AFD67005C1A34 = {
|
||||
CreatedOnToolsVersion = 10.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -287,7 +251,6 @@
|
||||
targets = (
|
||||
545DB9C02151169500CA77B8 /* FloatingPanel */,
|
||||
545DB9C92151169500CA77B8 /* FloatingPanelTests */,
|
||||
54E740C9218AFD67005C1A34 /* TestingApp */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -307,14 +270,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
54E740C8218AFD67005C1A34 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5469F4A224B003EF00537F8A /* LaunchScreen.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -326,15 +281,16 @@
|
||||
5469F4B024B30E1500537F8A /* LayoutAnchoring.swift in Sources */,
|
||||
54CDC5D3215B6D5A007D205C /* SurfaceView.swift in Sources */,
|
||||
54CFBFC3215CD045006B5735 /* Layout.swift in Sources */,
|
||||
5469F4B424B30F3500537F8A /* LayoutReferences.swift in Sources */,
|
||||
5469F4B424B30F3500537F8A /* LayoutProperties.swift in Sources */,
|
||||
54CDC5D5215B6D8D007D205C /* BackdropView.swift in Sources */,
|
||||
54352E9821A521CA00CBCA08 /* PassthroughView.swift in Sources */,
|
||||
54CFBFC5215CD09C006B5735 /* Core.swift in Sources */,
|
||||
54ABD7AF216CCFF7002E6C13 /* Logger.swift in Sources */,
|
||||
54ABD7AF216CCFF7002E6C13 /* Logging.swift in Sources */,
|
||||
545DB9E021511AC100CA77B8 /* Controller.swift in Sources */,
|
||||
54DBA3DC262E938500D75969 /* Extensions.swift in Sources */,
|
||||
5450EEE421646DF500135936 /* Behavior.swift in Sources */,
|
||||
545DBA2B2152383100CA77B8 /* GrabberView.swift in Sources */,
|
||||
54E3992727141F5100A8F9ED /* FloatingPanel.docc in Sources */,
|
||||
54352E9621A51A2500CBCA08 /* Transitioning.swift in Sources */,
|
||||
5469F4AE24B30D7E00537F8A /* State.swift in Sources */,
|
||||
);
|
||||
@@ -351,14 +307,7 @@
|
||||
542753C622C49A6E00D17955 /* LayoutTests.swift in Sources */,
|
||||
54A6B6B82296A8520077F348 /* SurfaceViewTests.swift in Sources */,
|
||||
546055BF2333C4740069F400 /* TestSupports.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
54E740C6218AFD67005C1A34 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5469F4A324B003EF00537F8A /* AppDelegate.swift in Sources */,
|
||||
547F7A9C2A6E946000303905 /* GestureTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -370,11 +319,6 @@
|
||||
target = 545DB9C02151169500CA77B8 /* FloatingPanel */;
|
||||
targetProxy = 545DB9CC2151169500CA77B8 /* PBXContainerItemProxy */;
|
||||
};
|
||||
54E740DD218AFE9F005C1A34 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 54E740C9218AFD67005C1A34 /* TestingApp */;
|
||||
targetProxy = 54E740DC218AFE9F005C1A34 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
@@ -404,6 +348,7 @@
|
||||
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;
|
||||
@@ -469,6 +414,7 @@
|
||||
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;
|
||||
@@ -516,7 +462,7 @@
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = Sources/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -547,7 +493,7 @@
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = Sources/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -569,17 +515,16 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalControllerTests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FloatingPanelTesting.app/FloatingPanelTesting";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -590,53 +535,16 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalControllerTests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FloatingPanelTesting.app/FloatingPanelTesting";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
54E740DA218AFD6A005C1A34 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = Tests/TestingApp/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTesting;
|
||||
PRODUCT_NAME = FloatingPanelTesting;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
54E740DB218AFD6A005C1A34 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = Tests/TestingApp/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTesting;
|
||||
PRODUCT_NAME = FloatingPanelTesting;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -666,6 +574,7 @@
|
||||
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;
|
||||
@@ -718,7 +627,7 @@
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = Sources/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -727,7 +636,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanel;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "TEST DEBUG __FP_LOG";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "TEST DEBUG FP_LOG";
|
||||
SWIFT_COMPILATION_MODE = singlefile;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@@ -742,31 +651,16 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingModalControllerTests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FloatingPanelTesting.app/FloatingPanelTesting";
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
54E8AC6A2286CFB6000C5A12 /* Test */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = Tests/TestingApp/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTesting;
|
||||
PRODUCT_NAME = FloatingPanelTesting;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Test;
|
||||
};
|
||||
@@ -803,16 +697,6 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
54E740D9218AFD6A005C1A34 /* Build configuration list for PBXNativeTarget "TestingApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
54E740DA218AFD6A005C1A34 /* Debug */,
|
||||
54E740DB218AFD6A005C1A34 /* Release */,
|
||||
54E8AC6A2286CFB6000C5A12 /* Test */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 545DB9B82151169500CA77B8 /* Project object */;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1000"
|
||||
LastUpgradeVersion = "1300"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -27,6 +27,15 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "545DB9C02151169500CA77B8"
|
||||
BuildableName = "FloatingPanel.framework"
|
||||
BlueprintName = "FloatingPanel"
|
||||
ReferencedContainer = "container:FloatingPanel.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
@@ -41,17 +50,6 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "545DB9C02151169500CA77B8"
|
||||
BuildableName = "FloatingPanel.framework"
|
||||
BlueprintName = "FloatingPanel"
|
||||
ReferencedContainer = "container:FloatingPanel.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -72,8 +70,6 @@
|
||||
ReferencedContainer = "container:FloatingPanel.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
<Group
|
||||
location = "group:Examples"
|
||||
name = "Examples">
|
||||
<FileRef
|
||||
location = "group:Maps-SwiftUI/Maps-SwiftUI.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Maps/Maps.xcodeproj">
|
||||
</FileRef>
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "FloatingPanel",
|
||||
platforms: [
|
||||
.iOS(.v10)
|
||||
.iOS(.v11)
|
||||
],
|
||||
products: [
|
||||
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
[](https://travis-ci.org/SCENEE/FloatingPanel)
|
||||
[](https://cocoapods.org/pods/FloatingPanel)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](https://swift.org/)
|
||||
[](https://cocoapods.org/pods/FloatingPanel)
|
||||
[](https://swift.org/)
|
||||
[](https://cocoapods.org/pods/FloatingPanel)
|
||||

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

|
||||

|
||||
@@ -19,47 +21,47 @@ The new interface displays the related contents and utilities in parallel as a u
|
||||
- [Features](#features)
|
||||
- [Requirements](#requirements)
|
||||
- [Installation](#installation)
|
||||
- [CocoaPods](#cocoapods)
|
||||
- [Carthage](#carthage)
|
||||
- [Swift Package Manager](#swift-package-manager)
|
||||
- [CocoaPods](#cocoapods)
|
||||
- [Carthage](#carthage)
|
||||
- [Swift Package Manager](#swift-package-manager)
|
||||
- [Getting Started](#getting-started)
|
||||
- [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)
|
||||
- [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)
|
||||
- [View hierarchy](#view-hierarchy)
|
||||
- [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)
|
||||
- [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)
|
||||
- [Change the initial layout](#change-the-initial-layout)
|
||||
- [Update your panel layout](#update-your-panel-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)
|
||||
- [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)
|
||||
- [Using custome panel states](#using-custome-panel-states)
|
||||
- [Customize the behavior with `FloatingPanelBehavior` protocol](#customize-the-behavior-with-floatingpanelbehavior-protocol)
|
||||
- [Modify your floating panel's interaction](#modify-your-floating-panels-interaction)
|
||||
- [Activate the rubber-band effect on panel edges](#activate-the-rubber-band-effect-on-panel-edges)
|
||||
- [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)
|
||||
- [Customize the surface design](#customize-the-surface-design)
|
||||
- [Modify your surface appearance](#modify-your-surface-appearance)
|
||||
- [Use a custom grabber handle](#use-a-custom-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 margins of the surface edges](#customize-margins-of-the-surface-edges)
|
||||
- [Customize gestures](#customize-gestures)
|
||||
- [Suppress the panel interaction](#suppress-the-panel-interaction)
|
||||
- [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)
|
||||
- [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)
|
||||
- [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)
|
||||
- [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)
|
||||
- [Customize the layout with `FloatingPanelLayout` protocol](#customize-the-layout-with-floatingpanellayout-protocol)
|
||||
- [Change the initial layout](#change-the-initial-layout)
|
||||
- [Update your panel layout](#update-your-panel-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)
|
||||
- [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)
|
||||
- [Using custome panel states](#using-custome-panel-states)
|
||||
- [Customize the behavior with `FloatingPanelBehavior` protocol](#customize-the-behavior-with-floatingpanelbehavior-protocol)
|
||||
- [Modify your floating panel's interaction](#modify-your-floating-panels-interaction)
|
||||
- [Activate the rubber-band effect on panel edges](#activate-the-rubber-band-effect-on-panel-edges)
|
||||
- [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)
|
||||
- [Customize the surface design](#customize-the-surface-design)
|
||||
- [Modify your surface appearance](#modify-your-surface-appearance)
|
||||
- [Use a custom grabber handle](#use-a-custom-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 margins of the surface edges](#customize-margins-of-the-surface-edges)
|
||||
- [Customize gestures](#customize-gestures)
|
||||
- [Suppress the panel interaction](#suppress-the-panel-interaction)
|
||||
- [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)
|
||||
- [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)
|
||||
- [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)
|
||||
- ['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)
|
||||
- [FloatingPanelSurfaceView's issue on iOS 10](#floatingpanelsurfaceviews-issue-on-ios-10)
|
||||
- ['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)
|
||||
- [Maintainer](#maintainer)
|
||||
- [License](#license)
|
||||
|
||||
@@ -73,14 +75,14 @@ The new interface displays the related contents and utilities in parallel as a u
|
||||
- [x] Removal interaction
|
||||
- [x] Multi panel support
|
||||
- [x] Modal presentation
|
||||
- [x] 4 positioning support(top, left, bottom, right)
|
||||
- [x] Support for 4 positions (top, left, bottom, right)
|
||||
- [x] 1 or more magnetic anchors(full, half, tip and more)
|
||||
- [x] Layout support for all trait environments(i.e. Landscape orientation)
|
||||
- [x] Common UI elements: surface, backdrop and grabber handle
|
||||
- [x] Free from common issues of Auto Layout and gesture handling
|
||||
- [x] Free from common Auto Layout and gesture handling issues
|
||||
- [x] Compatible with Objective-C
|
||||
|
||||
Examples are here.
|
||||
Examples can be found here:
|
||||
|
||||
- [Examples/Maps](https://github.com/SCENEE/FloatingPanel/tree/master/Examples/Maps) like Apple Maps.app.
|
||||
- [Examples/Stocks](https://github.com/SCENEE/FloatingPanel/tree/master/Examples/Stocks) like Apple Stocks.app.
|
||||
@@ -89,11 +91,7 @@ Examples are here.
|
||||
|
||||
## Requirements
|
||||
|
||||
FloatingPanel is written in Swift 5.0+. 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.
|
||||
FloatingPanel is written in Swift 5.0+ and compatible with iOS 11.0+.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -106,8 +104,6 @@ it, simply add the following line to your Podfile:
|
||||
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`:
|
||||
@@ -166,7 +162,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.
|
||||
|
||||
: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
|
||||
|
||||
@@ -250,7 +247,8 @@ fpc.contentMode = .fitToBounds
|
||||
|
||||
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
|
||||
|
||||
@@ -267,13 +265,11 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
|
||||
class MyFloatingPanelLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .tip
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -288,6 +284,8 @@ fpc.layout = MyPanelLayout()
|
||||
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.
|
||||
|
||||
```swift
|
||||
@@ -317,12 +315,11 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
|
||||
class LandscapePanelLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .tip
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
|
||||
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
||||
return [
|
||||
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 8.0),
|
||||
@@ -341,17 +338,16 @@ class LandscapePanelLayout: FloatingPanelLayout {
|
||||
class IntrinsicPanelLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .full
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
|
||||
]
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
: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
|
||||
|
||||
@@ -360,18 +356,16 @@ Use `.superview` reference guide in your anchors.
|
||||
```swift
|
||||
class MyFullScreenLayout: FloatingPanelLayout {
|
||||
...
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .superview),
|
||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
|
||||
}
|
||||
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .superview),
|
||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
:pencil2: `FloatingPanelFullScreenLayout` is deprecated on v1.
|
||||
> [!WARNING]
|
||||
> `FloatingPanelFullScreenLayout` is deprecated on v1.
|
||||
|
||||
#### Change the backdrop alpha
|
||||
|
||||
@@ -429,13 +423,14 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
|
||||
class CustomPanelBehavior: FloatingPanelBehavior {
|
||||
let springDecelerationRate = UIScrollView.DecelerationRate.fast.rawValue + 0.02
|
||||
let springResponseTime = 0.4
|
||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedTargetPosition: FloatingPanelState) -> Bool {
|
||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedState: FloatingPanelState) -> Bool {
|
||||
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
|
||||
|
||||
@@ -455,7 +450,7 @@ This allows full projectional panel behavior. For example, a user can swipe up a
|
||||
```swift
|
||||
class MyPanelBehavior: FloatingPanelBehavior {
|
||||
...
|
||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedTargetPosition: FloatingPanelPosition) -> Bool {
|
||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedState: FloatingPanelPosition) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -477,7 +472,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
|
||||
|
||||
@@ -518,7 +514,8 @@ fpc.surfaceView.grabberHandlePadding = 10.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
|
||||
|
||||
@@ -667,6 +664,24 @@ The tap-to-dismiss action is disabled by default. So it needs to be enabled as b
|
||||
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
|
||||
|
||||
### 'Show' or 'Show Detail' Segues from `FloatingPanelController`'s content view controller
|
||||
@@ -707,21 +722,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).
|
||||
|
||||
### 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
|
||||
|
||||
Shin Yamamoto <shin@scenee.com> | [@scenee](https://twitter.com/scenee)
|
||||
|
||||
@@ -7,5 +7,19 @@ import UIKit
|
||||
public class BackdropView: UIView {
|
||||
|
||||
/// The gesture recognizer for tap gestures to dismiss a panel.
|
||||
@objc public var dismissalTapGestureRecognizer: UITapGestureRecognizer!
|
||||
///
|
||||
/// By default, this gesture recognizer is disabled as following the default behavior of iOS modalities.
|
||||
/// To dismiss a panel by tap gestures on the backdrop, `dismissalTapGestureRecognizer.isEnabled` is set to true.
|
||||
@objc public var dismissalTapGestureRecognizer: UITapGestureRecognizer
|
||||
|
||||
init() {
|
||||
dismissalTapGestureRecognizer = UITapGestureRecognizer()
|
||||
dismissalTapGestureRecognizer.isEnabled = false
|
||||
super.init(frame: .zero)
|
||||
addGestureRecognizer(dismissalTapGestureRecognizer)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,16 @@ public protocol FloatingPanelBehavior {
|
||||
@objc optional
|
||||
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).
|
||||
/// Therefore, `proposedTargetPosition` can only be `FloatingPanelState.tip` or `FloatingPanelState.full`.
|
||||
/// The default implementation of this method returns `false`. This method is called for called
|
||||
/// for all states defined by the current layout object.
|
||||
@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.
|
||||
///
|
||||
@@ -46,7 +50,7 @@ public protocol FloatingPanelBehavior {
|
||||
|
||||
/// 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
|
||||
var removalInteractionVelocityThreshold: CGFloat { get }
|
||||
}
|
||||
|
||||
+134
-75
@@ -1,6 +1,7 @@
|
||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
/// 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
|
||||
@@ -37,9 +38,9 @@ import UIKit
|
||||
@objc optional
|
||||
func floatingPanelShouldBeginDragging(_ fpc: FloatingPanelController) -> Bool
|
||||
|
||||
/// Called when the user drags the surface or the surface is attracted to a state anchor.
|
||||
/// Called while the user drags the surface or the surface moves to a state anchor.
|
||||
@objc optional
|
||||
func floatingPanelDidMove(_ fpc: FloatingPanelController) // any surface frame changes in dragging
|
||||
func floatingPanelDidMove(_ fpc: FloatingPanelController)
|
||||
|
||||
/// Called on start of dragging (may require some time and or distance to move)
|
||||
@objc optional
|
||||
@@ -51,7 +52,12 @@ import UIKit
|
||||
|
||||
/// 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
|
||||
func floatingPanelDidEndDragging(_ fpc: FloatingPanelController, willAttract attract: Bool)
|
||||
|
||||
@@ -65,7 +71,7 @@ import UIKit
|
||||
|
||||
/// 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.
|
||||
@objc(floatingPanel:shouldRemoveAtLocation:withVelocity:)
|
||||
optional
|
||||
@@ -90,6 +96,35 @@ import UIKit
|
||||
@objc(floatingPanel:contentOffsetForPinningScrollView:)
|
||||
optional
|
||||
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
|
||||
}
|
||||
|
||||
///
|
||||
@@ -114,7 +149,7 @@ open class FloatingPanelController: UIViewController {
|
||||
}
|
||||
|
||||
/// The delegate of a panel controller object.
|
||||
@objc
|
||||
@objc
|
||||
public weak var delegate: FloatingPanelControllerDelegate?{
|
||||
didSet{
|
||||
didUpdateDelegate()
|
||||
@@ -157,26 +192,31 @@ open class FloatingPanelController: UIViewController {
|
||||
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
|
||||
public var layout: FloatingPanelLayout {
|
||||
get { _layout }
|
||||
set {
|
||||
_layout = newValue
|
||||
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
|
||||
public var behavior: FloatingPanelBehavior {
|
||||
get { _behavior }
|
||||
set {
|
||||
_behavior = newValue
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,8 +229,8 @@ open class FloatingPanelController: UIViewController {
|
||||
|
||||
/// 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.
|
||||
@objc
|
||||
/// 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
|
||||
public var contentInsetAdjustmentBehavior: ContentInsetAdjustmentBehavior = .always
|
||||
|
||||
/// A Boolean value that determines whether the removal interaction is enabled.
|
||||
@@ -210,7 +250,7 @@ open class FloatingPanelController: UIViewController {
|
||||
/// The NearbyState determines that finger's nearby state.
|
||||
public var nearbyState: FloatingPanelState {
|
||||
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.
|
||||
@@ -290,19 +330,19 @@ open class FloatingPanelController: UIViewController {
|
||||
|
||||
open override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
if #available(iOS 11.0, *) {
|
||||
// Ensure to update the static constraint of a panel after rotating a device in static mode
|
||||
if contentMode == .static {
|
||||
floatingPanel.layoutAdapter.updateStaticConstraint()
|
||||
}
|
||||
} else {
|
||||
// Because {top,bottom}LayoutGuide is managed as a view
|
||||
if floatingPanel.isAttracting == false {
|
||||
self.update(safeAreaInsets: fp_safeAreaInsets)
|
||||
}
|
||||
// Ensure to update the static constraint of a panel after rotating a device in static mode
|
||||
if contentMode == .static {
|
||||
floatingPanel.layoutAdapter.updateStaticConstraint()
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
|
||||
@@ -313,7 +353,7 @@ open class FloatingPanelController: UIViewController {
|
||||
// Change a layout for the new view size
|
||||
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: size) {
|
||||
layout = newLayout
|
||||
activateLayout(forceLayout: false)
|
||||
activateLayout(forceLayout: true)
|
||||
}
|
||||
|
||||
if view.translatesAutoresizingMaskIntoConstraints {
|
||||
@@ -332,7 +372,7 @@ open class FloatingPanelController: UIViewController {
|
||||
// Change a layout for the new trait collection
|
||||
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: newCollection) {
|
||||
self.layout = newLayout
|
||||
activateLayout(forceLayout: false)
|
||||
activateLayout(forceLayout: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,7 +412,7 @@ open class FloatingPanelController: UIViewController {
|
||||
preSafeAreaInsets != safeAreaInsets
|
||||
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()
|
||||
preSafeAreaInsets = safeAreaInsets
|
||||
@@ -384,6 +424,7 @@ open class FloatingPanelController: UIViewController {
|
||||
}
|
||||
|
||||
floatingPanel.layoutAdapter.updateStaticConstraint()
|
||||
floatingPanel.adjustScrollContentInsetIfNeeded()
|
||||
|
||||
if let contentOffset = contentOffset {
|
||||
trackingScrollView?.contentOffset = contentOffset
|
||||
@@ -398,8 +439,10 @@ open class FloatingPanelController: UIViewController {
|
||||
}
|
||||
|
||||
private func activateLayout(forceLayout: Bool = false) {
|
||||
floatingPanel.activateLayout(forceLayout: forceLayout,
|
||||
contentInsetAdjustmentBehavior: contentInsetAdjustmentBehavior)
|
||||
floatingPanel.activateLayout(
|
||||
forceLayout: forceLayout,
|
||||
contentInsetAdjustmentBehavior: contentInsetAdjustmentBehavior
|
||||
)
|
||||
}
|
||||
|
||||
func remove() {
|
||||
@@ -409,8 +452,15 @@ open class FloatingPanelController: UIViewController {
|
||||
guard let self = self else { return }
|
||||
self.delegate?.floatingPanelDidRemove?(self)
|
||||
}
|
||||
} else {
|
||||
} else if parent != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,22 +475,23 @@ open class FloatingPanelController: UIViewController {
|
||||
// Must apply the current layout here
|
||||
activateLayout(forceLayout: true)
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
// Must track the safeAreaInsets of `self.view` to update the layout.
|
||||
// There are 2 reasons.
|
||||
// 1. This or the parent VC doesn't call viewSafeAreaInsetsDidChange() on the bottom
|
||||
// inset's update expectedly.
|
||||
// 2. The safe area top inset can be variable on the large title navigation bar(iOS11+).
|
||||
// That's why it needs the observation to keep `adjustedContentInsets` correct.
|
||||
safeAreaInsetsObservation = self.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
|
||||
// is reported in https://github.com/SCENEE/FloatingPanel/issues/330
|
||||
guard let self = self, change.oldValue != self.view.safeAreaInsets else { return }
|
||||
self.update(safeAreaInsets: self.view.safeAreaInsets)
|
||||
}
|
||||
} else {
|
||||
// KVOs for topLayoutGuide & bottomLayoutGuide are not effective.
|
||||
// Instead, update(safeAreaInsets:) is called at `viewDidLayoutSubviews()`
|
||||
// Must track the safeAreaInsets of `self.view` to update the layout.
|
||||
// There are 2 reasons.
|
||||
// 1. This or the parent VC doesn't call viewSafeAreaInsetsDidChange() on the bottom
|
||||
// inset's update expectedly.
|
||||
// 2. The safe area top inset can be variable on the large title navigation bar(iOS11+).
|
||||
// That's why it needs the observation to keep `adjustedContentInsets` correct.
|
||||
safeAreaInsetsObservation = self.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
|
||||
// is reported in https://github.com/SCENEE/FloatingPanel/issues/330
|
||||
guard let self = self, change.oldValue != self.view.safeAreaInsets else { return }
|
||||
|
||||
// Sometimes the bounding rectangle of the controlled view becomes invalid when the screen is rotated.
|
||||
// This results in its safeAreaInsets change. In that case, `self.update(safeAreaInsets:)` leads
|
||||
// an unsatisfied constraints error. So this method should not be called with those bounds.
|
||||
guard self.view.bounds.height > 0 && self.view.bounds.width > 0 else { return }
|
||||
|
||||
self.update(safeAreaInsets: self.view.safeAreaInsets)
|
||||
}
|
||||
|
||||
move(to: floatingPanel.layoutAdapter.initialState,
|
||||
@@ -465,7 +516,7 @@ open class FloatingPanelController: UIViewController {
|
||||
@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 {
|
||||
log.warning("Already added to a parent(\(parent))")
|
||||
os_log(msg, log: sysLog, type: .error, "Warning: already added to a parent(\(parent))")
|
||||
return
|
||||
}
|
||||
assert((parent is UINavigationController) == false, "UINavigationController displays only one child view controller at a time.")
|
||||
@@ -548,7 +599,7 @@ open class FloatingPanelController: UIViewController {
|
||||
addChild(vc)
|
||||
|
||||
let surfaceView = floatingPanel.surfaceView
|
||||
surfaceView.set(contentView: vc.view)
|
||||
surfaceView.set(contentView: vc.view, mode: contentMode)
|
||||
|
||||
vc.didMove(toParent: self)
|
||||
}
|
||||
@@ -572,18 +623,22 @@ open class FloatingPanelController: UIViewController {
|
||||
|
||||
switch contentInsetAdjustmentBehavior {
|
||||
case .always:
|
||||
if #available(iOS 11.0, *) {
|
||||
scrollView.contentInsetAdjustmentBehavior = .never
|
||||
} else {
|
||||
children.forEach { (vc) in
|
||||
vc.automaticallyAdjustsScrollViewInsets = false
|
||||
}
|
||||
}
|
||||
scrollView.contentInsetAdjustmentBehavior = .never
|
||||
default:
|
||||
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.
|
||||
///
|
||||
@objc(untrackScrollView:)
|
||||
@@ -603,15 +658,19 @@ open class FloatingPanelController: UIViewController {
|
||||
|
||||
// MARK: - Utilities
|
||||
|
||||
/// Updates the layout object from the delegate and lays out the views managed
|
||||
/// by the controller immediately.
|
||||
/// Invalidates all layout information of the panel and apply the ``layout`` property into it immediately.
|
||||
///
|
||||
/// This method updates the `FloatingPanelLayout` object from the delegate and
|
||||
/// then it calls `layoutIfNeeded()` of the root view to force the view
|
||||
/// to update the layout immediately. It can be called in an
|
||||
/// animation block.
|
||||
/// This lays out subviews of the view that the controller manages with the ``layout`` property by
|
||||
/// calling the view's `layoutIfNeeded()`. Thus this method can be called in an animation block to
|
||||
/// animate the panel's changes.
|
||||
///
|
||||
/// 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
|
||||
public func invalidateLayout() {
|
||||
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: traitCollection) {
|
||||
layout = newLayout
|
||||
}
|
||||
activateLayout(forceLayout: true)
|
||||
}
|
||||
|
||||
@@ -667,30 +726,30 @@ extension FloatingPanelController {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Swizzling
|
||||
|
||||
private var originalDismissImp: IMP?
|
||||
private typealias DismissFunction = @convention(c) (AnyObject, Selector, Bool, (() -> Void)?) -> Void
|
||||
extension FloatingPanelController {
|
||||
private static let dismissSwizzling: Void = {
|
||||
let aClass: AnyClass! = UIViewController.self //object_getClass(vc)
|
||||
if let imp = class_getMethodImplementation(aClass, #selector(dismiss(animated:completion:))),
|
||||
let originalAltMethod = class_getInstanceMethod(aClass, #selector(fp_original_dismiss(animated:completion:))) {
|
||||
method_setImplementation(originalAltMethod, imp)
|
||||
}
|
||||
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)
|
||||
if let originalMethod = class_getInstanceMethod(aClass, #selector(dismiss(animated:completion:))),
|
||||
let swizzledImp = class_getMethodImplementation(aClass, #selector(__swizzled_dismiss(animated:completion:))) {
|
||||
originalDismissImp = method_setImplementation(originalMethod, swizzledImp)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
public extension UIViewController {
|
||||
@objc func fp_original_dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
// Implementation will be replaced by IMP of self.dismiss(animated:completion:)
|
||||
}
|
||||
@objc func fp_dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
extension UIViewController {
|
||||
@objc
|
||||
fileprivate func __swizzled_dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
let dismissImp = unsafeBitCast(originalDismissImp, to: DismissFunction.self)
|
||||
let sel = #selector(UIViewController.dismiss(animated:completion:))
|
||||
|
||||
// Call dismiss(animated:completion:) to a content view controller
|
||||
if let fpc = parent as? FloatingPanelController {
|
||||
if fpc.presentingViewController != nil {
|
||||
self.fp_original_dismiss(animated: flag, completion: completion)
|
||||
dismissImp(self, sel, flag, completion)
|
||||
} else {
|
||||
fpc.removePanelFromParent(animated: flag, completion: completion)
|
||||
}
|
||||
@@ -700,7 +759,7 @@ public extension UIViewController {
|
||||
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.
|
||||
if fpc.presentingViewController != nil, fpc.parent == nil {
|
||||
self.fp_original_dismiss(animated: flag, completion: completion)
|
||||
dismissImp(self, sel, flag, completion)
|
||||
} else {
|
||||
fpc.removePanelFromParent(animated: flag, completion: completion)
|
||||
}
|
||||
@@ -708,6 +767,6 @@ public extension UIViewController {
|
||||
}
|
||||
|
||||
// For other view controllers
|
||||
self.fp_original_dismiss(animated: flag, completion: completion)
|
||||
dismissImp(self, sel, flag, completion)
|
||||
}
|
||||
}
|
||||
|
||||
+402
-230
File diff suppressed because it is too large
Load Diff
+14
-65
@@ -7,10 +7,12 @@ import UIKit
|
||||
extension CGFloat {
|
||||
/// Returns this value rounded to an logical pixel value by a display scale
|
||||
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 {
|
||||
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 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 {
|
||||
/// 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 {
|
||||
if #available(iOS 11.0, *) {
|
||||
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))
|
||||
}
|
||||
return view.safeAreaInsets
|
||||
}
|
||||
}
|
||||
|
||||
// The reason why UIView has no extensions of safe area insets and top/bottom guides
|
||||
// is for iOS10 compatibility.
|
||||
extension UIView {
|
||||
var fp_safeAreaLayoutGuide: LayoutGuideProvider {
|
||||
if #available(iOS 11.0, *) {
|
||||
return safeAreaLayoutGuide
|
||||
} else {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
var presentationFrame: CGRect {
|
||||
return layer.presentation()?.frame ?? frame
|
||||
}
|
||||
@@ -139,7 +92,7 @@ extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
#if __FP_LOG
|
||||
#if FP_LOG
|
||||
extension UIGestureRecognizer.State: CustomDebugStringConvertible {
|
||||
public var debugDescription: String {
|
||||
switch self {
|
||||
@@ -157,18 +110,14 @@ extension UIGestureRecognizer.State: CustomDebugStringConvertible {
|
||||
|
||||
extension UIScrollView {
|
||||
var isLocked: Bool {
|
||||
return !showsVerticalScrollIndicator && !bounces && isDirectionalLockEnabled
|
||||
return !showsVerticalScrollIndicator && !bounces && isDirectionalLockEnabled
|
||||
}
|
||||
var fp_contentInset: UIEdgeInsets {
|
||||
if #available(iOS 11.0, *) {
|
||||
return adjustedContentInset
|
||||
} else {
|
||||
return contentInset
|
||||
}
|
||||
var isLooselyLocked: Bool {
|
||||
return !showsVerticalScrollIndicator && isDirectionalLockEnabled
|
||||
}
|
||||
var fp_contentOffsetMax: CGPoint {
|
||||
return CGPoint(x: max((contentSize.width + fp_contentInset.right) - bounds.width, 0.0),
|
||||
y: max((contentSize.height + fp_contentInset.bottom) - bounds.height, 0.0))
|
||||
return CGPoint(x: max((contentSize.width + adjustedContentInset.right) - bounds.width, 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>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.4.1</string>
|
||||
<string>2.8.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
</dict>
|
||||
|
||||
+64
-26
@@ -1,6 +1,7 @@
|
||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
/// An interface for generating layout information for a panel.
|
||||
@objc public protocol FloatingPanelLayout {
|
||||
@@ -44,8 +45,8 @@ open class FloatingPanelBottomLayout: NSObject, FloatingPanelLayout {
|
||||
|
||||
open func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
||||
return [
|
||||
surfaceView.leftAnchor.constraint(equalTo: view.fp_safeAreaLayoutGuide.leftAnchor, constant: 0.0),
|
||||
surfaceView.rightAnchor.constraint(equalTo: view.fp_safeAreaLayoutGuide.rightAnchor, constant: 0.0),
|
||||
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 0.0),
|
||||
surfaceView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: 0.0),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -92,6 +93,9 @@ class LayoutAdapter {
|
||||
|
||||
private var staticConstraint: NSLayoutConstraint?
|
||||
|
||||
/// A layout constraint to limit the content size in ``FloatingPanelAdaptiveLayoutAnchor``.
|
||||
private var contentBoundingConstraint: NSLayoutConstraint?
|
||||
|
||||
private var anchorStates: Set<FloatingPanelState> {
|
||||
return Set(layout.anchors.keys)
|
||||
}
|
||||
@@ -262,12 +266,25 @@ class LayoutAdapter {
|
||||
}
|
||||
|
||||
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 {
|
||||
case .top, .left:
|
||||
return edgePosition(surfaceView.presentationFrame) - position(for: mostExpandedState)
|
||||
offset = position(for: state) - edgePosition(surfaceView.frame)
|
||||
case .bottom, .right:
|
||||
return position(for: mostExpandedState) - edgePosition(surfaceView.presentationFrame)
|
||||
offset = edgePosition(surfaceView.frame) - position(for: state)
|
||||
}
|
||||
return offset.rounded(by: surfaceView.fp_displayScale)
|
||||
}
|
||||
|
||||
private var hiddenAnchor: FloatingPanelLayoutAnchoring {
|
||||
@@ -330,12 +347,27 @@ class LayoutAdapter {
|
||||
if anchor.referenceGuide == .safeArea {
|
||||
referenceBoundsLength += position.inset(safeAreaInsets)
|
||||
}
|
||||
return dimension - diff
|
||||
let maxPosition: CGFloat = {
|
||||
if let maxBounds = anchor.contentBoundingGuide.maxBounds(vc) {
|
||||
return layout.position.mainLocation(maxBounds.origin)
|
||||
+ layout.position.mainDimension(maxBounds.size)
|
||||
} else {
|
||||
return .infinity
|
||||
}
|
||||
}()
|
||||
return min(dimension - diff, maxPosition)
|
||||
case .bottom, .right:
|
||||
if anchor.referenceGuide == .safeArea {
|
||||
referenceBoundsLength -= position.inset(safeAreaInsets)
|
||||
}
|
||||
return referenceBoundsLength - dimension + diff
|
||||
let minPosition: CGFloat = {
|
||||
if let maxBounds = anchor.contentBoundingGuide.maxBounds(vc) {
|
||||
return layout.position.mainLocation(maxBounds.origin)
|
||||
} else {
|
||||
return -(.infinity)
|
||||
}
|
||||
}()
|
||||
return max(referenceBoundsLength - dimension + diff, minPosition)
|
||||
}
|
||||
case let anchor as FloatingPanelLayoutAnchor:
|
||||
let referenceBounds = anchor.referenceGuide == .safeArea ? bounds.inset(by: safeAreaInsets) : bounds
|
||||
@@ -403,13 +435,13 @@ class LayoutAdapter {
|
||||
switch position {
|
||||
case .top, .bottom:
|
||||
surfaceConstraints = [
|
||||
surfaceView.leftAnchor.constraint(equalTo: vc.fp_safeAreaLayoutGuide.leftAnchor, constant: 0.0),
|
||||
surfaceView.rightAnchor.constraint(equalTo: vc.fp_safeAreaLayoutGuide.rightAnchor, constant: 0.0),
|
||||
surfaceView.leftAnchor.constraint(equalTo: vc.view.safeAreaLayoutGuide.leftAnchor, constant: 0.0),
|
||||
surfaceView.rightAnchor.constraint(equalTo: vc.view.safeAreaLayoutGuide.rightAnchor, constant: 0.0),
|
||||
]
|
||||
case .left, .right:
|
||||
surfaceConstraints = [
|
||||
surfaceView.topAnchor.constraint(equalTo: vc.fp_safeAreaLayoutGuide.topAnchor, constant: 0.0),
|
||||
surfaceView.bottomAnchor.constraint(equalTo: vc.fp_safeAreaLayoutGuide.bottomAnchor, constant: 0.0),
|
||||
surfaceView.topAnchor.constraint(equalTo: vc.view.safeAreaLayoutGuide.topAnchor, constant: 0.0),
|
||||
surfaceView.bottomAnchor.constraint(equalTo: vc.view.safeAreaLayoutGuide.bottomAnchor, constant: 0.0),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -517,7 +549,7 @@ class LayoutAdapter {
|
||||
let layoutGuideProvider: LayoutGuideProvider
|
||||
switch anchor.referenceGuide {
|
||||
case .safeArea:
|
||||
layoutGuideProvider = vc.fp_safeAreaLayoutGuide
|
||||
layoutGuideProvider = vc.view.safeAreaLayoutGuide
|
||||
case .superview:
|
||||
layoutGuideProvider = vc.view
|
||||
}
|
||||
@@ -629,8 +661,9 @@ class LayoutAdapter {
|
||||
// The method is separated from prepareLayout(to:) for the rotation support
|
||||
// It must be called in FloatingPanelController.traitCollectionDidChange(_:)
|
||||
func updateStaticConstraint() {
|
||||
NSLayoutConstraint.deactivate(constraint: staticConstraint)
|
||||
NSLayoutConstraint.deactivate([staticConstraint, contentBoundingConstraint].compactMap{ $0 })
|
||||
staticConstraint = nil
|
||||
contentBoundingConstraint = nil
|
||||
|
||||
if vc.contentMode == .fitToBounds {
|
||||
surfaceView.containerOverflow = 0
|
||||
@@ -654,7 +687,18 @@ class LayoutAdapter {
|
||||
constant = 0.0
|
||||
}
|
||||
let baseAnchor = position.mainDimensionAnchor(anchor.contentLayoutGuide)
|
||||
staticConstraint = surfaceAnchor.constraint(equalTo: baseAnchor, constant: constant)
|
||||
if let boundingLayoutGuide = anchor.contentBoundingGuide.layoutGuide(vc) {
|
||||
if anchor.isAbsolute {
|
||||
contentBoundingConstraint = baseAnchor.constraint(lessThanOrEqualTo: position.mainDimensionAnchor(boundingLayoutGuide),
|
||||
constant: anchor.offset)
|
||||
} else {
|
||||
contentBoundingConstraint = baseAnchor.constraint(lessThanOrEqualTo: position.mainDimensionAnchor(boundingLayoutGuide),
|
||||
multiplier: anchor.offset)
|
||||
}
|
||||
staticConstraint = surfaceAnchor.constraint(lessThanOrEqualTo: baseAnchor, constant: constant)
|
||||
} else {
|
||||
staticConstraint = surfaceAnchor.constraint(equalTo: baseAnchor, constant: constant)
|
||||
}
|
||||
default:
|
||||
switch position {
|
||||
case .top, .left:
|
||||
@@ -673,14 +717,14 @@ class LayoutAdapter {
|
||||
staticConstraint?.identifier = "FloatingPanel-static-width"
|
||||
}
|
||||
|
||||
NSLayoutConstraint.activate(constraint: staticConstraint)
|
||||
NSLayoutConstraint.activate([staticConstraint, contentBoundingConstraint].compactMap{ $0 })
|
||||
|
||||
surfaceView.containerOverflow = position.mainDimension(vc.view.bounds.size)
|
||||
}
|
||||
|
||||
func updateInteractiveEdgeConstraint(diff: CGFloat, overflow: Bool, allowsRubberBanding: (UIRectEdge) -> Bool) {
|
||||
func updateInteractiveEdgeConstraint(diff: CGFloat, scrollingContent: Bool, allowsRubberBanding: (UIRectEdge) -> Bool) {
|
||||
defer {
|
||||
log.debug("update surface location = \(surfaceLocation)")
|
||||
os_log(msg, log: devLog, type: .debug, "update surface location = \(surfaceLocation)")
|
||||
}
|
||||
|
||||
let minConst: CGFloat = position(for: leastCoordinateState)
|
||||
@@ -701,7 +745,7 @@ class LayoutAdapter {
|
||||
const = maxConst + rubberBandEffect(for: buffer, base: base)
|
||||
}
|
||||
|
||||
if overflow == false {
|
||||
if scrollingContent {
|
||||
const = min(max(const, minConst), maxConst)
|
||||
}
|
||||
|
||||
@@ -720,9 +764,9 @@ class LayoutAdapter {
|
||||
defer {
|
||||
if forceLayout {
|
||||
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 {
|
||||
log.debug("activateLayout for \(state)")
|
||||
os_log(msg, log: devLog, type: .debug, "activateLayout for \(state)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -738,12 +782,6 @@ class LayoutAdapter {
|
||||
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
|
||||
// UIView.systemLayoutSizeFitting() returns a different size between an
|
||||
// on-screen and off-screen view which includes
|
||||
@@ -757,7 +795,7 @@ class LayoutAdapter {
|
||||
if let constraints = stateConstraints[state] {
|
||||
NSLayoutConstraint.activate(constraints)
|
||||
} 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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,11 @@ import UIKit
|
||||
///
|
||||
/// The inset is an amount to inset a panel from an edge of the reference guide. The edge refers to a panel
|
||||
/// positioning.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - absoluteOffset: An absolute offset to attach the panel from the edge.
|
||||
/// - edge: Specify the edge of ``FloatingPanelController``'s view. This is the staring point of the offset.
|
||||
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
|
||||
@objc public init(absoluteInset: CGFloat, edge: FloatingPanelReferenceEdge, referenceGuide: FloatingPanelLayoutReferenceGuide) {
|
||||
self.inset = absoluteInset
|
||||
self.referenceGuide = referenceGuide
|
||||
@@ -27,6 +32,11 @@ import UIKit
|
||||
/// The inset is an amount to inset a panel from the edge of the specified reference guide. The value is
|
||||
/// a floating-point number in the range 0.0 to 1.0, where 0.0 represents zero distance from the edge and
|
||||
/// 1.0 represents a distance to the opposite edge.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - fractionalOffset: A fractional value of the size of ``FloatingPanelController``'s view to attach the panel from the edge.
|
||||
/// - edge: Specify the edge of ``FloatingPanelController``'s view. This is the staring point of the offset.
|
||||
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
|
||||
@objc public init(fractionalInset: CGFloat, edge: FloatingPanelReferenceEdge, referenceGuide: FloatingPanelLayoutReferenceGuide) {
|
||||
self.inset = fractionalInset
|
||||
self.referenceGuide = referenceGuide
|
||||
@@ -49,7 +59,7 @@ public extension FloatingPanelLayoutAnchor {
|
||||
case .left:
|
||||
return layoutConstraints(layoutGuide, for: vc.surfaceView.rightAnchor)
|
||||
case .bottom:
|
||||
return layoutConstraints(layoutGuide, for: vc.surfaceView.topAnchor)
|
||||
return layoutConstraints(layoutGuide, for: vc.surfaceView.topAnchor)
|
||||
case .right:
|
||||
return layoutConstraints(layoutGuide, for: vc.surfaceView.leftAnchor)
|
||||
}
|
||||
@@ -101,6 +111,10 @@ public extension FloatingPanelLayoutAnchor {
|
||||
///
|
||||
/// The offset is an amount to offset a position of panel that displays the entire content from an edge of
|
||||
/// the reference guide. The edge refers to a panel positioning.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - absoluteOffset: An absolute offset from the content size in the main dimension(i.e. y axis for a bottom panel) to attach the panel.
|
||||
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
|
||||
@objc public init(absoluteOffset offset: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
||||
self.offset = offset
|
||||
self.referenceGuide = referenceGuide
|
||||
@@ -111,6 +125,10 @@ public extension FloatingPanelLayoutAnchor {
|
||||
///
|
||||
/// The offset value is a floating-point number in the range 0.0 to 1.0, where 0.0 represents the full content
|
||||
/// is displayed and 0.5 represents the half of content is displayed.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - fractionalOffset: A fractional offset of the content size in the main dimension(i.e. y axis for a bottom panel) to attach the panel.
|
||||
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
|
||||
@objc public init(fractionalOffset offset: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
||||
self.offset = offset
|
||||
self.referenceGuide = referenceGuide
|
||||
@@ -144,37 +162,72 @@ public extension FloatingPanelIntrinsicLayoutAnchor {
|
||||
/// An object that defines how to settles a panel with a layout guide of a content view.
|
||||
@objc final public class FloatingPanelAdaptiveLayoutAnchor: NSObject, FloatingPanelLayoutAnchoring /*, NSCopying */ {
|
||||
|
||||
/// Returns a layout anchor with the specified offset by an absolute value, layout guide to display content and reference guide for a panel.
|
||||
/// Returns a layout anchor with the specified offset by an absolute value to display a panel with its intrinsic content size.
|
||||
///
|
||||
/// The offset is an amount to offset a position of panel that displays the entire content of the specified guide from an edge of
|
||||
/// the reference guide. The edge refers to a panel positioning.
|
||||
@objc public init(absoluteOffset offset: CGFloat, contentLayout: UILayoutGuide, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
||||
///
|
||||
/// ``contentBoundingGuide`` restricts the content size which a panel displays. For example, given ``referenceGuide`` is `.superview` and ``contentBoundingGuide`` is `.safeArea` for a bottom positioned panel, the panel content is laid out inside the superview of the view of FloatingPanelController(not its safe area), but its content size is limited to its safe area size. Normally both of ``referenceGuide`` and ``contentBoundingGuide`` are specified with the same rectangle area.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - absoluteOffset: An absolute offset from the content size in the main dimension(i.e. y axis for a bottom panel) to attach the panel.
|
||||
/// - contentLayout: The content layout guide to calculate the content size in the panel.
|
||||
/// - referenceGuide: The rectangular area to lay out the content of a panel. If it's set to `.safeArea`, the panel content displays inside the safe area of its ``FloatingPanelController``'s view. This argument doesn't limit its content size.
|
||||
/// - contentBoundingGuide: The rectangular area to restrict the content size of a panel in the main dimension(i.e. y axis is the main dimension for a bottom panel).
|
||||
///
|
||||
/// - Warning: If ``contentBoundingGuide`` is set to none, the panel may expand out of the screen size, depending on the intrinsic size of its content.
|
||||
@objc public init(
|
||||
absoluteOffset offset: CGFloat,
|
||||
contentLayout: UILayoutGuide,
|
||||
referenceGuide: FloatingPanelLayoutReferenceGuide,
|
||||
contentBoundingGuide: FloatingPanelLayoutContentBoundingGuide = .none
|
||||
) {
|
||||
self.offset = offset
|
||||
self.contentLayoutGuide = contentLayout
|
||||
self.referenceGuide = referenceGuide
|
||||
self.contentBoundingGuide = contentBoundingGuide
|
||||
self.isAbsolute = true
|
||||
|
||||
}
|
||||
|
||||
/// Returns a layout anchor with the specified offset by a fractional value, layout guide to display content and reference guide for a panel.
|
||||
/// Returns a layout anchor with the specified offset by a fractional value to display a panel with its intrinsic content size.
|
||||
///
|
||||
/// The offset value is a floating-point number in the range 0.0 to 1.0, where 0.0 represents the full content
|
||||
/// is displayed and 0.5 represents the half of content is displayed.
|
||||
@objc public init(fractionalOffset offset: CGFloat, contentLayout: UILayoutGuide, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
||||
///
|
||||
/// ``contentBoundingGuide`` restricts the content size which a panel displays. For example, given ``referenceGuide`` is `.superview` and ``contentBoundingGuide`` is `.safeArea` for a bottom positioned panel, the panel content is laid out inside the superview of the view of FloatingPanelController(not its safe area), but its content size is limited to its safe area size. Normally both of ``referenceGuide`` and ``contentBoundingGuide`` are specified with the same rectangle area.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - fractionalOffset: A fractional offset of the content size in the main dimension(i.e. y axis for a bottom panel) to attach the panel.
|
||||
/// - contentLayout: The content layout guide to calculate the content size in the panel.
|
||||
/// - referenceGuide: The rectangular area to lay out the content of a panel. If it's set to `.safeArea`, the panel content displays inside the safe area of its ``FloatingPanelController``'s view. This argument doesn't limit its content size.
|
||||
/// - contentBoundingGuide: The rectangular area to restrict the content size of a panel in the main dimension(i.e. y axis is the main dimension for a bottom panel).
|
||||
///
|
||||
/// - Warning: If ``contentBoundingGuide`` is set to none, the panel may expand out of the screen size, depending on the intrinsic size of its content.
|
||||
@objc public init(
|
||||
fractionalOffset offset: CGFloat,
|
||||
contentLayout: UILayoutGuide,
|
||||
referenceGuide: FloatingPanelLayoutReferenceGuide,
|
||||
contentBoundingGuide: FloatingPanelLayoutContentBoundingGuide = .none
|
||||
) {
|
||||
self.offset = offset
|
||||
self.contentLayoutGuide = contentLayout
|
||||
self.referenceGuide = referenceGuide
|
||||
self.contentBoundingGuide = contentBoundingGuide
|
||||
self.isAbsolute = false
|
||||
}
|
||||
fileprivate let offset: CGFloat
|
||||
fileprivate let isAbsolute: Bool
|
||||
let offset: CGFloat
|
||||
let isAbsolute: Bool
|
||||
let contentLayoutGuide: UILayoutGuide
|
||||
@objc public let referenceGuide: FloatingPanelLayoutReferenceGuide
|
||||
@objc public let contentBoundingGuide: FloatingPanelLayoutContentBoundingGuide
|
||||
}
|
||||
|
||||
public extension FloatingPanelAdaptiveLayoutAnchor {
|
||||
func layoutConstraints(_ vc: FloatingPanelController, for position: FloatingPanelPosition) -> [NSLayoutConstraint] {
|
||||
var constraints = [NSLayoutConstraint]()
|
||||
|
||||
let layoutGuide = referenceGuide.layoutGuide(vc: vc)
|
||||
let offsetConstraint: NSLayoutConstraint
|
||||
let offsetAnchor: NSLayoutDimension
|
||||
switch position {
|
||||
case .top:
|
||||
@@ -187,10 +240,13 @@ public extension FloatingPanelAdaptiveLayoutAnchor {
|
||||
offsetAnchor = vc.surfaceView.leftAnchor.anchorWithOffset(to: layoutGuide.rightAnchor)
|
||||
}
|
||||
if isAbsolute {
|
||||
return [offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), constant: -offset)]
|
||||
offsetConstraint = offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), constant: -offset)
|
||||
} else {
|
||||
return [offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), multiplier: (1 - offset))]
|
||||
offsetConstraint = offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), multiplier: (1 - offset))
|
||||
}
|
||||
constraints.append(offsetConstraint)
|
||||
|
||||
return constraints
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ extension FloatingPanelReferenceEdge {
|
||||
}
|
||||
}
|
||||
|
||||
/// Constants that specify a layout guide to lay out a panel.
|
||||
/// A representation to specify a rectangular area to lay out a panel.
|
||||
@objc public enum FloatingPanelLayoutReferenceGuide: Int {
|
||||
case superview = 0
|
||||
case safeArea = 1
|
||||
@@ -37,9 +37,40 @@ extension FloatingPanelLayoutReferenceGuide {
|
||||
func layoutGuide(vc: UIViewController) -> LayoutGuideProvider {
|
||||
switch self {
|
||||
case .safeArea:
|
||||
return vc.fp_safeAreaLayoutGuide
|
||||
return vc.view.safeAreaLayoutGuide
|
||||
case .superview:
|
||||
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() {}
|
||||
}
|
||||
+52
-28
@@ -1,6 +1,7 @@
|
||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
/// An object for customizing the appearance of a surface view
|
||||
@objc(FloatingPanelSurfaceAppearance)
|
||||
@@ -54,7 +55,12 @@ public class SurfaceAppearance: NSObject {
|
||||
///
|
||||
/// Defaults to `.circular`.
|
||||
@available(iOS 13.0, *)
|
||||
public lazy var cornerCurve: CALayerCornerCurve = .circular
|
||||
public var cornerCurve: CALayerCornerCurve {
|
||||
get { _cornerCurve ?? .circular }
|
||||
set { _cornerCurve = newValue }
|
||||
}
|
||||
|
||||
private var _cornerCurve: CALayerCornerCurve?
|
||||
|
||||
/// An array of shadows used to create drop shadows underneath a surface view.
|
||||
public var shadows: [Shadow] = [Shadow()]
|
||||
@@ -85,7 +91,7 @@ public class SurfaceView: UIView {
|
||||
|
||||
/// 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 {
|
||||
setNeedsUpdateConstraints()
|
||||
} }
|
||||
@@ -313,7 +319,7 @@ public class SurfaceView: UIView {
|
||||
|
||||
public override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
log.debug("surface view frame = \(frame)")
|
||||
os_log(msg, log: devLog, type: .debug, "surface view frame = \(frame)")
|
||||
|
||||
containerView.backgroundColor = appearance.backgroundColor
|
||||
|
||||
@@ -377,29 +383,24 @@ public class SurfaceView: UIView {
|
||||
}
|
||||
containerView.layer.masksToBounds = true
|
||||
if position.inset(containerMargins) != 0 {
|
||||
if #available(iOS 11, *) {
|
||||
containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner,
|
||||
.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
}
|
||||
containerView.layer.maskedCorners = [
|
||||
.layerMinXMinYCorner, .layerMaxXMinYCorner,
|
||||
.layerMinXMaxYCorner, .layerMaxXMaxYCorner
|
||||
]
|
||||
return
|
||||
}
|
||||
if #available(iOS 11, *) {
|
||||
// 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.
|
||||
// Because the bottom of contentView must be fit to the bottom of a screen to work the `safeLayoutGuide` of a content VC.
|
||||
switch position {
|
||||
case .top:
|
||||
containerView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
case .left:
|
||||
containerView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
|
||||
case .bottom:
|
||||
containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
case .right:
|
||||
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.
|
||||
// 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.
|
||||
// Because the bottom of contentView must be fit to the bottom of a screen to work the `safeLayoutGuide` of a content VC.
|
||||
switch position {
|
||||
case .top:
|
||||
containerView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
case .left:
|
||||
containerView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
|
||||
case .bottom:
|
||||
containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
case .right:
|
||||
containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,12 +409,11 @@ public class SurfaceView: UIView {
|
||||
containerView.layer.borderWidth = appearance.borderWidth
|
||||
}
|
||||
|
||||
func set(contentView: UIView) {
|
||||
func set(contentView: UIView, mode: FloatingPanelController.ContentMode) {
|
||||
containerView.addSubview(contentView)
|
||||
self.contentView = contentView
|
||||
/* contentView.frame = bounds */ // MUST NOT: Because the top safe area inset of a content VC will be incorrect.
|
||||
contentView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
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 rightConstraint = rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: containerMargins.right + contentPadding.right)
|
||||
@@ -424,9 +424,15 @@ public class SurfaceView: UIView {
|
||||
rightConstraint,
|
||||
bottomConstraint,
|
||||
].map {
|
||||
$0.priority = .required - 1;
|
||||
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"
|
||||
return $0;
|
||||
return $0
|
||||
})
|
||||
self.contentViewTopConstraint = topConstraint
|
||||
self.contentViewLeftConstraint = leftConstraint
|
||||
@@ -437,4 +443,22 @@ public class SurfaceView: UIView {
|
||||
func hasStackView() -> Bool {
|
||||
return contentView?.subviews.reduce(false) { $0 || ($1 is UIStackView) } ?? false
|
||||
}
|
||||
|
||||
func grabberAreaContains(_ location: CGPoint) -> Bool {
|
||||
// Sometimes a dragging finger's location is out of surface frame.
|
||||
let cappedLocation: CGPoint
|
||||
// Because the maximum width / height is out of bounds in CGRect.contains(_:)
|
||||
let adjustment = 1 / fp_displayScale
|
||||
switch position {
|
||||
case .top:
|
||||
cappedLocation = CGPoint(x: location.x, y: min(location.y, bounds.height - adjustment))
|
||||
case .left:
|
||||
cappedLocation = CGPoint(x: min(location.x, bounds.width - adjustment), y: location.y)
|
||||
case .bottom:
|
||||
cappedLocation = CGPoint(x: location.x, y: max(location.y, 0))
|
||||
case .right:
|
||||
cappedLocation = CGPoint(x: max(location.x, 0), y: location.y)
|
||||
}
|
||||
return grabberAreaFrame.contains(cappedLocation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,10 @@ class ModalPresentTransition: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
fpc?.suspendTransitionAnimator(false)
|
||||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||
}
|
||||
return fpc.transitionAnimator!
|
||||
guard let transitionAnimator = fpc.transitionAnimator else {
|
||||
fatalError("The panel state must be `hidden` but it is `\(fpc.state)`")
|
||||
}
|
||||
return transitionAnimator
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
|
||||
+60
-17
@@ -1,5 +1,6 @@
|
||||
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||
|
||||
import OSLog
|
||||
import XCTest
|
||||
@testable import FloatingPanel
|
||||
|
||||
@@ -7,22 +8,32 @@ class ControllerTests: XCTestCase {
|
||||
override func setUp() {}
|
||||
override func tearDown() {}
|
||||
|
||||
func test_warningRetainCycle() {
|
||||
let exp = expectation(description: "Warning retain cycle")
|
||||
exp.expectedFulfillmentCount = 2 // For layout & behavior logs
|
||||
log.hook = {(log, level) in
|
||||
if log.contains("A memory leak will occur by a retain cycle because") {
|
||||
XCTAssert(level == .warning)
|
||||
exp.fulfill()
|
||||
}
|
||||
#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
|
||||
func test_warningRetainCycle() throws {
|
||||
guard #available(iOS 15.0, *) else {
|
||||
throw XCTSkip("Unsupported iOS version: this test needs iOS 15 or later")
|
||||
}
|
||||
let myVC = MyZombieViewController(nibName: nil, bundle: nil)
|
||||
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() {
|
||||
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else { fatalError() }
|
||||
let rootVC = UIViewController()
|
||||
rootVC.loadViewIfNeeded()
|
||||
rootVC.view.bounds = .init(origin: .zero, size: .init(width: 390, height: 844))
|
||||
|
||||
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.addPanel(toParent: rootVC)
|
||||
XCTAssertEqual(fpc.surfaceLocation.y, fpc.surfaceLocation(for: .half).y)
|
||||
@@ -30,8 +41,13 @@ class ControllerTests: XCTestCase {
|
||||
XCTAssertEqual(fpc.surfaceLocation.y, fpc.surfaceLocation(for: .tip).y)
|
||||
}
|
||||
|
||||
@available(iOS 12.0, *)
|
||||
func test_updateLayout_willTransition() {
|
||||
func test_updateLayout_willTransition() throws {
|
||||
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 {
|
||||
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
|
||||
if newCollection.userInterfaceStyle == .dark {
|
||||
@@ -314,11 +330,40 @@ class ControllerTests: XCTestCase {
|
||||
fpc.move(to: .full, animated: false)
|
||||
XCTAssertEqual(fpc.surfaceView.frame.height, fpc.view.bounds.height - fpc.surfaceLocation(for: .full).y)
|
||||
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)
|
||||
fpc.move(to: .tip, animated: false)
|
||||
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 {
|
||||
@@ -336,8 +381,7 @@ private class MyZombieViewController: UIViewController, FloatingPanelLayout, Flo
|
||||
return .half
|
||||
}
|
||||
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: UIScreen.main.bounds.height == 667.0 ? 18.0 : 16.0,
|
||||
edge: .top,
|
||||
referenceGuide: .superview),
|
||||
@@ -347,6 +391,5 @@ private class MyZombieViewController: UIViewController, FloatingPanelLayout, Flo
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0,
|
||||
edge: .bottom,
|
||||
referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+217
-77
@@ -7,7 +7,7 @@ class CoreTests: XCTestCase {
|
||||
override func setUp() {}
|
||||
override func tearDown() {}
|
||||
|
||||
func test_scrolllock() {
|
||||
func test_scrollLock() {
|
||||
let fpc = FloatingPanelController()
|
||||
|
||||
let contentVC1 = UITableViewController(nibName: nil, bundle: nil)
|
||||
@@ -61,9 +61,8 @@ class CoreTests: XCTestCase {
|
||||
class FloatingPanelLayout1Positions: FloatingPanelLayout {
|
||||
let initialState: FloatingPanelState = .full
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] =
|
||||
[.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
|
||||
}
|
||||
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
@@ -106,12 +105,10 @@ class CoreTests: XCTestCase {
|
||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||
let initialState: FloatingPanelState = .half
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
@@ -198,26 +195,41 @@ class CoreTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
let fpc = FloatingPanelController()
|
||||
class BackdropTestLayout2: FloatingPanelTestLayout {
|
||||
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
|
||||
return 0.0
|
||||
}
|
||||
}
|
||||
class TestDelegate: FloatingPanelControllerDelegate {
|
||||
var layout: FloatingPanelLayout = BackdropTestLayout2()
|
||||
func floatingPanel(_ fpc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout { layout }
|
||||
func floatingPanel(_ fpc: FloatingPanelController, layoutFor size: CGSize) -> FloatingPanelLayout { layout }
|
||||
}
|
||||
func _floor(_ alpha: CGFloat) -> CGFloat {
|
||||
return floor(fpc.backdropView.alpha * 1e+06) / 1e+06
|
||||
}
|
||||
|
||||
let delegate = TestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
fpc.layout = BackdropTestLayout()
|
||||
|
||||
fpc.showForTest()
|
||||
|
||||
fpc.move(to: .full, animated: false)
|
||||
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
|
||||
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
|
||||
|
||||
fpc.move(to: .half, animated: false)
|
||||
XCTAssertEqual(fpc.backdropView.alpha, 0.0)
|
||||
|
||||
fpc.move(to: .tip, animated: false)
|
||||
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
|
||||
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
|
||||
|
||||
let exp1 = expectation(description: "move to full with animation")
|
||||
fpc.move(to: .full, animated: true) {
|
||||
exp1.fulfill()
|
||||
}
|
||||
wait(for: [exp1], timeout: 1.0)
|
||||
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
|
||||
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
|
||||
|
||||
let exp2 = expectation(description: "move to half with animation")
|
||||
fpc.move(to: .half, animated: true) {
|
||||
@@ -226,23 +238,66 @@ class CoreTests: XCTestCase {
|
||||
wait(for: [exp2], timeout: 1.0)
|
||||
XCTAssertEqual(fpc.backdropView.alpha, 0.0)
|
||||
|
||||
// Test a content mode change of FloatingPanelController
|
||||
|
||||
let exp3 = expectation(description: "move to tip with animation")
|
||||
fpc.move(to: .tip, animated: true) {
|
||||
exp3.fulfill()
|
||||
}
|
||||
fpc.contentMode = .fitToBounds
|
||||
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must not affect the backdrop alpha by changing the content mode
|
||||
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must not affect the backdrop alpha by changing the content mode
|
||||
wait(for: [exp3], timeout: 1.0)
|
||||
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
|
||||
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
|
||||
|
||||
// Test a size class change of FloatingPanelController.view
|
||||
|
||||
fpc.move(to: .full, animated: false)
|
||||
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
|
||||
fpc.willTransition(to: UITraitCollection(horizontalSizeClass: .regular), with: MockTransitionCoordinator())
|
||||
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must update the alpha by BackdropTestLayout2 in TestDelegate.
|
||||
|
||||
// Test a view size change of FloatingPanelController.view
|
||||
|
||||
fpc.move(to: .full, animated: false)
|
||||
delegate.layout = BackdropTestLayout()
|
||||
fpc.invalidateLayout()
|
||||
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
|
||||
|
||||
delegate.layout = BackdropTestLayout2()
|
||||
fpc.viewWillTransition(to: CGSize.zero, with: MockTransitionCoordinator())
|
||||
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must update the alpha by BackdropTestLayout2 in TestDelegate.
|
||||
}
|
||||
|
||||
func test_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 {
|
||||
let initialState: FloatingPanelState = .full
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring]
|
||||
= [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
|
||||
}
|
||||
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
@@ -254,7 +309,7 @@ class CoreTests: XCTestCase {
|
||||
let fullPos = fpc.surfaceLocation(for: .full).y
|
||||
|
||||
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, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
||||
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full), // redirect
|
||||
@@ -265,16 +320,14 @@ class CoreTests: XCTestCase {
|
||||
])
|
||||
}
|
||||
|
||||
func test_targetPosition_2positions() {
|
||||
func test_targetState_2positions() {
|
||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||
let initialState: FloatingPanelState = .half
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
@@ -287,7 +340,7 @@ class CoreTests: XCTestCase {
|
||||
let halfPos = fpc.surfaceLocation(for: .half).y
|
||||
|
||||
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, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
||||
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
||||
@@ -304,7 +357,7 @@ class CoreTests: XCTestCase {
|
||||
(#line, halfPos + 10.0, CGPoint(x: 0.0, y: -1000.0), .full), // project to full
|
||||
])
|
||||
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, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
||||
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
||||
@@ -322,16 +375,14 @@ class CoreTests: XCTestCase {
|
||||
])
|
||||
}
|
||||
|
||||
func test_targetPosition_2positionsWithHidden() {
|
||||
func test_targetState_2positionsWithHidden() {
|
||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||
let initialState: FloatingPanelState = .hidden
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
@@ -344,7 +395,7 @@ class CoreTests: XCTestCase {
|
||||
let hiddenPos = fpc.surfaceLocation(for: .hidden).y
|
||||
|
||||
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, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
||||
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
||||
@@ -361,7 +412,7 @@ class CoreTests: XCTestCase {
|
||||
(#line, hiddenPos + 10.0, CGPoint(x: 0.0, y: -1000.0), .full), // project to full
|
||||
])
|
||||
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, CGPoint(x: 0.0, y: -1000.0), .full), // redirect
|
||||
(#line, fullPos, CGPoint(x: 0.0, y: -100.0), .full),
|
||||
@@ -379,7 +430,7 @@ class CoreTests: XCTestCase {
|
||||
])
|
||||
}
|
||||
|
||||
func test_targetPosition_3positionsFromFull() {
|
||||
func test_targetState_3positionsFromFull() {
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
fpc.layout = FloatingPanelLayout3Positions()
|
||||
@@ -391,7 +442,7 @@ class CoreTests: XCTestCase {
|
||||
let tipPos = fpc.surfaceLocation(for: .tip).y
|
||||
// From .full
|
||||
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: 0.0), .full), // far from topMostState
|
||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 100.0), .full), // far from topMostState
|
||||
@@ -423,7 +474,7 @@ class CoreTests: XCTestCase {
|
||||
])
|
||||
}
|
||||
|
||||
func test_targetPosition_3positionsFromFull_bottomEdge() {
|
||||
func test_targetState_3positionsFromFull_bottomEdge() {
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
fpc.layout = FloatingPanelLayout3PositionsBottomEdge()
|
||||
@@ -435,7 +486,7 @@ class CoreTests: XCTestCase {
|
||||
let tipPos = fpc.surfaceLocation(for: .tip).y
|
||||
// From .full
|
||||
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: 0.0), .tip), // far from topMostState
|
||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 100.0), .tip), // far from topMostState
|
||||
@@ -467,7 +518,7 @@ class CoreTests: XCTestCase {
|
||||
])
|
||||
}
|
||||
|
||||
func test_targetPosition_3positionsFromHalf() {
|
||||
func test_targetState_3positionsFromHalf() {
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
fpc.layout = FloatingPanelLayout3Positions()
|
||||
@@ -479,7 +530,7 @@ class CoreTests: XCTestCase {
|
||||
let tipPos = fpc.surfaceLocation(for: .tip).y
|
||||
// From .half
|
||||
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: 0.0), .full), // far from topMostState
|
||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 100.0), .full), // far from topMostState
|
||||
@@ -509,7 +560,7 @@ class CoreTests: XCTestCase {
|
||||
])
|
||||
}
|
||||
|
||||
func test_targetPosition_3positionsFromHalf_bottomEdge() {
|
||||
func test_targetState_3positionsFromHalf_bottomEdge() {
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
fpc.layout = FloatingPanelLayout3PositionsBottomEdge()
|
||||
@@ -521,7 +572,7 @@ class CoreTests: XCTestCase {
|
||||
let tipPos = fpc.surfaceLocation(for: .tip).y
|
||||
// From .half
|
||||
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: 0.0), .tip), // far from topMostState
|
||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 100.0), .tip), // far from topMostState
|
||||
@@ -551,7 +602,7 @@ class CoreTests: XCTestCase {
|
||||
])
|
||||
}
|
||||
|
||||
func test_targetPosition_3positionsFromTip() {
|
||||
func test_targetState_3positionsFromTip() {
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
fpc.layout = FloatingPanelLayout3Positions()
|
||||
@@ -564,7 +615,7 @@ class CoreTests: XCTestCase {
|
||||
|
||||
// From .tip
|
||||
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: 0.0), .full), // far from topMostState
|
||||
(#line, fullPos - 500.0, CGPoint(x: 0.0, y: 100.0), .full), // far from topMostState
|
||||
@@ -594,7 +645,7 @@ class CoreTests: XCTestCase {
|
||||
])
|
||||
}
|
||||
|
||||
func test_targetPosition_3positionsFromTip_bottomEdge() {
|
||||
func test_targetState_3positionsFromTip_bottomEdge() {
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
fpc.layout = FloatingPanelLayout3PositionsBottomEdge()
|
||||
@@ -607,7 +658,7 @@ class CoreTests: XCTestCase {
|
||||
|
||||
// From .tip
|
||||
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: 0.0), .tip), // far from topMostState
|
||||
(#line, tipPos - 500.0, CGPoint(x: 0.0, y: 100.0), .tip), // far from topMostState
|
||||
@@ -637,7 +688,7 @@ class CoreTests: XCTestCase {
|
||||
])
|
||||
}
|
||||
|
||||
func test_targetPosition_3positionsAllProjection() {
|
||||
func test_targetState_3positionsAllProjection() {
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
fpc.layout = FloatingPanelLayout3Positions()
|
||||
@@ -651,7 +702,7 @@ class CoreTests: XCTestCase {
|
||||
|
||||
// From .full
|
||||
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, CGPoint(x: 0.0, y: 1000.0), .tip),
|
||||
(#line, fullPos, CGPoint(x: 0.0, y: 3000.0), .tip),
|
||||
@@ -664,7 +715,7 @@ class CoreTests: XCTestCase {
|
||||
|
||||
// From .half
|
||||
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: 3000.0), .tip),
|
||||
(#line, tipPos, CGPoint(x: 0.0, y: -3000.0), .full),
|
||||
@@ -673,7 +724,7 @@ class CoreTests: XCTestCase {
|
||||
|
||||
// From .tip
|
||||
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, CGPoint(x: 0.0, y: 1000.0), .tip),
|
||||
(#line, fullPos, CGPoint(x: 0.0, y: 3000.0), .tip),
|
||||
@@ -685,17 +736,15 @@ class CoreTests: XCTestCase {
|
||||
])
|
||||
}
|
||||
|
||||
func test_targetPosition_3positionsWithHidden() {
|
||||
func test_targetState_3positionsWithHidden() {
|
||||
class FloatingPanelLayout3PositionsWithHidden: FloatingPanelLayout {
|
||||
let initialState: FloatingPanelState = .hidden
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
@@ -705,11 +754,11 @@ class CoreTests: XCTestCase {
|
||||
XCTAssertEqual(fpc.state, .hidden)
|
||||
|
||||
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),
|
||||
])
|
||||
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: -1000.0), .full),
|
||||
(#line, fpc.surfaceView.frame.minY, CGPoint(x: 0.0, y: 0.0), .half),
|
||||
@@ -717,17 +766,15 @@ class CoreTests: XCTestCase {
|
||||
])
|
||||
}
|
||||
|
||||
func test_targetPosition_3positionsWithHiddenWithoutFull() {
|
||||
func test_targetState_3positionsWithHiddenWithoutFull() {
|
||||
class FloatingPanelLayout3Positions: FloatingPanelLayout {
|
||||
let initialState: FloatingPanelState = .hidden
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
@@ -743,7 +790,7 @@ class CoreTests: XCTestCase {
|
||||
//let hiddenPos = fpc.surfaceLocation(for: .hidden)
|
||||
|
||||
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: 0.0), .half),
|
||||
(#line, halfPos, CGPoint(x: 0.0, y: 385.0), .tip), // projection
|
||||
@@ -759,7 +806,7 @@ class CoreTests: XCTestCase {
|
||||
(#line, tipPos - 10.0, CGPoint(x: 0.0, y: 10.0), .tip), // redirection
|
||||
])
|
||||
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: -1000.0), .half),
|
||||
(#line, tipPos, CGPoint(x: 0.0, y: 0.0), .tip),
|
||||
@@ -773,6 +820,99 @@ class CoreTests: XCTestCase {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FloatingPanelLayout3Positions: FloatingPanelTestLayout {
|
||||
@@ -788,9 +928,9 @@ private class FloatingPanelLayout3PositionsBottomEdge: FloatingPanelTop2BottomTe
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
+99
-84
@@ -17,23 +17,19 @@ class LayoutTests: XCTestCase {
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.leastExpandedState, .tip)
|
||||
|
||||
class FloatingPanelLayoutWithHidden: FloatingPanelLayout {
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview)
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview)
|
||||
]
|
||||
let initialState: FloatingPanelState = .hidden
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
}
|
||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
let initialState: FloatingPanelState = .tip
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
}
|
||||
@@ -138,31 +134,31 @@ class LayoutTests: XCTestCase {
|
||||
var next: CGFloat
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||
overflow: false,
|
||||
scrollingContent: true,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, fullPos)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, fullPos + 100.0)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, tipPos)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
|
||||
overflow: false,
|
||||
scrollingContent: true,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, tipPos)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, tipPos + 100.0)
|
||||
@@ -190,31 +186,31 @@ class LayoutTests: XCTestCase {
|
||||
var next: CGFloat
|
||||
pre = fpc.surfaceLocation.y
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||
overflow: false,
|
||||
scrollingContent: true,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, pre)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, tipPos + 100.0)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, fullPos)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos + 100,
|
||||
overflow: false,
|
||||
scrollingContent: true,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, fullPos)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos + 100,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, fullPos + 100.0)
|
||||
@@ -224,12 +220,10 @@ class LayoutTests: XCTestCase {
|
||||
|
||||
func test_updateInteractiveEdgeConstraintWithHidden() {
|
||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
let initialState: FloatingPanelState = .hidden
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
}
|
||||
@@ -245,19 +239,19 @@ class LayoutTests: XCTestCase {
|
||||
var next: CGFloat
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||
overflow: false,
|
||||
scrollingContent: true,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, fullPos)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, fullPos - 100.0)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: hiddenPos - fullPos + 100.0,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, hiddenPos + 100.0)
|
||||
@@ -267,12 +261,10 @@ class LayoutTests: XCTestCase {
|
||||
|
||||
func test_updateInteractiveEdgeConstraintWithHidden_bottomEdge() {
|
||||
class FloatingPanelLayout2Positions: FloatingPanelLayout {
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
[
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .top, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .top, referenceGuide: .superview),
|
||||
]
|
||||
let initialState: FloatingPanelState = .hidden
|
||||
let position: FloatingPanelPosition = .top
|
||||
}
|
||||
@@ -288,19 +280,19 @@ class LayoutTests: XCTestCase {
|
||||
var next: CGFloat
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
||||
overflow: false,
|
||||
scrollingContent: true,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, fullPos)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, fullPos + 100.0)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: hiddenPos - fullPos + 100.0,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, hiddenPos + 100.0)
|
||||
@@ -310,12 +302,10 @@ class LayoutTests: XCTestCase {
|
||||
|
||||
func test_updateInteractiveTopConstraintWithMinusInsets() {
|
||||
class FloatingPanelLayoutMinusInsets: FloatingPanelLayout {
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
[
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
}
|
||||
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .bottom, referenceGuide: .safeArea),
|
||||
]
|
||||
let initialState: FloatingPanelState = .full
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
}
|
||||
@@ -328,19 +318,19 @@ class LayoutTests: XCTestCase {
|
||||
|
||||
var next: CGFloat
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||
overflow: false,
|
||||
scrollingContent: true,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, fullPos)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, fullPos - 100)
|
||||
|
||||
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
|
||||
overflow: true,
|
||||
scrollingContent: false,
|
||||
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
|
||||
next = fpc.surfaceLocation.y
|
||||
XCTAssertEqual(next, tipPos + 100)
|
||||
@@ -421,18 +411,18 @@ class LayoutTests: XCTestCase {
|
||||
for prop in [
|
||||
// from top edge
|
||||
(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),
|
||||
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),
|
||||
result: (#line, constant: 0.0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .superview),
|
||||
result: (#line, constant: 100.0, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||
// from bottom edge
|
||||
(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),
|
||||
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),
|
||||
result: (#line, constant: 0.0, firstAnchor: fpc.view.bottomAnchor, secondAnchor: fpc.surfaceView.bottomAnchor)),
|
||||
(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),
|
||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||
(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),
|
||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||
(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),
|
||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||
(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),
|
||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||
(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]
|
||||
XCTAssertEqual(c.multiplier, CGFloat(prop.result.multiplier), 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`
|
||||
// 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)
|
||||
XCTAssertEqual(c.secondAnchor, prop.result.secondAnchor, line: UInt(prop.result.0))
|
||||
}
|
||||
}
|
||||
func test_layoutAnchor_bottomPosition() {
|
||||
@@ -487,9 +472,9 @@ class LayoutTests: XCTestCase {
|
||||
for prop in [
|
||||
// from top edge
|
||||
(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),
|
||||
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),
|
||||
result: (#line, constant: 0.0, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||
(anchor: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .superview),
|
||||
@@ -497,9 +482,9 @@ class LayoutTests: XCTestCase {
|
||||
|
||||
// from bottom edge
|
||||
(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),
|
||||
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),
|
||||
result: (#line, constant: 0.0, firstAnchor: fpc.view.bottomAnchor, secondAnchor: fpc.surfaceView.topAnchor)),
|
||||
(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),
|
||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||
(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),
|
||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||
(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),
|
||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||
(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),
|
||||
result: (#line, multiplier: 1.0, secondAnchor: nil)),
|
||||
(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]
|
||||
XCTAssertEqual(c.multiplier, CGFloat(prop.result.multiplier), 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`
|
||||
// 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)
|
||||
XCTAssertEqual(c.secondAnchor, prop.result.secondAnchor, line: UInt(prop.result.0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,20 +547,20 @@ class LayoutTests: XCTestCase {
|
||||
|
||||
for prop in [
|
||||
(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),
|
||||
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),
|
||||
result: (#line, constant: 420, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 42.0, referenceGuide: .superview),
|
||||
result: (#line, constant: 420 - 42, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||
|
||||
(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),
|
||||
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),
|
||||
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),
|
||||
result: (#line, constant: 420, firstAnchor: fpc.surfaceView.bottomAnchor, secondAnchor: fpc.view.topAnchor)),
|
||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .superview),
|
||||
@@ -617,20 +597,20 @@ class LayoutTests: XCTestCase {
|
||||
|
||||
for prop in [
|
||||
(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),
|
||||
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),
|
||||
result: (#line, constant: -420, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.bottomAnchor)),
|
||||
(anchor: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 42.0, referenceGuide: .superview),
|
||||
result: (#line, constant: -420 + 42, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.bottomAnchor)),
|
||||
|
||||
(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),
|
||||
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),
|
||||
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),
|
||||
result: (#line, constant: -420, firstAnchor: fpc.surfaceView.topAnchor, secondAnchor: fpc.view.bottomAnchor)),
|
||||
(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))
|
||||
}
|
||||
}
|
||||
|
||||
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?)
|
||||
|
||||
+132
-47
@@ -48,35 +48,37 @@ class SurfaceViewTests: XCTestCase {
|
||||
}
|
||||
|
||||
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))
|
||||
surface.position = position
|
||||
surface.layoutIfNeeded()
|
||||
|
||||
let contentView = UIView()
|
||||
surface.set(contentView: contentView)
|
||||
surface.set(contentView: contentView, mode: mode)
|
||||
|
||||
let height = surface.bounds.height * 2
|
||||
surface.containerOverflow = height
|
||||
surface.setNeedsLayout()
|
||||
surface.layoutIfNeeded()
|
||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds)
|
||||
}
|
||||
|
||||
XCTContext.runActivity(named: "Top sheet") { _ in
|
||||
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
||||
surface.position = .top
|
||||
surface.layoutIfNeeded()
|
||||
|
||||
let contentView = UIView()
|
||||
surface.set(contentView: contentView)
|
||||
|
||||
let height = surface.bounds.height * 2
|
||||
surface.containerOverflow = height
|
||||
surface.setNeedsLayout()
|
||||
surface.layoutIfNeeded()
|
||||
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)
|
||||
switch position {
|
||||
case .top:
|
||||
XCTAssertEqual(surface.containerView.frame,
|
||||
CGRect(x: 0.0, y: -height, width: 320.0, height: 480.0 * 3),
|
||||
line: line)
|
||||
XCTAssertEqual(surface.convert(surface.contentView?.frame ?? .zero, from: surface.containerView),
|
||||
surface.bounds,
|
||||
line: line)
|
||||
case .bottom:
|
||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds, line: line)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,46 +142,45 @@ class SurfaceViewTests: XCTestCase {
|
||||
}
|
||||
|
||||
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))
|
||||
surface.position = .top
|
||||
surface.position = position
|
||||
let contentView = UIView()
|
||||
surface.set(contentView: contentView)
|
||||
surface.set(contentView: contentView, mode: mode)
|
||||
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.setNeedsLayout()
|
||||
surface.layoutIfNeeded()
|
||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds.inset(by: surface.contentPadding))
|
||||
}
|
||||
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))
|
||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds.inset(by: surface.contentPadding), line: line)
|
||||
}
|
||||
}
|
||||
|
||||
func test_surfaceView_containerMargins_and_contentInsets() {
|
||||
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.containerMargins = 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.layoutIfNeeded()
|
||||
XCTAssertEqual(surface.containerView.frame, surface.bounds.inset(by: surface.containerMargins))
|
||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.containerView.bounds.inset(by: surface.contentPadding))
|
||||
for (mode, line) in [
|
||||
(.static, #line),
|
||||
(.fitToBounds, #line),
|
||||
] as [(FloatingPanelController.ContentMode, UInt)] {
|
||||
let surface = SurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
|
||||
let contentView = UIView()
|
||||
surface.set(contentView: contentView, mode: mode)
|
||||
surface.layoutIfNeeded()
|
||||
XCTAssertEqual(surface.contentView?.frame ?? .zero, surface.bounds, line: line)
|
||||
surface.containerMargins = 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.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))
|
||||
XCTAssert(surface.containerView.layer.cornerRadius == 0.0)
|
||||
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.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)))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedTargetPosition: FloatingPanelState) -> Bool {
|
||||
func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedState: FloatingPanelState) -> Bool {
|
||||
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