Compare commits

..

40 Commits

Author SHA1 Message Date
Shin Yamamoto 4ab7d26030 Version 2.5.5 2022-10-15 19:39:31 +09:00
Shin Yamamoto 162545d7e9 Add test_initial_surface_position() 2022-10-15 09:30:44 +09:00
Shin Yamamoto 4904ea19cb Fix the stiff animation of "show detail panel" panel.
When a panel moves to tip position, the moving animation has been stiff.
This issue has happened since Xcode 14.
2022-10-15 09:19:01 +09:00
Shin Yamamoto b941f91556 Fix the backdrop alpha when the view size or its size class changes (#573)
This fixes #572 to change the backdrop alpha when the view size or 
its size class changes.

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

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

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

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

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

Additional improvements: 

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

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

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

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

Because CALayerCornerCurve is not marked with any @available annotation it is possible to declare private optional storage of it.
2022-06-13 19:54:54 +09:00
Shin Yamamoto 0bc9812c23 Update GitHub Actions workflow using macOS-12 (#538)
* Updated 'ci' workflow for Xcode 13.3.1
* Modernized and simplified the workflow using 'strategy.matrix'
* 'arm64-apple-ios15.4' target is not included in swiftpm because its build gets an build error.
2022-05-31 21:42:36 +09:00
Shin Yamamoto a4d98971b3 Fix a warning in SamplesObjC 2022-04-20 21:16:45 +09:00
little-huang 1b0ec64489 fix: add missing import UIKit (#534)
Co-authored-by: 黄宝成 <huangbc@publink.cn>
2022-04-20 20:29:28 +09:00
Shin Yamamoto 345e894007 Stop scrolling if the initial location of a pan gesture is in the grabber area 2022-02-21 21:16:39 +09:00
Shin Yamamoto 1fb1708df9 Fix the grabber area detecion 2022-02-19 08:08:56 +09:00
Shin Yamamoto b9edbf766e Merge pull request #528 from scenee/release/2.5.2
Release 2.5.2
2022-02-10 17:07:24 +09:00
Shin Yamamoto fe30e60235 Version 2.5.2 2022-02-04 23:59:07 +09:00
Jakub Dudek 6e7c31110f Fix dragging on content placed above the trackedView resets scroll position (#527)
Changed how `initialScrollOffset` is set. This issue was described in #526.
2022-02-04 23:56:46 +09:00
Shin Yamamoto e39f634d34 Unlock a scroll view when untracked 2021-12-04 11:12:11 +09:00
Shin Yamamoto 5851fd77c4 Merge pull request #521 from scenee/release/2.5.1
Release 2.5.1
2021-12-03 18:06:12 +09:00
Shin Yamamoto 0fbbbc8d99 Version 2.5.1 2021-11-29 20:46:32 +09:00
Shin Yamamoto 69078366de Change constraint priorities of the content view on the default content mode (#519)
They were set to `.required - 1` is #359, which fixed #294. However #294
was an issue on `.fitToBounds` content mode. But because of #444 and #515,
if your panel’s content mode is `.static`, their priorities should be set to
`.required` on `.static` content mode.
2021-11-29 20:40:26 +09:00
Shin Yamamoto 084b589a10 Fix invalidateLayout() implementation as following the doc comment (#510)
* Fix FloatingPanelController.invalidateLayout() implementation as following the doc comment
* Revise the doc comments of invalidateLayout() method, layout and behavior properties of FloatingPanelController.
* Add a note in README 
* Fix some grammar errors
2021-11-25 23:19:05 +09:00
Shin Yamamoto 07ae324586 Clean up the example projects and codes (#512)
* Clean up Samples and Maps examples
* Rename the root view controller in Maps/Stocks/SampleObjC examples
* Reorder resource file references
* Rename examples' bundle ids with `example` domain.
* Remove 'Run Script' to modify CFBundleVersion: now this has not been used for testing since unit tests were added
* Revise methods to handle content view controllers in UseCaseController
* Fix a bug on PagePanelController
2021-11-24 20:26:35 +09:00
Shin Yamamoto 819c87c530 Stop changing the content mode whose panel is attracting (#513)
This code causes #511. Unfortunately I don't remember the reason
why the example code do that (I should have added a comment...).
And it's a bit tricky for the example. So I remove it.
2021-11-16 08:29:43 +09:00
Shin Yamamoto 3bb9c4fd0f Apply a part of build settings recommended by Xcode 13 2021-10-30 11:19:52 +09:00
Shin Yamamoto 910a304849 Update README 2021-10-30 11:04:24 +09:00
Shin Yamamoto 2ecdbbae52 Add the documentation catalog 2021-10-11 16:33:16 +09:00
Shin Yamamoto fe2e3173b8 Support DocC symbols 2021-10-09 10:09:14 +09:00
Shin Yamamoto 21c6e3c715 Merge pull request #505 from scenee/release/2.5.0
Release 2.5.0
2021-09-28 20:44:31 +09:00
57 changed files with 1155 additions and 787 deletions
+86 -69
View File
@@ -10,94 +10,111 @@ on:
jobs:
build:
runs-on: macOS-11
runs-on: ${{ matrix.runsOn }}
env:
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
strategy:
fail-fast: false
matrix:
include:
- swift: "5.6"
xcode: "13.3.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-10.15
- swift: "5.2"
xcode: "11.7"
runsOn: macos-10.15
- swift: "5.1"
xcode: "11.3.1"
runsOn: macos-10.15
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@v2
- 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: "15.4"
xcode: "13.3.1"
sim: "iPhone 13 Pro"
runsOn: macos-12
- os: "15.2"
xcode: "13.2.1"
sim: "iPhone 13 Pro"
runsOn: macos-11
- os: "14.5"
xcode: "12.5.1"
sim: "iPhone 12 Pro"
runsOn: macos-11
- os: "14.4"
xcode: "12.4"
sim: "iPhone 12 Pro"
runsOn: macos-10.15
- os: "13.7"
xcode: "11.7"
sim: "iPhone 11 Pro"
runsOn: macos-10.15
steps:
- uses: actions/checkout@v1
- name: "Swift 5.1"
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.1 clean build
env:
DEVELOPER_DIR: /Applications/Xcode_11.3.1.app/Contents/Developer
- name: "Swift 5.2"
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.2 clean build
env:
DEVELOPER_DIR: /Applications/Xcode_11.7.app/Contents/Developer
- name: "Swift 5.3"
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=5.3 clean build
env:
DEVELOPER_DIR: /Applications/Xcode_12.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@v2
- 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 }}'
example:
runs-on: macOS-11
runs-on: macos-12
env:
DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer
DEVELOPER_DIR: /Applications/Xcode_13.3.1.app/Contents/Developer
strategy:
fail-fast: false
matrix:
include:
- example: "Maps"
- 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@v2
- 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_13.3.1.app/Contents/Developer
strategy:
fail-fast: false
matrix:
include:
- target: "arm64-apple-ios15.4-simulator"
- target: "x86_64-apple-ios15.4-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@v2
- 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@v2
- 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@v2
- name: "CocoaPods: pod lib lint"
run: pod lib lint --allow-warnings
- name: "CocoaPods: pod spec lint"
-3
View File
@@ -1,3 +0,0 @@
--header "// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license."
--disable andOperator,anyObjectProtocol,blankLinesAroundMark,blankLinesAtEndOfScope,blankLinesAtStartOfScope,blankLinesBetweenScopes,braces,consecutiveBlankLines,consecutiveSpaces,duplicateImports,elseOnSameLine,emptyBraces,hoistPatternLet,indent,isEmpty,leadingDelimiters,linebreakAtEndOfFile,linebreaks,numberFormatting,redundantBackticks,redundantBreak,redundantExtensionACL,redundantFileprivate,redundantGet,redundantInit,redundantLet,redundantLetError,redundantNilInit,redundantObjc,redundantParens,redundantPattern,redundantRawValues,redundantReturn,redundantSelf,redundantVoidReturnType,semicolons,sortedImports,spaceAroundBraces,spaceAroundBrackets,spaceAroundComments,spaceAroundGenerics,spaceAroundOperators,spaceAroundParens,spaceInsideBraces,spaceInsideBrackets,spaceInsideComments,spaceInsideGenerics,spaceInsideParens,specifiers,strongOutlets,strongifiedSelf,todos,trailingClosures,trailingCommas,trailingSpace,typeSugar,unusedArguments,void,wrapArguments,yodaConditions
@@ -364,7 +364,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.scenee.Maps-SwiftUI";
PRODUCT_BUNDLE_IDENTIFIER = "example.Maps-SwiftUI";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
@@ -391,7 +391,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.scenee.Maps-SwiftUI";
PRODUCT_BUNDLE_IDENTIFIER = "exmaple.Maps-SwiftUI";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
@@ -96,8 +96,20 @@ struct FloatingPanelView<Content: View, FloatingPanelContent: View>: UIViewContr
ignoresKeyboard: true
)
hostingViewController.view.backgroundColor = nil
fpc.set(contentViewController: hostingViewController)
fpc.addPanel(toParent: parentViewController, at: 1, animated: false)
let contentViewController = UIViewController()
contentViewController.view.addSubview(hostingViewController.view)
fpc.set(contentViewController: contentViewController)
fpc.addPanel(toParent: parentViewController, animated: false)
hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false
let bottomConstraint = hostingViewController.view.bottomAnchor.constraint(equalTo: contentViewController.view.bottomAnchor)
bottomConstraint.priority = .defaultHigh
NSLayoutConstraint.activate([
hostingViewController.view.topAnchor.constraint(equalTo: contentViewController.view.topAnchor),
hostingViewController.view.leadingAnchor.constraint(equalTo: contentViewController.view.leadingAnchor),
hostingViewController.view.trailingAnchor.constraint(equalTo: contentViewController.view.trailingAnchor),
bottomConstraint
])
}
func updateIfNeeded() {
@@ -1,6 +1,7 @@
// Copyright 2021 the FloatingPanel authors. All rights reserved. MIT license.
import FloatingPanel
import UIKit
final class SearchPanelPhoneDelegate: FloatingPanelControllerDelegate {
func floatingPanelWillBeginDragging(_ vc: FloatingPanelController) {
+24 -10
View File
@@ -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"
-4
View File
@@ -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
}
}
+32 -34
View File
@@ -1,28 +1,26 @@
<?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="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="17505"/>
<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="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="600" height="600"/>
<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="600" height="600"/>
</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="600" height="0.0"/>
<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="600" height="0.0"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<blurEffect style="prominent"/>
@@ -54,31 +52,31 @@
<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="600" height="600"/>
<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="600" height="600"/>
<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="600" height="600"/>
<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="600" height="51"/>
<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"/>
<rect key="frame" x="0.0" y="61" width="600" height="539"/>
<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="600" 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.5" width="552" height="97.5"/>
<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"/>
<rect key="frame" x="0.0" y="0.0" width="60" height="97.5"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="food" translatesAutoresizingMaskIntoConstraints="NO" id="ErN-bC-qTx">
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
@@ -88,7 +86,7 @@
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Food &amp; Drinks" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nx2-fW-xAm">
<rect key="frame" x="0.0" y="66" width="60" height="31.333333333333329"/>
<rect key="frame" x="0.0" y="66" width="60" height="31.5"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@@ -96,7 +94,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="164" y="0.0" width="60" height="97.5"/>
<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"/>
@@ -106,7 +104,7 @@
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="1000" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="H7q-q2-ga5">
<rect key="frame" x="0.0" y="66" width="60" height="31.333333333333329"/>
<rect key="frame" x="0.0" y="66" width="60" height="31.5"/>
<string key="text">Shopping
</string>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
@@ -116,7 +114,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="328" y="0.0" width="60" height="97.5"/>
<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"/>
@@ -126,7 +124,7 @@
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kKh-45-FZ2">
<rect key="frame" x="0.0" y="66" width="60" height="31.333333333333329"/>
<rect key="frame" x="0.0" y="66" width="60" height="31.5"/>
<string key="text">Fun
</string>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
@@ -136,7 +134,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="492" y="0.0" width="60" height="97.5"/>
<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"/>
@@ -146,7 +144,7 @@
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WBT-Vj-7QA">
<rect key="frame" x="0.0" y="66" width="60" height="31.333333333333329"/>
<rect key="frame" x="0.0" y="66" width="60" height="31.5"/>
<string key="text">Travel
</string>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
@@ -168,10 +166,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="160.5" width="600" 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="600" 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 +181,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="528" 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="528" 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="528" 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 +255,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="600" height="600"/>
<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="0.0" y="0.0" width="600" height="600"/>
<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="600" height="600"/>
<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="600" height="600"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableView>
</subviews>
@@ -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 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,9 +150,9 @@ 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
}
@@ -201,25 +204,16 @@ 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 [
@@ -239,20 +233,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 +254,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 +293,11 @@ class SearchPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestu
class SearchPanelPadLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .top
let initialState: FloatingPanelState = .tip
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.tip: FloatingPanelLayoutAnchor(absoluteInset: 80.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 200.0, edge: .top, referenceGuide: .superview),
.full: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.tip: FloatingPanelLayoutAnchor(absoluteInset: 80.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 200.0, edge: .top, referenceGuide: .superview),
.full: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
]
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.0
}
@@ -335,9 +325,9 @@ class SearchPaneliPadBehavior: FloatingPanelBehavior {
}
class DetailPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestureRecognizerDelegate {
unowned let owner: ViewController
unowned let owner: MainViewController
init(owner: ViewController) {
init(owner: MainViewController) {
self.owner = owner
}
@@ -361,11 +351,9 @@ class DetailPanelPadDelegate: NSObject, FloatingPanelControllerDelegate, UIGestu
class DetailPanelPadLeftLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .left
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .left, referenceGuide: .superview)
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .left, referenceGuide: .superview)
]
let initialState: FloatingPanelState = .full
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.0
@@ -374,11 +362,9 @@ class DetailPanelPadLeftLayout: FloatingPanelLayout {
class DetailPanelPadRightLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .right
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .right, referenceGuide: .superview)
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 375, edge: .right, referenceGuide: .superview)
]
let initialState: FloatingPanelState = .full
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.0
@@ -387,8 +373,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 +386,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
@@ -30,9 +30,10 @@
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 */
@@ -95,9 +96,10 @@
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,6 +107,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5D82A6AD28D1843C006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
549D23CB233C7779008EF4D7 /* FloatingPanel.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -126,7 +129,7 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
5442E22225FC519700A26F43 /* ViewControllers */ = {
5442E22225FC519700A26F43 /* ContentViewControllers */ = {
isa = PBXGroup;
children = (
5442E23F25FC533800A26F43 /* DebugTableViewController.swift */,
@@ -140,7 +143,7 @@
5442E22B25FC521F00A26F43 /* SettingsViewController.swift */,
5442E22F25FC525200A26F43 /* TabBarViewController.swift */,
);
path = ViewControllers;
path = ContentViewControllers;
sourceTree = "<group>";
};
545DB9E121511E6300CA77B8 = {
@@ -151,6 +154,7 @@
545DBA0121511E6400CA77B8 /* Tests */,
545DBA0C21511E6400CA77B8 /* UITests */,
545DB9EB21511E6300CA77B8 /* Products */,
5D82A6AB28D18438006A44BA /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -172,12 +176,12 @@
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;
@@ -205,12 +209,20 @@
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 +230,6 @@
isa = PBXNativeTarget;
buildConfigurationList = 545DBA1221511E6400CA77B8 /* Build configuration list for PBXNativeTarget "Samples" */;
buildPhases = (
54D7209621D4DB970054A255 /* ShellScript */,
545DB9E621511E6300CA77B8 /* Sources */,
545DB9E721511E6300CA77B8 /* Frameworks */,
545DB9E821511E6300CA77B8 /* Resources */,
@@ -276,7 +287,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1000;
LastUpgradeCheck = 1000;
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = scenee;
TargetAttributes = {
545DB9E921511E6300CA77B8 = {
@@ -339,26 +350,6 @@
};
/* 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,7 +357,7 @@
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 */,
5442E24A25FC53C100A26F43 /* DebugTextViewController.swift in Sources */,
546341AC25C6426500CA0596 /* CustomState.swift in Sources */,
@@ -379,7 +370,7 @@
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 */,
@@ -463,6 +454,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 +516,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;
@@ -565,7 +558,11 @@
"$(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";
@@ -584,7 +581,11 @@
"$(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";
@@ -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="21225" 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="21207"/>
<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">
@@ -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,22 +383,22 @@
<objects>
<viewController storyboardIdentifier="ModalViewController" id="bYI-y3-Rzb" customClass="ModalViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="qwo-GK-p1U">
<rect key="frame" x="0.0" y="0.0" width="375" height="778"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="720"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vut-mK-Y4t" customClass="SafeAreaView" customModule="Samples" customModuleProvider="target">
<rect key="frame" x="0.0" y="758" width="375" height="0.0"/>
<rect key="frame" x="0.0" y="720" width="375" height="0.0"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sbF-Az-7sy">
<rect key="frame" x="20" y="0.0" width="39" height="30"/>
<rect key="frame" x="20" y="48" width="39" height="30"/>
<state key="normal" title="Close"/>
<connections>
<action selector="closeWithSender:" destination="bYI-y3-Rzb" eventType="touchUpInside" id="MSC-ch-YJK"/>
</connections>
</button>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="44" translatesAutoresizingMaskIntoConstraints="NO" id="9p4-06-y2T">
<rect key="frame" x="134.5" y="88" width="106" height="326"/>
<rect key="frame" x="134.5" y="136" width="106" height="326"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="i9x-x5-n1q">
<rect key="frame" x="0.0" y="0.0" width="80" height="30"/>
@@ -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="48" width="375" height="730"/>
<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="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>
@@ -781,6 +783,11 @@ Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
<point key="canvasLocation" x="-1" 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 +803,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>
@@ -53,6 +53,7 @@ class DebugTableViewController: InspectableViewController {
// MARK: - Properties
var kvoObservers: [NSKeyValueObservation] = []
private lazy var items: [String] = {
let items = (0..<100).map { "Items \($0)" }
return Command.replace(items: items)
@@ -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)
]
}
}
@@ -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)
]
}
}
@@ -195,14 +195,12 @@ class OneTabBarPanelLayout: FloatingPanelLayout {
}
class TwoTabBarPanelLayout: FloatingPanelLayout {
var initialState: FloatingPanelState { .half }
var position: FloatingPanelPosition { .bottom }
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 261.0, edge: .bottom, referenceGuide: .safeArea)
]
}
let initialState: FloatingPanelState = .half
let position: FloatingPanelPosition = .bottom
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(absoluteInset: 261.0, edge: .bottom, referenceGuide: .safeArea)
]
}
class TwoTabBarPanelBehavior: FloatingPanelBehavior {
@@ -6,9 +6,10 @@ 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
@@ -21,8 +22,6 @@ final class MainViewController: UIViewController {
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
navigationItem.largeTitleDisplayMode = .automatic
} else {
// Fallback on earlier versions
}
var insets = UIEdgeInsets.zero
insets.bottom += 69.0
@@ -47,8 +46,9 @@ final class MainViewController: UIViewController {
super.viewWillDisappear(animated)
observations.removeAll()
}
}
// MARK:- Actions
extension MainViewController {
@IBAction func showDebugMenu(_ sender: UIBarButtonItem) {
useCaseController.setUpSettingsPanel(for: self)
}
@@ -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
@@ -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)
+38 -24
View File
@@ -1,6 +1,6 @@
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
import Foundation
import UIKit
enum UseCase: Int, CaseIterable {
case trackingTableView
@@ -23,7 +23,9 @@ enum UseCase: Int, CaseIterable {
case showAdaptivePanel
case showAdaptivePanelWithCustomGuide
case showCustomStatePanel
}
extension UseCase {
var name: String {
switch self {
case .trackingTableView: return "Scroll tracking(TableView)"
@@ -49,30 +51,42 @@ enum UseCase: Int, CaseIterable {
}
}
var storyboardID: String? {
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 .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: ImageViewController.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):
return vc
}
}
}
@@ -5,48 +5,83 @@ 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] = []
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 +92,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")
@@ -96,6 +173,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 +188,6 @@ final class UseCaseController: NSObject {
case .showContainerMargins:
let fpc = FloatingPanelController()
let appearance = SurfaceAppearance()
appearance.cornerRadius = 38.5
fpc.surfaceView.appearance = appearance
@@ -126,81 +203,49 @@ 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
case .showAdaptivePanel, .showAdaptivePanelWithCustomGuide:
let fpc = FloatingPanelController()
fpc.isRemovalInteractionEnabled = true
fpc.set(contentViewController: contentVC)
fpc.ext_trackScrollView(in: contentVC)
if case let contentVC as ImageViewController = contentVC {
if #available(iOS 11.0, *) {
let mode: ImageViewController.Mode = (useCase == .showAdaptivePanelWithCustomGuide) ? .withHeaderFooter : .onlyImage
let layoutGuide = contentVC.layoutGuideFor(mode: mode)
fpc.layout = ImageViewController.PanelLayout(targetGuide: layoutGuide)
} else {
fpc.layout = ImageViewController.PanelLayout(targetGuide: nil)
}
}
mainPanelObserves.append(ob)
mainPanelVC.track(scrollView: contentVC.tableView)
case let contentVC as NestedScrollViewController:
mainPanelVC.track(scrollView: contentVC.scrollView)
case let navVC as UINavigationController:
if let rootVC = (navVC.topViewController as? MainViewController) {
rootVC.loadViewIfNeeded()
mainPanelVC.track(scrollView: rootVC.tableView)
}
case let contentVC as ImageViewController:
if #available(iOS 11.0, *) {
let mode: ImageViewController.Mode = (useCase == .showAdaptivePanelWithCustomGuide) ? .withHeaderFooter : .onlyImage
let layoutGuide = contentVC.layoutGuideFor(mode: mode)
mainPanelVC.layout = ImageViewController.PanelLayout(targetGuide: layoutGuide)
} else {
mainPanelVC.layout = ImageViewController.PanelLayout(targetGuide: nil)
}
mainPanelVC.delegate = nil
mainPanelVC.isRemovalInteractionEnabled = true
mainPanelVC.track(scrollView: contentVC.scrollView)
default:
break
}
}
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 .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,6 +270,28 @@ 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 {
@@ -274,54 +341,42 @@ 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)
default:
break
}
@@ -8,13 +8,14 @@
/* 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 */; };
545BA71421BA3217007F7846 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 545BA71321BA3217007F7846 /* main.m */; };
545BA72621BA3BAF007F7846 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 545BA72521BA3BAF007F7846 /* FloatingPanel.framework */; };
545BA72721BA3BAF007F7846 /* FloatingPanel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 545BA72521BA3BAF007F7846 /* FloatingPanel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5D82A6B028D18447006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6AF28D18443006A44BA /* libswiftCoreGraphics.tbd */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -35,8 +36,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 +45,7 @@
545BA71321BA3217007F7846 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
545BA72221BA3867007F7846 /* SamplesObjC-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SamplesObjC-Bridging-Header.h"; sourceTree = "<group>"; };
545BA72521BA3BAF007F7846 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5D82A6AF28D18443006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -51,6 +53,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5D82A6B028D18447006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
545BA72621BA3BAF007F7846 /* FloatingPanel.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -64,6 +67,7 @@
545BA72521BA3BAF007F7846 /* FloatingPanel.framework */,
545BA70321BA3214007F7846 /* SamplesObjC */,
545BA70221BA3214007F7846 /* Products */,
5D82A6AE28D18443006A44BA /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -78,13 +82,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 +96,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 +131,7 @@
545BA6F921BA3214007F7846 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1110;
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "Shin Yamamoto";
TargetAttributes = {
545BA70021BA3214007F7846 = {
@@ -164,7 +176,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 +230,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 +290,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 +332,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 +358,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,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"/>
@@ -3,7 +3,7 @@
#import <UIKit/UIKit.h>
@import FloatingPanel;
@interface ViewController : UIViewController
@interface MainViewController : UIViewController
@end
@interface MyFloatingPanelLayout : NSObject <FloatingPanelLayout>
@@ -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>
@@ -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 -1
View File
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "FloatingPanel"
s.version = "2.5.0"
s.version = "2.5.5"
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.
+20 -1
View File
@@ -33,6 +33,8 @@
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 */
@@ -83,7 +85,9 @@
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>"; };
54E3992627141F5100A8F9ED /* FloatingPanel.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = FloatingPanel.docc; sourceTree = "<group>"; };
54E740CA218AFD67005C1A34 /* FloatingPanelTesting.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FloatingPanelTesting.app; sourceTree = BUILT_PRODUCTS_DIR; };
5D82A6B428D18461006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -91,6 +95,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5D82A6B528D18464006A44BA /* libswiftCoreGraphics.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -118,6 +123,7 @@
545DB9C32151169500CA77B8 /* Sources */,
545DB9CE2151169500CA77B8 /* Tests */,
545DB9C22151169500CA77B8 /* Products */,
5D82A6B328D18460006A44BA /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -151,6 +157,7 @@
54ABD7AE216CCFF7002E6C13 /* Logger.swift */,
545DB9C42151169500CA77B8 /* FloatingPanel.h */,
545DB9C52151169500CA77B8 /* Info.plist */,
54E3992627141F5100A8F9ED /* FloatingPanel.docc */,
);
path = Sources;
sourceTree = "<group>";
@@ -181,6 +188,14 @@
path = TestingApp;
sourceTree = "<group>";
};
5D82A6B328D18460006A44BA /* Frameworks */ = {
isa = PBXGroup;
children = (
5D82A6B428D18461006A44BA /* libswiftCoreGraphics.tbd */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@@ -256,7 +271,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1010;
LastUpgradeCheck = 1000;
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = scenee;
TargetAttributes = {
545DB9C02151169500CA77B8 = {
@@ -335,6 +350,7 @@
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 */,
);
@@ -404,6 +420,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 +486,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;
@@ -666,6 +684,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;
@@ -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"
+23 -27
View File
@@ -9,6 +9,8 @@
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.
📝[Here](https://docs.scenee.com/documentation/floatingpanel) is the API references for the latest version powered by [DocC](https://developer.apple.com/documentation/docc).
![Maps](https://github.com/SCENEE/FloatingPanel/blob/master/assets/maps.gif)
![Stocks](https://github.com/SCENEE/FloatingPanel/blob/master/assets/stocks.gif)
@@ -267,13 +269,11 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
class MyFloatingPanelLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .tip
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea),
]
}
```
@@ -288,6 +288,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 +319,11 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
class LandscapePanelLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .tip
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
]
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
return [
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 8.0),
@@ -341,12 +342,10 @@ class LandscapePanelLayout: FloatingPanelLayout {
class IntrinsicPanelLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .full
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0, referenceGuide: .safeArea),
.half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0, referenceGuide: .safeArea),
.half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
]
...
}
```
@@ -360,14 +359,11 @@ Use `.superview` reference guide in your anchors.
```swift
class MyFullScreenLayout: FloatingPanelLayout {
...
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .superview),
]
}
```
+1 -1
View File
@@ -46,7 +46,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 }
}
+24 -15
View File
@@ -65,7 +65,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
@@ -157,7 +157,10 @@ 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 }
@@ -169,7 +172,7 @@ open class FloatingPanelController: UIViewController {
}
}
/// The behavior object managed by the controller
/// The behavior object that the controller manages
@objc
public var behavior: FloatingPanelBehavior {
get { _behavior }
@@ -189,7 +192,7 @@ 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.
/// 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
@@ -313,7 +316,7 @@ open class FloatingPanelController: UIViewController {
// Change a layout for the new view size
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: size) {
layout = newLayout
activateLayout(forceLayout: false)
activateLayout(forceLayout: true)
}
if view.translatesAutoresizingMaskIntoConstraints {
@@ -332,7 +335,7 @@ open class FloatingPanelController: UIViewController {
// Change a layout for the new trait collection
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: newCollection) {
self.layout = newLayout
activateLayout(forceLayout: false)
activateLayout(forceLayout: true)
}
}
@@ -398,8 +401,10 @@ open class FloatingPanelController: UIViewController {
}
private func activateLayout(forceLayout: Bool = false) {
floatingPanel.activateLayout(forceLayout: forceLayout,
contentInsetAdjustmentBehavior: contentInsetAdjustmentBehavior)
floatingPanel.activateLayout(
forceLayout: forceLayout,
contentInsetAdjustmentBehavior: contentInsetAdjustmentBehavior
)
}
func remove() {
@@ -548,7 +553,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)
}
@@ -603,15 +608,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)
}
+45 -29
View File
@@ -17,6 +17,19 @@ class Core: NSObject, UIGestureRecognizerDelegate {
didSet {
oldValue?.panGestureRecognizer.removeTarget(self, action: nil)
scrollView?.panGestureRecognizer.addTarget(self, action: #selector(handle(panGesture:)))
if let cur = scrollView {
if oldValue == nil {
initialScrollOffset = cur.contentOffset
scrollBounce = cur.bounces
scrollIndictorVisible = cur.showsVerticalScrollIndicator
}
} else {
if let pre = oldValue {
pre.isDirectionalLockEnabled = false
pre.bounces = scrollBounce
pre.showsVerticalScrollIndicator = scrollIndictorVisible
}
}
}
}
@@ -53,9 +66,6 @@ class Core: NSObject, UIGestureRecognizerDelegate {
private var stopScrollDeceleration: Bool = false
private var scrollBounce = false
private var scrollIndictorVisible = false
private var grabberAreaFrame: CGRect {
return surfaceView.grabberAreaFrame
}
// MARK: - Interface
@@ -63,6 +73,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
ownerVC = vc
surfaceView = SurfaceView()
surfaceView.position = layout.position
surfaceView.backgroundColor = .white
backdropView = BackdropView()
@@ -186,8 +197,10 @@ class Core: NSObject, UIGestureRecognizerDelegate {
// MARK: - Layout update
func activateLayout(forceLayout: Bool = false,
contentInsetAdjustmentBehavior: FloatingPanelController.ContentInsetAdjustmentBehavior) {
func activateLayout(
forceLayout: Bool = false,
contentInsetAdjustmentBehavior: FloatingPanelController.ContentInsetAdjustmentBehavior
) {
layoutAdapter.prepareLayout()
// preserve the current content offset if contentInsetAdjustmentBehavior is `.always`
@@ -197,7 +210,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
}
layoutAdapter.updateStaticConstraint()
layoutAdapter.activateLayout(for: state, forceLayout: true)
layoutAdapter.activateLayout(for: state, forceLayout: forceLayout)
// Update the backdrop alpha only when called in `Controller.show(animated:completion:)`
// Because that prevents a backdrop flicking just before presenting a panel(#466).
@@ -264,7 +277,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
is UIRotationGestureRecognizer,
is UIScreenEdgePanGestureRecognizer,
is UIPinchGestureRecognizer:
if grabberAreaFrame.contains(gestureRecognizer.location(in: gestureRecognizer.view)) {
if surfaceView.grabberAreaContains(gestureRecognizer.location(in: surfaceView)) {
return true
}
// all gestures of the tracking scroll view should be recognized in parallel
@@ -295,7 +308,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
return true
}
if grabberAreaFrame.contains(gestureRecognizer.location(in: gestureRecognizer.view)) {
if surfaceView.grabberAreaContains(gestureRecognizer.location(in: surfaceView)) {
return true
}
@@ -323,7 +336,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
scrollGestureRecognizers.contains(otherGestureRecognizer) {
switch otherGestureRecognizer {
case scrollView.panGestureRecognizer:
if grabberAreaFrame.contains(gestureRecognizer.location(in: gestureRecognizer.view)) {
if surfaceView.grabberAreaContains(gestureRecognizer.location(in: surfaceView)) {
return false
}
return allowScrollPanGesture(for: scrollView)
@@ -351,7 +364,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
// Should begin the pan gesture without waiting the dismiss gesture of a sheet modal.
return false
}
if grabberAreaFrame.contains(gestureRecognizer.location(in: gestureRecognizer.view)) {
if surfaceView.grabberAreaContains(gestureRecognizer.location(in: surfaceView)) {
return false
}
// Do not begin the pan gesture until these gestures fail
@@ -396,7 +409,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
log.debug("settle offset --", value(of: initialScrollOffset))
stopScrolling(at: initialScrollOffset)
} else {
if grabberAreaFrame.contains(location) {
if surfaceView.grabberAreaContains(location) {
// Preserve the current content offset in moving from full.
stopScrolling(at: initialScrollOffset)
}
@@ -439,7 +452,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
}
if state == layoutAdapter.mostExpandedState {
// Adjust a small gap of the scroll offset just after swiping down starts in the grabber area.
if grabberAreaFrame.contains(location), grabberAreaFrame.contains(initialLocation) {
if surfaceView.grabberAreaContains(location), surfaceView.grabberAreaContains(initialLocation) {
stopScrolling(at: initialScrollOffset)
}
}
@@ -464,7 +477,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
}
}
// Adjust a small gap of the scroll offset just before swiping down starts in the grabber area,
if grabberAreaFrame.contains(location), grabberAreaFrame.contains(initialLocation) {
if surfaceView.grabberAreaContains(location), surfaceView.grabberAreaContains(initialLocation) {
stopScrolling(at: initialScrollOffset)
}
}
@@ -511,7 +524,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
// doesn't pass through .changed state after an interruptible animator is interrupted.
let diff = translation - .leastNonzeroMagnitude
layoutAdapter.updateInteractiveEdgeConstraint(diff: value(of: diff),
overflow: true,
scrollingContent: true,
allowsRubberBanding: behaviorAdapter.allowsRubberBanding(for:))
}
panningEnd(with: translation, velocity: velocity)
@@ -572,19 +585,19 @@ class Core: NSObject, UIGestureRecognizerDelegate {
}
// When the current point is within grabber area but the initial point is not, do scroll.
if grabberAreaFrame.contains(point), !grabberAreaFrame.contains(initialLocation) {
if surfaceView.grabberAreaContains(point), !surfaceView.grabberAreaContains(initialLocation) {
return true
}
// When the initial point is within grabber area and the current point is out of surface, don't scroll.
if grabberAreaFrame.contains(initialLocation), !surfaceView.frame.contains(point) {
if surfaceView.grabberAreaContains(initialLocation), !surfaceView.frame.contains(point) {
return false
}
let scrollViewFrame = scrollView.convert(scrollView.bounds, to: surfaceView)
guard
scrollViewFrame.contains(initialLocation), // When the initial point not in scrollView, don't scroll.
!grabberAreaFrame.contains(point) // When point within grabber area, don't scroll.
!surfaceView.grabberAreaContains(point) // When point within grabber area, don't scroll.
else {
return false
}
@@ -627,7 +640,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
guard let scrollView = scrollView else { return }
if state == layoutAdapter.mostExpandedState {
if grabberAreaFrame.contains(location) {
if surfaceView.grabberAreaContains(location) {
initialScrollOffset = scrollView.contentOffset
}
} else {
@@ -640,10 +653,9 @@ class Core: NSObject, UIGestureRecognizerDelegate {
let pre = value(of: layoutAdapter.surfaceLocation)
let diff = value(of: translation - initialTranslation)
let next = pre + diff
let overflow = shouldOverflow(from: pre, to: next)
layoutAdapter.updateInteractiveEdgeConstraint(diff: diff,
overflow: overflow,
scrollingContent: shouldScrollingContentInMoving(from: pre, to: next),
allowsRubberBanding: behaviorAdapter.allowsRubberBanding(for:))
let cur = value(of: layoutAdapter.surfaceLocation)
@@ -657,32 +669,36 @@ class Core: NSObject, UIGestureRecognizerDelegate {
}
}
private func shouldOverflow(from pre: CGFloat, to next: CGFloat) -> Bool {
private func shouldScrollingContentInMoving(from pre: CGFloat, to next: CGFloat) -> Bool {
// Don't allow scrolling if the initial panning location is in the grabber area.
if surfaceView.grabberAreaContains(initialLocation) {
return false
}
if let scrollView = scrollView, scrollView.panGestureRecognizer.state == .changed {
switch layoutAdapter.position {
case .top:
if pre > .zero, pre < next,
scrollView.contentSize.height > scrollView.bounds.height || scrollView.alwaysBounceVertical {
return false
return true
}
case .left:
if pre > .zero, pre < next,
scrollView.contentSize.width > scrollView.bounds.width || scrollView.alwaysBounceHorizontal {
return false
return true
}
case .bottom:
if pre > .zero, pre > next,
scrollView.contentSize.height > scrollView.bounds.height || scrollView.alwaysBounceVertical {
return false
return true
}
case .right:
if pre > .zero, pre > next,
scrollView.contentSize.width > scrollView.bounds.width || scrollView.alwaysBounceHorizontal {
return false
return true
}
}
}
return true
return false
}
private func panningEnd(with translation: CGPoint, velocity: CGPoint) {
@@ -697,7 +713,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
if stopScrollDeceleration {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.stopScrolling(at: self.initialScrollOffset)
}
}
@@ -785,10 +801,10 @@ class Core: NSObject, UIGestureRecognizerDelegate {
initialSurfaceLocation = layoutAdapter.surfaceLocation
if state == layoutAdapter.mostExpandedState, let scrollView = scrollView {
if grabberAreaFrame.contains(location) {
if surfaceView.grabberAreaContains(location) {
initialScrollOffset = scrollView.contentOffset
} else {
initialScrollOffset = contentOffsetForPinning(of: scrollView)
initialScrollOffset = scrollView.contentOffset
let offsetDiff = scrollView.contentOffset - contentOffsetForPinning(of: scrollView)
switch layoutAdapter.position {
case .top, .left:
@@ -0,0 +1,51 @@
# ``FloatingPanel``
The new interface displays the related contents and utilities in parallel as a user wants.
## Overview
FloatingPanel is a simple and easy-to-use UI component for a new interface introduced in Apple Maps, Shortcuts and Stocks app.
The new interface displays the related contents and utilities in parallel as a user wants.
## 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``
### Behaviors
- ``FloatingPanelBehavior``
- ``FloatingPanelDefaultBehavior``
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.5.0</string>
<string>2.5.5</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
+2 -2
View File
@@ -678,7 +678,7 @@ class LayoutAdapter {
surfaceView.containerOverflow = position.mainDimension(vc.view.bounds.size)
}
func updateInteractiveEdgeConstraint(diff: CGFloat, overflow: Bool, allowsRubberBanding: (UIRectEdge) -> Bool) {
func updateInteractiveEdgeConstraint(diff: CGFloat, scrollingContent: Bool, allowsRubberBanding: (UIRectEdge) -> Bool) {
defer {
log.debug("update surface location = \(surfaceLocation)")
}
@@ -701,7 +701,7 @@ class LayoutAdapter {
const = maxConst + rubberBandEffect(for: buffer, base: base)
}
if overflow == false {
if scrollingContent {
const = min(max(const, minConst), maxConst)
}
+34 -6
View File
@@ -54,7 +54,12 @@ public class SurfaceAppearance: NSObject {
///
/// Defaults to `.circular`.
@available(iOS 13.0, *)
public lazy var cornerCurve: CALayerCornerCurve = .circular
public var cornerCurve: CALayerCornerCurve {
get { _cornerCurve ?? .circular }
set { _cornerCurve = newValue }
}
private var _cornerCurve: CALayerCornerCurve?
/// An array of shadows used to create drop shadows underneath a surface view.
public var shadows: [Shadow] = [Shadow()]
@@ -85,7 +90,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()
} }
@@ -408,12 +413,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 +428,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 +447,22 @@ public class SurfaceView: UIView {
func hasStackView() -> Bool {
return contentView?.subviews.reduce(false) { $0 || ($1 is UIStackView) } ?? false
}
func grabberAreaContains(_ location: CGPoint) -> Bool {
// Sometimes a dragging finger's location is out of surface frame.
let cappedLocation: CGPoint
// Because the maximum width / height is out of bounds in CGRect.contains(_:)
let adjustment = 1 / fp_displayScale
switch position {
case .top:
cappedLocation = CGPoint(x: location.x, y: min(location.y, bounds.height - adjustment))
case .left:
cappedLocation = CGPoint(x: min(location.x, bounds.width - adjustment), y: location.y)
case .bottom:
cappedLocation = CGPoint(x: location.x, y: max(location.y, 0))
case .right:
cappedLocation = CGPoint(x: max(location.x, 0), y: location.y)
}
return grabberAreaFrame.contains(cappedLocation)
}
}
+4 -1
View File
@@ -104,7 +104,10 @@ class ModalPresentTransition: NSObject, UIViewControllerAnimatedTransitioning {
fpc?.suspendTransitionAnimator(false)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
return fpc.transitionAnimator!
guard let transitionAnimator = fpc.transitionAnimator else {
fatalError("The panel state must be `hidden` but it is `\(fpc.state)`")
}
return transitionAnimator
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
+2 -4
View File
@@ -336,8 +336,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 +346,5 @@ private class MyZombieViewController: UIViewController, FloatingPanelLayout, Flo
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0,
edge: .bottom,
referenceGuide: .superview),
]
}
]
}
+91 -44
View File
@@ -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_initial_surface_position() {
class FloatingPanelTestDelegate: FloatingPanelControllerDelegate {
class Layout: FloatingPanelLayout {
let initialState: FloatingPanelState = .full
let position: FloatingPanelPosition = .top
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring]
= [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .bottom, referenceGuide: .superview)]
}
func floatingPanel(_ fpc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
Layout()
}
}
do {
let delegate = FloatingPanelTestDelegate()
let fpc = FloatingPanelController(delegate: delegate)
XCTAssertEqual(fpc.surfaceView.position, .top)
}
do {
let fpc = FloatingPanelController()
fpc.layout = FloatingPanelTestDelegate.Layout()
XCTAssertEqual(fpc.surfaceView.position, .top)
}
}
func test_targetPosition_1positions() {
class FloatingPanelLayout1Positions: FloatingPanelLayout {
let initialState: FloatingPanelState = .full
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring]
= [.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview)]
}
let delegate = FloatingPanelTestDelegate()
@@ -269,12 +324,10 @@ class CoreTests: XCTestCase {
class FloatingPanelLayout2Positions: FloatingPanelLayout {
let initialState: FloatingPanelState = .half
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
]
}
let delegate = FloatingPanelTestDelegate()
@@ -326,12 +379,10 @@ class CoreTests: XCTestCase {
class FloatingPanelLayout2Positions: FloatingPanelLayout {
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let delegate = FloatingPanelTestDelegate()
@@ -689,13 +740,11 @@ class CoreTests: XCTestCase {
class FloatingPanelLayout3PositionsWithHidden: FloatingPanelLayout {
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 20.0, edge: .top, referenceGuide: .superview),
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let delegate = FloatingPanelTestDelegate()
let fpc = FloatingPanelController(delegate: delegate)
@@ -721,13 +770,11 @@ class CoreTests: XCTestCase {
class FloatingPanelLayout3Positions: FloatingPanelLayout {
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .bottom
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
.half: FloatingPanelLayoutAnchor(absoluteInset: 250.0, edge: .bottom, referenceGuide: .superview),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 60.0, edge: .bottom, referenceGuide: .superview),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let delegate = FloatingPanelTestDelegate()
+40 -50
View File
@@ -17,23 +17,19 @@ class LayoutTests: XCTestCase {
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.leastExpandedState, .tip)
class FloatingPanelLayoutWithHidden: FloatingPanelLayout {
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview)
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview)
]
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .bottom
}
class FloatingPanelLayout2Positions: FloatingPanelLayout {
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
]
let initialState: FloatingPanelState = .tip
let position: FloatingPanelPosition = .bottom
}
@@ -138,31 +134,31 @@ class LayoutTests: XCTestCase {
var next: CGFloat
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos + 100.0)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, tipPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, tipPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, tipPos + 100.0)
@@ -190,31 +186,31 @@ class LayoutTests: XCTestCase {
var next: CGFloat
pre = fpc.surfaceLocation.y
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, pre)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, tipPos + 100.0)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos + 100,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: fullPos - tipPos + 100,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos + 100.0)
@@ -224,12 +220,10 @@ class LayoutTests: XCTestCase {
func test_updateInteractiveEdgeConstraintWithHidden() {
class FloatingPanelLayout2Positions: FloatingPanelLayout {
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .bottom, referenceGuide: .superview),
]
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .bottom
}
@@ -245,19 +239,19 @@ class LayoutTests: XCTestCase {
var next: CGFloat
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos - 100.0)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: hiddenPos - fullPos + 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, hiddenPos + 100.0)
@@ -267,12 +261,10 @@ class LayoutTests: XCTestCase {
func test_updateInteractiveEdgeConstraintWithHidden_bottomEdge() {
class FloatingPanelLayout2Positions: FloatingPanelLayout {
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
[
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .top, referenceGuide: .superview),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: 18.0, edge: .bottom, referenceGuide: .safeArea),
.hidden: FloatingPanelLayoutAnchor(absoluteInset: 0, edge: .top, referenceGuide: .superview),
]
let initialState: FloatingPanelState = .hidden
let position: FloatingPanelPosition = .top
}
@@ -288,19 +280,19 @@ class LayoutTests: XCTestCase {
var next: CGFloat
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos + 100.0)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: hiddenPos - fullPos + 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, hiddenPos + 100.0)
@@ -310,12 +302,10 @@ class LayoutTests: XCTestCase {
func test_updateInteractiveTopConstraintWithMinusInsets() {
class FloatingPanelLayoutMinusInsets: FloatingPanelLayout {
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
[
.full: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .bottom, referenceGuide: .safeArea),
]
}
let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [
.full: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .top, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: -200, edge: .bottom, referenceGuide: .safeArea),
]
let initialState: FloatingPanelState = .full
let position: FloatingPanelPosition = .bottom
}
@@ -328,19 +318,19 @@ class LayoutTests: XCTestCase {
var next: CGFloat
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: false,
scrollingContent: true,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: -100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, fullPos - 100)
fpc.floatingPanel.layoutAdapter.updateInteractiveEdgeConstraint(diff: tipPos - fullPos + 100.0,
overflow: true,
scrollingContent: false,
allowsRubberBanding: fpc.floatingPanel.behaviorAdapter.allowsRubberBanding(for:))
next = fpc.surfaceLocation.y
XCTAssertEqual(next, tipPos + 100)
+132 -47
View File
@@ -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)))
}
}
}
+22
View File
@@ -77,3 +77,25 @@ class FloatingPanelProjectableBehavior: FloatingPanelBehavior {
return true
}
}
class MockTransitionCoordinator: NSObject, UIViewControllerTransitionCoordinator {
func animate(alongsideTransition animation: ((UIViewControllerTransitionCoordinatorContext) -> Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Void)? = nil) -> Bool { true }
func animateAlongsideTransition(in view: UIView?, animation: ((UIViewControllerTransitionCoordinatorContext) -> Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Void)? = nil) -> Bool { true }
func notifyWhenInteractionEnds(_ handler: @escaping (UIViewControllerTransitionCoordinatorContext) -> Void) {}
func notifyWhenInteractionChanges(_ handler: @escaping (UIViewControllerTransitionCoordinatorContext) -> Void) {}
var isAnimated: Bool = false
var presentationStyle: UIModalPresentationStyle = .fullScreen
var initiallyInteractive: Bool = false
var isInterruptible: Bool = false
var isInteractive: Bool = false
var isCancelled: Bool = false
var transitionDuration: TimeInterval = 0.25
var percentComplete: CGFloat = 0
var completionVelocity: CGFloat = 0
var completionCurve: UIView.AnimationCurve = .easeInOut
func viewController(forKey key: UITransitionContextViewControllerKey) -> UIViewController? { nil }
func view(forKey key: UITransitionContextViewKey) -> UIView? { nil }
var containerView: UIView { UIView() }
var targetTransform: CGAffineTransform = .identity
}