Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 65efbdfcbd | |||
| 1f5f5bf222 | |||
| 71f419a3cd | |||
| 0e27410460 | |||
| 29185a47bd | |||
| 6821b26706 | |||
| 5bdbe0f0ea | |||
| afe2a9bced | |||
| c0d88af234 | |||
| 3b4c1bd51c | |||
| 3c9f556533 | |||
| 7f1a74825d | |||
| 22d46c5260 | |||
| 466aaf21dd | |||
| 56e71ac580 | |||
| f45b6aaa3f | |||
| e473c3c440 |
@@ -7,15 +7,8 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=14.5,name=iPhone 12 Pro'
|
||||
test-ios13_7-iPhone_11_Pro:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
steps:
|
||||
- checkout
|
||||
- run: xcodebuild clean test -scheme FloatingPanel -workspace FloatingPanel.xcworkspace -destination 'platform=iOS Simulator,OS=13.7,name=iPhone 11 Pro'
|
||||
|
||||
workflows:
|
||||
test:
|
||||
jobs:
|
||||
- test-ios14_5-iPhone_12_Pro
|
||||
- test-ios13_7-iPhone_11_Pro
|
||||
|
||||
+18
-31
@@ -18,6 +18,9 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- swift: "5.10"
|
||||
xcode: "15.4"
|
||||
runs-on: macos-14
|
||||
- swift: "5.9"
|
||||
xcode: "15.2"
|
||||
runs-on: macos-13
|
||||
@@ -33,17 +36,8 @@ jobs:
|
||||
- swift: "5.5"
|
||||
xcode: "13.2.1"
|
||||
runs-on: macos-12
|
||||
- swift: "5.4"
|
||||
xcode: "12.5.1"
|
||||
runs-on: macos-11
|
||||
- swift: "5.3"
|
||||
xcode: "12.4"
|
||||
runs-on: macos-11
|
||||
- swift: "5.2"
|
||||
xcode: "11.7"
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Building in Swift ${{ matrix.swift }}
|
||||
run: xcodebuild -scheme FloatingPanel SWIFT_VERSION=${{ matrix.swift }} clean build
|
||||
|
||||
@@ -55,11 +49,11 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: "17.2"
|
||||
xcode: "15.2"
|
||||
- os: "17.5"
|
||||
xcode: "15.4"
|
||||
sim: "iPhone 15 Pro"
|
||||
parallel: NO # Stop random test job failures
|
||||
runs-on: macos-13
|
||||
runs-on: macos-14
|
||||
- os: "16.4"
|
||||
xcode: "14.3.1"
|
||||
sim: "iPhone 14 Pro"
|
||||
@@ -71,7 +65,7 @@ jobs:
|
||||
parallel: NO # Stop random test job failures
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Testing in iOS ${{ matrix.os }}
|
||||
run: |
|
||||
xcodebuild clean test \
|
||||
@@ -82,9 +76,9 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
|
||||
example:
|
||||
runs-on: macos-13
|
||||
runs-on: macos-14
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer
|
||||
DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -94,7 +88,7 @@ jobs:
|
||||
- example: "Stocks"
|
||||
- example: "Samples"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Building ${{ matrix.example }}
|
||||
run: |
|
||||
xcodebuild clean build \
|
||||
@@ -103,9 +97,9 @@ jobs:
|
||||
-sdk iphonesimulator
|
||||
|
||||
swiftpm:
|
||||
runs-on: macos-13
|
||||
runs-on: macos-14
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer
|
||||
DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -121,7 +115,7 @@ jobs:
|
||||
- platform: iphonesimulator
|
||||
sys: "ios17.2-simulator"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Swift Package Manager build"
|
||||
run: |
|
||||
xcrun swift build \
|
||||
@@ -151,26 +145,19 @@ jobs:
|
||||
xcode: "14.1"
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- 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
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Carthage build"
|
||||
run: carthage build --use-xcframeworks --no-skip-current
|
||||
|
||||
cocoapods:
|
||||
runs-on: macos-13
|
||||
runs-on: macos-14
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer
|
||||
DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: "CocoaPods: pod lib lint"
|
||||
run: pod lib lint --allow-warnings
|
||||
- name: "CocoaPods: pod spec lint"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
5442E24A25FC53C100A26F43 /* DebugTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5442E24925FC53C100A26F43 /* DebugTextViewController.swift */; };
|
||||
5442E25225FC541700A26F43 /* NestedScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5442E25125FC541700A26F43 /* NestedScrollViewController.swift */; };
|
||||
54496C59263A7E5A0031E0C8 /* UseCaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54496C58263A7E5A0031E0C8 /* UseCaseController.swift */; };
|
||||
544BC56826CC918200D0A436 /* AdaptiveLayoutTestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 544BC56726CC918200D0A436 /* AdaptiveLayoutTestViewController.swift */; };
|
||||
544BC56826CC918200D0A436 /* TableViewControllerForAdaptiveLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 544BC56726CC918200D0A436 /* TableViewControllerForAdaptiveLayout.swift */; };
|
||||
545DB9EE21511E6300CA77B8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9ED21511E6300CA77B8 /* AppDelegate.swift */; };
|
||||
545DB9F021511E6300CA77B8 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9EF21511E6300CA77B8 /* MainViewController.swift */; };
|
||||
545DB9F321511E6300CA77B8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 545DB9F121511E6300CA77B8 /* Main.storyboard */; };
|
||||
@@ -30,8 +30,11 @@
|
||||
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 /* SupplementaryViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D7215BBE23007D205C /* SupplementaryViews.swift */; };
|
||||
54E58CB72BF8A8D900408EA9 /* CollectionViewControllerForAdaptiveLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54E58CB62BF8A8D900408EA9 /* CollectionViewControllerForAdaptiveLayout.swift */; };
|
||||
54EAD35B263A75EB006A36EA /* PanelLayouts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EAD35A263A75EB006A36EA /* PanelLayouts.swift */; };
|
||||
54EAD365263A765F006A36EA /* PagePanelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EAD364263A765F006A36EA /* PagePanelController.swift */; };
|
||||
54F185822BF4AD0000916F57 /* DebugListCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54F185812BF4AD0000916F57 /* DebugListCollectionViewController.swift */; };
|
||||
54F185842BF4B82E00916F57 /* UnavailableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54F185832BF4B82E00916F57 /* UnavailableViewController.swift */; };
|
||||
5D82A6AD28D1843C006A44BA /* libswiftCoreGraphics.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D82A6AC28D18438006A44BA /* libswiftCoreGraphics.tbd */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -61,7 +64,7 @@
|
||||
5442E24925FC53C100A26F43 /* DebugTextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugTextViewController.swift; sourceTree = "<group>"; };
|
||||
5442E25125FC541700A26F43 /* NestedScrollViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedScrollViewController.swift; sourceTree = "<group>"; };
|
||||
54496C58263A7E5A0031E0C8 /* UseCaseController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UseCaseController.swift; sourceTree = "<group>"; };
|
||||
544BC56726CC918200D0A436 /* AdaptiveLayoutTestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveLayoutTestViewController.swift; sourceTree = "<group>"; };
|
||||
544BC56726CC918200D0A436 /* TableViewControllerForAdaptiveLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewControllerForAdaptiveLayout.swift; sourceTree = "<group>"; };
|
||||
545DB9EA21511E6300CA77B8 /* Samples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Samples.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
545DB9ED21511E6300CA77B8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
545DB9EF21511E6300CA77B8 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
|
||||
@@ -74,8 +77,11 @@
|
||||
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 /* SupplementaryViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupplementaryViews.swift; sourceTree = "<group>"; };
|
||||
54E58CB62BF8A8D900408EA9 /* CollectionViewControllerForAdaptiveLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewControllerForAdaptiveLayout.swift; sourceTree = "<group>"; };
|
||||
54EAD35A263A75EB006A36EA /* PanelLayouts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PanelLayouts.swift; sourceTree = "<group>"; };
|
||||
54EAD364263A765F006A36EA /* PagePanelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagePanelController.swift; sourceTree = "<group>"; };
|
||||
54F185812BF4AD0000916F57 /* DebugListCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugListCollectionViewController.swift; sourceTree = "<group>"; };
|
||||
54F185832BF4B82E00916F57 /* UnavailableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnavailableViewController.swift; sourceTree = "<group>"; };
|
||||
5D82A6AC28D18438006A44BA /* libswiftCoreGraphics.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libswiftCoreGraphics.tbd; path = usr/lib/swift/libswiftCoreGraphics.tbd; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -95,17 +101,18 @@
|
||||
5442E22225FC519700A26F43 /* ContentViewControllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
54E58CB52BF8A88600408EA9 /* AdaptiveLayout */,
|
||||
5442E23F25FC533800A26F43 /* DebugTableViewController.swift */,
|
||||
5442E24925FC53C100A26F43 /* DebugTextViewController.swift */,
|
||||
54F185812BF4AD0000916F57 /* DebugListCollectionViewController.swift */,
|
||||
5442E23325FC528400A26F43 /* DetailViewController.swift */,
|
||||
5442E24325FC538200A26F43 /* InspectorViewController.swift */,
|
||||
5442E22325FC51AF00A26F43 /* ImageViewController.swift */,
|
||||
5442E25125FC541700A26F43 /* NestedScrollViewController.swift */,
|
||||
5442E23925FC52CD00A26F43 /* ModalViewController.swift */,
|
||||
5442E22725FC51E200A26F43 /* MultiPanelController.swift */,
|
||||
5442E22B25FC521F00A26F43 /* SettingsViewController.swift */,
|
||||
5442E22F25FC525200A26F43 /* TabBarViewController.swift */,
|
||||
544BC56726CC918200D0A436 /* AdaptiveLayoutTestViewController.swift */,
|
||||
54F185832BF4B82E00916F57 /* UnavailableViewController.swift */,
|
||||
);
|
||||
path = ContentViewControllers;
|
||||
sourceTree = "<group>";
|
||||
@@ -157,6 +164,16 @@
|
||||
path = UseCases;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
54E58CB52BF8A88600408EA9 /* AdaptiveLayout */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5442E22325FC51AF00A26F43 /* ImageViewController.swift */,
|
||||
544BC56726CC918200D0A436 /* TableViewControllerForAdaptiveLayout.swift */,
|
||||
54E58CB62BF8A8D900408EA9 /* CollectionViewControllerForAdaptiveLayout.swift */,
|
||||
);
|
||||
path = AdaptiveLayout;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5D82A6AB28D18438006A44BA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -238,15 +255,17 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5442E23425FC528400A26F43 /* DetailViewController.swift in Sources */,
|
||||
54E58CB72BF8A8D900408EA9 /* CollectionViewControllerForAdaptiveLayout.swift in Sources */,
|
||||
54496C59263A7E5A0031E0C8 /* UseCaseController.swift in Sources */,
|
||||
54CDC5D8215BBE23007D205C /* SupplementaryViews.swift in Sources */,
|
||||
54B51116216AFE5F0033A6F3 /* Extensions.swift in Sources */,
|
||||
544BC56826CC918200D0A436 /* AdaptiveLayoutTestViewController.swift in Sources */,
|
||||
544BC56826CC918200D0A436 /* TableViewControllerForAdaptiveLayout.swift in Sources */,
|
||||
5442E24A25FC53C100A26F43 /* DebugTextViewController.swift in Sources */,
|
||||
546341AC25C6426500CA0596 /* CustomState.swift in Sources */,
|
||||
5442E23A25FC52CD00A26F43 /* ModalViewController.swift in Sources */,
|
||||
5442E22425FC51AF00A26F43 /* ImageViewController.swift in Sources */,
|
||||
5442E24025FC533800A26F43 /* DebugTableViewController.swift in Sources */,
|
||||
54F185822BF4AD0000916F57 /* DebugListCollectionViewController.swift in Sources */,
|
||||
5442E25225FC541700A26F43 /* NestedScrollViewController.swift in Sources */,
|
||||
5442E22825FC51E200A26F43 /* MultiPanelController.swift in Sources */,
|
||||
546341A125C6415100CA0596 /* UseCase.swift in Sources */,
|
||||
@@ -257,6 +276,7 @@
|
||||
54EAD365263A765F006A36EA /* PagePanelController.swift in Sources */,
|
||||
5442E24425FC538200A26F43 /* InspectorViewController.swift in Sources */,
|
||||
5442E22C25FC521F00A26F43 /* SettingsViewController.swift in Sources */,
|
||||
54F185842BF4B82E00916F57 /* UnavailableViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="RoN-h0-uBD">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="RoN-h0-uBD">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
|
||||
<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"/>
|
||||
@@ -801,10 +801,10 @@ Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-2.1739130434782612" y="733.92857142857144"/>
|
||||
</scene>
|
||||
<!--Adaptive Layout Test View Controller-->
|
||||
<!--Table View Controller For Adaptive Layout-->
|
||||
<scene sceneID="rDI-lU-wEx">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="AdaptiveLayoutTestViewController" id="5nC-6E-bXf" customClass="AdaptiveLayoutTestViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController storyboardIdentifier="TableViewControllerForAdaptiveLayout" id="5nC-6E-bXf" customClass="TableViewControllerForAdaptiveLayout" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="jXL-Ss-NCJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="778"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@@ -861,13 +861,13 @@ Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemOrangeColor">
|
||||
<color red="1" green="0.58431372549019611" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="1" green="0.58431372550000005" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemPurpleColor">
|
||||
<color red="0.68627450980392157" green="0.32156862745098042" blue="0.87058823529411766" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.68627450980000004" green="0.32156862749999998" blue="0.87058823529999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemTealColor">
|
||||
<color red="0.18823529411764706" green="0.69019607843137254" blue="0.7803921568627451" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.18823529410000001" green="0.69019607839999997" blue="0.78039215689999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
||||
|
||||
import UIKit
|
||||
import FloatingPanel
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
class CollectionViewControllerForAdaptiveLayout: UIViewController {
|
||||
class PanelLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .full
|
||||
|
||||
private unowned var targetGuide: UILayoutGuide
|
||||
|
||||
init(targetGuide: UILayoutGuide) {
|
||||
self.targetGuide = targetGuide
|
||||
}
|
||||
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelAdaptiveLayoutAnchor(
|
||||
absoluteOffset: 0.0,
|
||||
contentLayout: targetGuide,
|
||||
referenceGuide: .superview,
|
||||
contentBoundingGuide: .safeArea
|
||||
),
|
||||
.half: FloatingPanelAdaptiveLayoutAnchor(
|
||||
fractionalOffset: 0.5,
|
||||
contentLayout: targetGuide,
|
||||
referenceGuide: .superview,
|
||||
contentBoundingGuide: .safeArea
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
enum LayoutType {
|
||||
case flow
|
||||
case compositional
|
||||
}
|
||||
|
||||
weak var collectionView: UICollectionView!
|
||||
var layoutType: LayoutType = .flow
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupCollectionView()
|
||||
}
|
||||
|
||||
private func setupCollectionView() {
|
||||
let collectionViewLayout = {
|
||||
switch layoutType {
|
||||
case .flow:
|
||||
CollectionViewLayoutFactory.flowLayout
|
||||
case .compositional:
|
||||
CollectionViewLayoutFactory.compositionalLayout
|
||||
}
|
||||
}()
|
||||
let collectionView = IntrinsicCollectionView(
|
||||
frame: .zero,
|
||||
collectionViewLayout: collectionViewLayout
|
||||
)
|
||||
|
||||
collectionView.delegate = self
|
||||
collectionView.dataSource = self
|
||||
collectionView.backgroundColor = .yellow
|
||||
collectionView.register(Cell.self, forCellWithReuseIdentifier: Cell.reuseIdentifier)
|
||||
|
||||
view.addSubview(collectionView)
|
||||
self.collectionView = collectionView
|
||||
|
||||
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
extension CollectionViewControllerForAdaptiveLayout: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return 5 // Only three cells needed to fill the space
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Cell.reuseIdentifier, for: indexPath) as! Cell
|
||||
cell.configure(text: "Item \(indexPath.row)")
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||
let width = collectionView.frame.width
|
||||
return CGSize(width: width, height: 100)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
extension CollectionViewControllerForAdaptiveLayout {
|
||||
enum CollectionViewLayoutFactory {
|
||||
static var flowLayout: UICollectionViewLayout {
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
layout.scrollDirection = .vertical
|
||||
layout.minimumLineSpacing = 8 // Vertical spacing between rows
|
||||
return layout
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
static var compositionalLayout: UICollectionViewLayout {
|
||||
UICollectionViewCompositionalLayout { (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
|
||||
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(100))
|
||||
let item = NSCollectionLayoutItem(layoutSize: itemSize)
|
||||
|
||||
let group = NSCollectionLayoutGroup.horizontal(layoutSize: itemSize, subitems: [item])
|
||||
|
||||
let section = NSCollectionLayoutSection(group: group)
|
||||
section.interGroupSpacing = 8 // Spacing between each group/item
|
||||
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
|
||||
|
||||
return section
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final class Cell: UICollectionViewCell {
|
||||
static let reuseIdentifier = "Cell"
|
||||
|
||||
private let label: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textAlignment = .center
|
||||
label.textColor = .white
|
||||
return label
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
backgroundColor = .systemBlue
|
||||
addSubview(label)
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
label.centerXAnchor.constraint(equalTo: centerXAnchor),
|
||||
label.centerYAnchor.constraint(equalTo: centerYAnchor)
|
||||
])
|
||||
}
|
||||
|
||||
func configure(text: String) {
|
||||
label.text = text
|
||||
}
|
||||
}
|
||||
|
||||
private final class IntrinsicCollectionView: UICollectionView {
|
||||
override public var contentSize: CGSize {
|
||||
didSet {
|
||||
invalidateIntrinsicContentSize()
|
||||
}
|
||||
}
|
||||
|
||||
override public var intrinsicContentSize: CGSize {
|
||||
layoutIfNeeded()
|
||||
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -3,7 +3,7 @@
|
||||
import UIKit
|
||||
import FloatingPanel
|
||||
|
||||
final class AdaptiveLayoutTestViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
|
||||
final class TableViewControllerForAdaptiveLayout: UIViewController, UITableViewDataSource, UITableViewDelegate {
|
||||
class PanelLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .full
|
||||
@@ -75,7 +75,6 @@ final class AdaptiveLayoutTestViewController: UIViewController, UITableViewDataS
|
||||
}
|
||||
|
||||
class IntrinsicTableView: UITableView {
|
||||
|
||||
override var contentSize:CGSize {
|
||||
didSet {
|
||||
invalidateIntrinsicContentSize()
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||
|
||||
import UIKit
|
||||
|
||||
@available(iOS 14, *)
|
||||
class DebugListCollectionViewController: UIViewController {
|
||||
|
||||
enum Section {
|
||||
case main
|
||||
}
|
||||
|
||||
var dataSource: UICollectionViewDiffableDataSource<Section, Int>! = nil
|
||||
var collectionView: UICollectionView! = nil
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
navigationItem.title = "List"
|
||||
configureHierarchy()
|
||||
configureDataSource()
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
extension DebugListCollectionViewController {
|
||||
/// - Tag: List
|
||||
private func createLayout() -> UICollectionViewLayout {
|
||||
var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
|
||||
config.trailingSwipeActionsConfigurationProvider = { indexPath -> UISwipeActionsConfiguration? in
|
||||
return UISwipeActionsConfiguration(
|
||||
actions: [UIContextualAction(
|
||||
style: .destructive,
|
||||
title: "Delete",
|
||||
handler: { _, _, completion in
|
||||
// Do nothing now
|
||||
}
|
||||
)]
|
||||
)
|
||||
}
|
||||
return UICollectionViewCompositionalLayout.list(using: config)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
extension DebugListCollectionViewController {
|
||||
private func configureHierarchy() {
|
||||
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
|
||||
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
view.addSubview(collectionView)
|
||||
collectionView.delegate = self
|
||||
}
|
||||
private func configureDataSource() {
|
||||
|
||||
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { (cell, indexPath, item) in
|
||||
var content = cell.defaultContentConfiguration()
|
||||
content.text = "\(item)"
|
||||
cell.contentConfiguration = content
|
||||
}
|
||||
|
||||
dataSource = UICollectionViewDiffableDataSource<Section, Int>(collectionView: collectionView) {
|
||||
(collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -> UICollectionViewCell? in
|
||||
|
||||
return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
|
||||
}
|
||||
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Int>()
|
||||
snapshot.appendSections([.main])
|
||||
snapshot.appendItems(Array(0..<94))
|
||||
dataSource.apply(snapshot, animatingDifferences: false)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
extension DebugListCollectionViewController: UICollectionViewDelegate {
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
collectionView.deselectItem(at: indexPath, animated: true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||
|
||||
import UIKit
|
||||
|
||||
class UnavailableViewController: UIViewController {
|
||||
weak var label: UILabel!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
let label = UILabel()
|
||||
label.text = "Unavailable content"
|
||||
label.numberOfLines = 0
|
||||
label.textAlignment = .center
|
||||
label.frame = view.bounds
|
||||
label.autoresizingMask = [
|
||||
.flexibleTopMargin,
|
||||
.flexibleLeftMargin,
|
||||
.flexibleBottomMargin,
|
||||
.flexibleRightMargin
|
||||
]
|
||||
view.addSubview(label)
|
||||
self.label = label
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import UIKit
|
||||
enum UseCase: Int, CaseIterable {
|
||||
case trackingTableView
|
||||
case trackingTextView
|
||||
case trackingCollectionViewList
|
||||
case showDetail
|
||||
case showModal
|
||||
case showPanelModal
|
||||
@@ -22,7 +23,9 @@ enum UseCase: Int, CaseIterable {
|
||||
case showNavigationController
|
||||
case showTopPositionedPanel
|
||||
case showAdaptivePanel
|
||||
case showAdaptivePanelWithCustomGuide
|
||||
case showAdaptivePanelWithTableView
|
||||
case showAdaptivePanelWithCollectionView
|
||||
case showAdaptivePanelWithCompositionalCollectionView
|
||||
case showCustomStatePanel
|
||||
case showCustomBackdrop
|
||||
}
|
||||
@@ -31,6 +34,7 @@ extension UseCase {
|
||||
var name: String {
|
||||
switch self {
|
||||
case .trackingTableView: return "Scroll tracking(TableView)"
|
||||
case .trackingCollectionViewList: return "Scroll tracking(List CollectionView)"
|
||||
case .trackingTextView: return "Scroll tracking(TextView)"
|
||||
case .showDetail: return "Show Detail Panel"
|
||||
case .showModal: return "Show Modal"
|
||||
@@ -49,7 +53,9 @@ extension UseCase {
|
||||
case .showNavigationController: return "Show Navigation Controller"
|
||||
case .showTopPositionedPanel: return "Show Top Positioned Panel"
|
||||
case .showAdaptivePanel: return "Show Adaptive Panel"
|
||||
case .showAdaptivePanelWithCustomGuide: return "Show Adaptive Panel (Custom Layout Guide)"
|
||||
case .showAdaptivePanelWithTableView: return "Show Adaptive Panel (TableView)"
|
||||
case .showAdaptivePanelWithCollectionView: return "Show Adaptive Panel (CollectionView)"
|
||||
case .showAdaptivePanelWithCompositionalCollectionView: return "Show Adaptive Panel (Compositional CollectionView)"
|
||||
case .showCustomStatePanel: return "Show Panel with Custom state"
|
||||
case .showCustomBackdrop: return "Show Panel with Custom Backdrop"
|
||||
}
|
||||
@@ -65,6 +71,13 @@ extension UseCase {
|
||||
private var content: Content {
|
||||
switch self {
|
||||
case .trackingTableView: return .viewController(DebugTableViewController())
|
||||
case .trackingCollectionViewList:
|
||||
if #available(iOS 14, *) {
|
||||
return .viewController(DebugListCollectionViewController())
|
||||
} else {
|
||||
let msg = "UICollectionLayoutListConfiguration is unavailable.\nBuild this app on iOS 14 and later."
|
||||
return makeUnavailableViewContent(message: msg)
|
||||
}
|
||||
case .trackingTextView: return .storyboard("ConsoleViewController") // Storyboard only
|
||||
case .showDetail: return .storyboard(String(describing: DetailViewController.self))
|
||||
case .showModal: return .storyboard(String(describing: ModalViewController.self))
|
||||
@@ -83,7 +96,17 @@ extension UseCase {
|
||||
case .showNavigationController: return .storyboard("RootNavigationController") // Storyboard only
|
||||
case .showTopPositionedPanel: return .viewController(DebugTableViewController())
|
||||
case .showAdaptivePanel: return .storyboard(String(describing: ImageViewController.self))
|
||||
case .showAdaptivePanelWithCustomGuide: return .storyboard(String(describing: AdaptiveLayoutTestViewController.self))
|
||||
case .showAdaptivePanelWithTableView: return .storyboard(String(describing: TableViewControllerForAdaptiveLayout.self))
|
||||
case .showAdaptivePanelWithCollectionView,
|
||||
.showAdaptivePanelWithCompositionalCollectionView:
|
||||
if #available(iOS 13, *) {
|
||||
let vc = CollectionViewControllerForAdaptiveLayout()
|
||||
vc.layoutType = self == .showAdaptivePanelWithCollectionView ? .flow : .compositional
|
||||
return .viewController(vc)
|
||||
} else {
|
||||
let msg = "Compositional layout is unavailable.\nBuild this app on iOS 13 and later."
|
||||
return makeUnavailableViewContent(message: msg)
|
||||
}
|
||||
case .showCustomStatePanel: return .viewController(DebugTableViewController())
|
||||
case .showCustomBackdrop: return .viewController(UIViewController())
|
||||
}
|
||||
@@ -98,4 +121,11 @@ extension UseCase {
|
||||
return vc
|
||||
}
|
||||
}
|
||||
|
||||
private func makeUnavailableViewContent(message: String) -> Content {
|
||||
let vc = UnavailableViewController()
|
||||
vc.loadViewIfNeeded()
|
||||
vc.label.text = message
|
||||
return .viewController(vc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,30 @@ extension UseCaseController {
|
||||
fpc.ext_trackScrollView(in: contentVC)
|
||||
addMain(panel: fpc)
|
||||
|
||||
case .trackingCollectionViewList:
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.delegate = self
|
||||
fpc.contentInsetAdjustmentBehavior = .always
|
||||
fpc.surfaceView.appearance = {
|
||||
let appearance = SurfaceAppearance()
|
||||
appearance.cornerRadius = 6.0
|
||||
return appearance
|
||||
}()
|
||||
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(UseCaseController.handleSurface(tapGesture:)))
|
||||
tapGesture.cancelsTouchesInView = false
|
||||
tapGesture.numberOfTapsRequired = 2
|
||||
// Prevents a delay to response a tap in menus of DebugTableViewController.
|
||||
tapGesture.delaysTouchesEnded = false
|
||||
fpc.surfaceView.addGestureRecognizer(tapGesture)
|
||||
|
||||
fpc.set(contentViewController: contentVC)
|
||||
if #available(iOS 14, *),
|
||||
let scrollView = (fpc.contentViewController as? DebugListCollectionViewController)?.collectionView {
|
||||
fpc.track(scrollView: scrollView)
|
||||
}
|
||||
addMain(panel: fpc)
|
||||
|
||||
case .trackingTextView:
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.delegate = self
|
||||
@@ -239,13 +263,13 @@ extension UseCaseController {
|
||||
fpc.set(contentViewController: contentVC)
|
||||
fpc.ext_trackScrollView(in: contentVC)
|
||||
if case let contentVC as ImageViewController = contentVC {
|
||||
let mode: ImageViewController.Mode = (useCase == .showAdaptivePanelWithCustomGuide) ? .withHeaderFooter : .onlyImage
|
||||
let mode: ImageViewController.Mode = (useCase == .showAdaptivePanelWithTableView) ? .withHeaderFooter : .onlyImage
|
||||
let layoutGuide = contentVC.layoutGuideFor(mode: mode)
|
||||
fpc.layout = ImageViewController.PanelLayout(targetGuide: layoutGuide)
|
||||
}
|
||||
addMain(panel: fpc)
|
||||
|
||||
case .showAdaptivePanelWithCustomGuide:
|
||||
case .showAdaptivePanelWithTableView:
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.isRemovalInteractionEnabled = true
|
||||
fpc.contentInsetAdjustmentBehavior = .always
|
||||
@@ -255,10 +279,25 @@ extension UseCaseController {
|
||||
return appearance
|
||||
}()
|
||||
|
||||
|
||||
fpc.set(contentViewController: contentVC)
|
||||
fpc.ext_trackScrollView(in: contentVC)
|
||||
fpc.layout = AdaptiveLayoutTestViewController.PanelLayout(targetGuide: contentVC.view.makeBoundsLayoutGuide())
|
||||
fpc.track(scrollView: (contentVC as! TableViewControllerForAdaptiveLayout).tableView)
|
||||
fpc.layout = TableViewControllerForAdaptiveLayout.PanelLayout(targetGuide: contentVC.view.makeBoundsLayoutGuide())
|
||||
addMain(panel: fpc)
|
||||
|
||||
case .showAdaptivePanelWithCollectionView, .showAdaptivePanelWithCompositionalCollectionView:
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.isRemovalInteractionEnabled = true
|
||||
fpc.contentInsetAdjustmentBehavior = .always
|
||||
fpc.surfaceView.appearance = {
|
||||
let appearance = SurfaceAppearance()
|
||||
appearance.cornerRadius = 6.0
|
||||
return appearance
|
||||
}()
|
||||
fpc.set(contentViewController: contentVC)
|
||||
if #available(iOS 13, *) {
|
||||
fpc.track(scrollView: (contentVC as! CollectionViewControllerForAdaptiveLayout).collectionView)
|
||||
fpc.layout = CollectionViewControllerForAdaptiveLayout.PanelLayout(targetGuide: contentVC.view.makeBoundsLayoutGuide())
|
||||
}
|
||||
addMain(panel: fpc)
|
||||
|
||||
case .showCustomStatePanel:
|
||||
@@ -458,7 +497,7 @@ private extension FloatingPanelController {
|
||||
case let contentVC as ImageViewController:
|
||||
track(scrollView: contentVC.scrollView)
|
||||
|
||||
case let contentVC as AdaptiveLayoutTestViewController:
|
||||
case let contentVC as TableViewControllerForAdaptiveLayout:
|
||||
track(scrollView: contentVC.tableView)
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "FloatingPanel"
|
||||
s.version = "2.8.2"
|
||||
s.version = "2.8.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.
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
[](https://cocoapods.org/pods/FloatingPanel)
|
||||
[](https://cocoapods.org/pods/FloatingPanel)
|
||||

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

|
||||

|
||||
@@ -22,7 +21,6 @@ Please see also [the API reference@SPI](https://swiftpackageindex.com/scenee/Flo
|
||||
- [Requirements](#requirements)
|
||||
- [Installation](#installation)
|
||||
- [CocoaPods](#cocoapods)
|
||||
- [Carthage](#carthage)
|
||||
- [Swift Package Manager](#swift-package-manager)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Add a floating panel as a child view controller](#add-a-floating-panel-as-a-child-view-controller)
|
||||
@@ -104,14 +102,6 @@ it, simply add the following line to your Podfile:
|
||||
pod 'FloatingPanel'
|
||||
```
|
||||
|
||||
### Carthage
|
||||
|
||||
For [Carthage](https://github.com/Carthage/Carthage), add the following to your `Cartfile`:
|
||||
|
||||
```ogdl
|
||||
github "scenee/FloatingPanel"
|
||||
```
|
||||
|
||||
### Swift Package Manager
|
||||
|
||||
Follow [this doc](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app).
|
||||
|
||||
+33
-18
@@ -588,11 +588,16 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
// When no scrollView, nothing to handle.
|
||||
guard let scrollView = scrollView, scrollView.frame.contains(initialLocation) else { return false }
|
||||
|
||||
// For _UISwipeActionPanGestureRecognizer
|
||||
if let scrollGestureRecognizers = scrollView.gestureRecognizers {
|
||||
// Prevents moving a panel on swipe actions using _UISwipeActionPanGestureRecognizer.
|
||||
// [Warning] Do not apply this to WKWebView. Since iOS 17.4, WKWebView has an additional pan
|
||||
// gesture recognizer besides UIScrollViewPanGestureRecognizer. Applying this to WKWebView
|
||||
// will block panel movements because another pan gesture isn't `scrollView.panGestureRecognizer`.
|
||||
if let scrollGestureRecognizers = scrollView.gestureRecognizers,
|
||||
scrollView is UITableView || scrollView is UICollectionView {
|
||||
for gesture in scrollGestureRecognizers {
|
||||
guard gesture.state == .began || gesture.state == .changed
|
||||
else { continue }
|
||||
guard gesture.state == .began || gesture.state == .changed else {
|
||||
continue
|
||||
}
|
||||
|
||||
if gesture != scrollView.panGestureRecognizer {
|
||||
return true
|
||||
@@ -651,14 +656,19 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
private func panningChange(with translation: CGPoint) {
|
||||
os_log(msg, log: devLog, type: .debug, "panningChange -- translation = \(value(of: translation))")
|
||||
let pre = value(of: layoutAdapter.surfaceLocation)
|
||||
let diff = value(of: translation - initialTranslation)
|
||||
let next = pre + diff
|
||||
|
||||
layoutAdapter.updateInteractiveEdgeConstraint(diff: diff,
|
||||
scrollingContent: shouldScrollingContentInMoving(from: pre, to: next),
|
||||
allowsRubberBanding: behaviorAdapter.allowsRubberBanding(for:))
|
||||
os_log(msg, log: devLog, type: .debug, """
|
||||
panningChange -- translation = \(value(of: translation)), diff = \(diff), pre = \(pre), next = \(next)
|
||||
""")
|
||||
|
||||
layoutAdapter.updateInteractiveEdgeConstraint(
|
||||
diff: diff,
|
||||
scrollingContent: shouldScrollingContentInMoving(from: pre, to: next),
|
||||
allowsRubberBanding: behaviorAdapter.allowsRubberBanding(for:)
|
||||
)
|
||||
|
||||
let cur = value(of: layoutAdapter.surfaceLocation)
|
||||
|
||||
@@ -671,31 +681,36 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func shouldScrollingContentInMoving(from pre: CGFloat, to next: CGFloat) -> Bool {
|
||||
/// Determines if the content should scroll while the surface is moving from `cur` to `target`.
|
||||
///
|
||||
/// - Note: `cur` argument starts from an anchor location of surface view in a state. For example,
|
||||
/// it starts from zero if the state is full whose FloatingPanelLayoutAnchor.absoluteInset is zero
|
||||
/// and there is no additional safe area insets like a navigation bar. Therefore, `cur` argument
|
||||
/// can be minus if the absoluteInset is minus with such a condition.
|
||||
private func shouldScrollingContentInMoving(from cur: CGFloat, to target: 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 {
|
||||
if let sv = scrollView, sv.panGestureRecognizer.state == .changed {
|
||||
let (contentSize, bounds, alwaysBounceHorizontal, alwaysBounceVertical)
|
||||
= (sv.contentSize, sv.bounds, sv.alwaysBounceHorizontal, sv.alwaysBounceVertical)
|
||||
|
||||
switch layoutAdapter.position {
|
||||
case .top:
|
||||
if pre > .zero, pre < next,
|
||||
scrollView.contentSize.height > scrollView.bounds.height || scrollView.alwaysBounceVertical {
|
||||
if cur < target, contentSize.height > bounds.height || alwaysBounceVertical {
|
||||
return true
|
||||
}
|
||||
case .left:
|
||||
if pre > .zero, pre < next,
|
||||
scrollView.contentSize.width > scrollView.bounds.width || scrollView.alwaysBounceHorizontal {
|
||||
if cur < target, contentSize.width > bounds.width || alwaysBounceHorizontal {
|
||||
return true
|
||||
}
|
||||
case .bottom:
|
||||
if pre > .zero, pre > next,
|
||||
scrollView.contentSize.height > scrollView.bounds.height || scrollView.alwaysBounceVertical {
|
||||
if cur > target, contentSize.height > bounds.height || alwaysBounceVertical {
|
||||
return true
|
||||
}
|
||||
case .right:
|
||||
if pre > .zero, pre > next,
|
||||
scrollView.contentSize.width > scrollView.bounds.width || scrollView.alwaysBounceHorizontal {
|
||||
if cur > target, contentSize.width > bounds.width || alwaysBounceHorizontal {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.8.2</string>
|
||||
<string>2.8.5</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
</dict>
|
||||
|
||||
@@ -17,7 +17,7 @@ import UIKit
|
||||
/// positioning.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - absoluteOffset: An absolute offset to attach the panel from the edge.
|
||||
/// - absoluteInset: An absolute distance to attach the panel from the specified edge.
|
||||
/// - edge: Specify the edge of ``FloatingPanelController``'s view. This is the staring point of the offset.
|
||||
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
|
||||
@objc public init(absoluteInset: CGFloat, edge: FloatingPanelReferenceEdge, referenceGuide: FloatingPanelLayoutReferenceGuide) {
|
||||
@@ -34,7 +34,7 @@ import UIKit
|
||||
/// 1.0 represents a distance to the opposite edge.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - fractionalOffset: A fractional value of the size of ``FloatingPanelController``'s view to attach the panel from the edge.
|
||||
/// - fractionalInset: A fractional value of the size of ``FloatingPanelController``'s view to attach the panel from the specified edge.
|
||||
/// - edge: Specify the edge of ``FloatingPanelController``'s view. This is the staring point of the offset.
|
||||
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
|
||||
@objc public init(fractionalInset: CGFloat, edge: FloatingPanelReferenceEdge, referenceGuide: FloatingPanelLayoutReferenceGuide) {
|
||||
@@ -115,8 +115,8 @@ public extension FloatingPanelLayoutAnchor {
|
||||
/// - Parameters:
|
||||
/// - absoluteOffset: An absolute offset from the content size in the main dimension(i.e. y axis for a bottom panel) to attach the panel.
|
||||
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
|
||||
@objc public init(absoluteOffset offset: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
||||
self.offset = offset
|
||||
@objc public init(absoluteOffset: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
||||
self.offset = absoluteOffset
|
||||
self.referenceGuide = referenceGuide
|
||||
self.isAbsolute = true
|
||||
}
|
||||
@@ -129,8 +129,8 @@ public extension FloatingPanelLayoutAnchor {
|
||||
/// - Parameters:
|
||||
/// - fractionalOffset: A fractional offset of the content size in the main dimension(i.e. y axis for a bottom panel) to attach the panel.
|
||||
/// - referenceGuide: The rectangular area to lay out the content. If it's set to `.safeArea`, the panel content lays out inside the safe area of its ``FloatingPanelController``'s view.
|
||||
@objc public init(fractionalOffset offset: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
||||
self.offset = offset
|
||||
@objc public init(fractionalOffset: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
|
||||
self.offset = fractionalOffset
|
||||
self.referenceGuide = referenceGuide
|
||||
self.isAbsolute = false
|
||||
}
|
||||
@@ -177,12 +177,12 @@ public extension FloatingPanelIntrinsicLayoutAnchor {
|
||||
///
|
||||
/// - Warning: If ``contentBoundingGuide`` is set to none, the panel may expand out of the screen size, depending on the intrinsic size of its content.
|
||||
@objc public init(
|
||||
absoluteOffset offset: CGFloat,
|
||||
absoluteOffset: CGFloat,
|
||||
contentLayout: UILayoutGuide,
|
||||
referenceGuide: FloatingPanelLayoutReferenceGuide,
|
||||
contentBoundingGuide: FloatingPanelLayoutContentBoundingGuide = .none
|
||||
) {
|
||||
self.offset = offset
|
||||
self.offset = absoluteOffset
|
||||
self.contentLayoutGuide = contentLayout
|
||||
self.referenceGuide = referenceGuide
|
||||
self.contentBoundingGuide = contentBoundingGuide
|
||||
@@ -204,12 +204,12 @@ public extension FloatingPanelIntrinsicLayoutAnchor {
|
||||
///
|
||||
/// - Warning: If ``contentBoundingGuide`` is set to none, the panel may expand out of the screen size, depending on the intrinsic size of its content.
|
||||
@objc public init(
|
||||
fractionalOffset offset: CGFloat,
|
||||
fractionalOffset: CGFloat,
|
||||
contentLayout: UILayoutGuide,
|
||||
referenceGuide: FloatingPanelLayoutReferenceGuide,
|
||||
contentBoundingGuide: FloatingPanelLayoutContentBoundingGuide = .none
|
||||
) {
|
||||
self.offset = offset
|
||||
self.offset = fractionalOffset
|
||||
self.contentLayoutGuide = contentLayout
|
||||
self.referenceGuide = referenceGuide
|
||||
self.contentBoundingGuide = contentBoundingGuide
|
||||
|
||||
@@ -56,11 +56,11 @@ public class SurfaceAppearance: NSObject {
|
||||
/// Defaults to `.circular`.
|
||||
@available(iOS 13.0, *)
|
||||
public var cornerCurve: CALayerCornerCurve {
|
||||
get { _cornerCurve ?? .circular }
|
||||
get { _cornerCurve as? CALayerCornerCurve ?? .circular }
|
||||
set { _cornerCurve = newValue }
|
||||
}
|
||||
|
||||
private var _cornerCurve: CALayerCornerCurve?
|
||||
private var _cornerCurve: Any?
|
||||
|
||||
/// An array of shadows used to create drop shadows underneath a surface view.
|
||||
public var shadows: [Shadow] = [Shadow()]
|
||||
@@ -418,12 +418,30 @@ public class SurfaceView: UIView {
|
||||
let leftConstraint = contentView.leftAnchor.constraint(equalTo: leftAnchor, constant: containerMargins.left + contentPadding.left)
|
||||
let rightConstraint = rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: containerMargins.right + contentPadding.right)
|
||||
let bottomConstraint = bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: containerMargins.bottom + contentPadding.bottom)
|
||||
NSLayoutConstraint.activate([
|
||||
|
||||
var constraints = [
|
||||
topConstraint,
|
||||
leftConstraint,
|
||||
rightConstraint,
|
||||
bottomConstraint,
|
||||
].map {
|
||||
bottomConstraint
|
||||
]
|
||||
|
||||
// This constraint is for UICollectionView using UICollectionViewCompositionalLayout.
|
||||
// It's seemingly obvious, but the UICollectionView doesn't work without setting it. (#628)
|
||||
switch position {
|
||||
case .top, .bottom:
|
||||
constraints += [
|
||||
heightAnchor.constraint(greaterThanOrEqualToConstant: 1.0)
|
||||
]
|
||||
case .left, .right:
|
||||
constraints += [
|
||||
widthAnchor.constraint(greaterThanOrEqualToConstant: 1.0)
|
||||
]
|
||||
}
|
||||
|
||||
NSLayoutConstraint.activate(
|
||||
constraints
|
||||
.map {
|
||||
switch mode {
|
||||
case .static:
|
||||
$0.priority = .required
|
||||
|
||||
@@ -84,7 +84,7 @@ class ModalPresentTransition: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
guard
|
||||
let fpc = transitionContext?.viewController(forKey: .to) as? FloatingPanelController
|
||||
else { fatalError()}
|
||||
else { return 0.0 }
|
||||
|
||||
let animator = fpc.animatorForPresenting(to: fpc.layout.initialState)
|
||||
return TimeInterval(animator.duration)
|
||||
@@ -119,7 +119,7 @@ class ModalDismissTransition: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
guard
|
||||
let fpc = transitionContext?.viewController(forKey: .from) as? FloatingPanelController
|
||||
else { fatalError()}
|
||||
else { return 0.0 }
|
||||
|
||||
let animator = fpc.animatorForDismissing(with: .zero)
|
||||
return TimeInterval(animator.duration)
|
||||
|
||||
+13
-10
@@ -64,6 +64,7 @@ class ControllerTests: XCTestCase {
|
||||
}
|
||||
|
||||
func test_moveTo() {
|
||||
let timeout = 3.0
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
XCTAssertEqual(delegate.position, .hidden)
|
||||
@@ -102,7 +103,7 @@ class ControllerTests: XCTestCase {
|
||||
}
|
||||
XCTAssertEqual(fpc.state, .full)
|
||||
XCTAssertEqual(delegate.position, .full)
|
||||
wait(for: [exp], timeout: 1.0)
|
||||
wait(for: [exp], timeout: timeout)
|
||||
}
|
||||
|
||||
XCTContext.runActivity(named: "move to half(animated)") { act in
|
||||
@@ -113,7 +114,7 @@ class ControllerTests: XCTestCase {
|
||||
}
|
||||
XCTAssertEqual(fpc.state, .half)
|
||||
XCTAssertEqual(delegate.position, .half)
|
||||
wait(for: [exp], timeout: 1.0)
|
||||
wait(for: [exp], timeout: timeout)
|
||||
}
|
||||
|
||||
XCTContext.runActivity(named: "move to tip(animated)") { act in
|
||||
@@ -124,7 +125,7 @@ class ControllerTests: XCTestCase {
|
||||
}
|
||||
XCTAssertEqual(fpc.state, .tip)
|
||||
XCTAssertEqual(delegate.position, .tip)
|
||||
wait(for: [exp], timeout: 1.0)
|
||||
wait(for: [exp], timeout: timeout)
|
||||
}
|
||||
|
||||
fpc.move(to: .hidden, animated: true)
|
||||
@@ -137,6 +138,7 @@ class ControllerTests: XCTestCase {
|
||||
class MyFloatingPanelTop2BottomLayout: FloatingPanelTop2BottomTestLayout {
|
||||
override var initialState: FloatingPanelState { return .half }
|
||||
}
|
||||
let timeout = 3.0
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
fpc.layout = MyFloatingPanelTop2BottomLayout()
|
||||
@@ -175,7 +177,7 @@ class ControllerTests: XCTestCase {
|
||||
}
|
||||
XCTAssertEqual(fpc.state, .full)
|
||||
XCTAssertEqual(delegate.position, .full)
|
||||
wait(for: [exp], timeout: 1.0)
|
||||
wait(for: [exp], timeout: timeout)
|
||||
}
|
||||
|
||||
XCTContext.runActivity(named: "move to half(animated)") { act in
|
||||
@@ -186,7 +188,7 @@ class ControllerTests: XCTestCase {
|
||||
}
|
||||
XCTAssertEqual(fpc.state, .half)
|
||||
XCTAssertEqual(delegate.position, .half)
|
||||
wait(for: [exp], timeout: 1.0)
|
||||
wait(for: [exp], timeout: timeout)
|
||||
}
|
||||
|
||||
XCTContext.runActivity(named: "move to tip(animated)") { act in
|
||||
@@ -197,7 +199,7 @@ class ControllerTests: XCTestCase {
|
||||
}
|
||||
XCTAssertEqual(fpc.state, .tip)
|
||||
XCTAssertEqual(delegate.position, .tip)
|
||||
wait(for: [exp], timeout: 1.0)
|
||||
wait(for: [exp], timeout: timeout)
|
||||
}
|
||||
|
||||
fpc.move(to: .hidden, animated: true)
|
||||
@@ -223,6 +225,7 @@ class ControllerTests: XCTestCase {
|
||||
}
|
||||
|
||||
func test_moveTo_didMoveDelegate() {
|
||||
let timeout = 3.0
|
||||
let delegate = FloatingPanelTestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
XCTAssertEqual(delegate.position, .hidden)
|
||||
@@ -237,7 +240,7 @@ class ControllerTests: XCTestCase {
|
||||
exp.fulfill()
|
||||
}
|
||||
fpc.move(to: .full, animated: false)
|
||||
wait(for: [exp], timeout: 1.0)
|
||||
wait(for: [exp], timeout: timeout)
|
||||
|
||||
XCTAssertEqual(count, 1)
|
||||
}
|
||||
@@ -253,7 +256,7 @@ class ControllerTests: XCTestCase {
|
||||
fpc.move(to: .half, animated: true) {
|
||||
exp.fulfill()
|
||||
}
|
||||
wait(for: [exp], timeout: 1.0)
|
||||
wait(for: [exp], timeout: timeout)
|
||||
|
||||
XCTAssertGreaterThan(count, 1)
|
||||
}
|
||||
@@ -270,7 +273,7 @@ class ControllerTests: XCTestCase {
|
||||
exp.fulfill()
|
||||
}
|
||||
}
|
||||
wait(for: [exp], timeout: 1.0)
|
||||
wait(for: [exp], timeout: timeout)
|
||||
|
||||
XCTAssertEqual(count, 1)
|
||||
}
|
||||
@@ -288,7 +291,7 @@ class ControllerTests: XCTestCase {
|
||||
exp.fulfill()
|
||||
}
|
||||
}
|
||||
wait(for: [exp], timeout: 1.0)
|
||||
wait(for: [exp], timeout: timeout)
|
||||
|
||||
XCTAssertGreaterThan(count, 1)
|
||||
}
|
||||
|
||||
@@ -209,6 +209,8 @@ class CoreTests: XCTestCase {
|
||||
return floor(fpc.backdropView.alpha * 1e+06) / 1e+06
|
||||
}
|
||||
|
||||
let timeout = 3.0
|
||||
|
||||
let delegate = TestDelegate()
|
||||
let fpc = FloatingPanelController(delegate: delegate)
|
||||
fpc.layout = BackdropTestLayout()
|
||||
@@ -228,14 +230,14 @@ class CoreTests: XCTestCase {
|
||||
fpc.move(to: .full, animated: true) {
|
||||
exp1.fulfill()
|
||||
}
|
||||
wait(for: [exp1], timeout: 1.0)
|
||||
wait(for: [exp1], timeout: timeout)
|
||||
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
|
||||
|
||||
let exp2 = expectation(description: "move to half with animation")
|
||||
fpc.move(to: .half, animated: true) {
|
||||
exp2.fulfill()
|
||||
}
|
||||
wait(for: [exp2], timeout: 1.0)
|
||||
wait(for: [exp2], timeout: timeout)
|
||||
XCTAssertEqual(fpc.backdropView.alpha, 0.0)
|
||||
|
||||
// Test a content mode change of FloatingPanelController
|
||||
@@ -246,7 +248,7 @@ class CoreTests: XCTestCase {
|
||||
}
|
||||
fpc.contentMode = .fitToBounds
|
||||
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must not affect the backdrop alpha by changing the content mode
|
||||
wait(for: [exp3], timeout: 1.0)
|
||||
wait(for: [exp3], timeout: timeout)
|
||||
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
|
||||
|
||||
// Test a size class change of FloatingPanelController.view
|
||||
@@ -922,7 +924,6 @@ class CoreTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
let fpc = FloatingPanelController()
|
||||
let scrollView = UIScrollView()
|
||||
let delegate = Delegate()
|
||||
fpc.showForTest()
|
||||
fpc.delegate = delegate
|
||||
|
||||
Reference in New Issue
Block a user