Compare commits

..

15 Commits

Author SHA1 Message Date
Shin Yamamoto b4423bcaa2 Version 2.1.0 2020-12-07 19:25:47 +09:00
Shin Yamamoto b34fd41650 Fix Maps samlpe (#411)
* Fix the detail vc layout in Maps sample
* Deactivate search bar when the detail vc shows in Maps sample
2020-12-05 11:36:54 +09:00
Ryan McLeod 199a77182b Work magic numbers out of Adaptive Panel (Custom Layout Guide) example (#412) 2020-12-05 11:36:21 +09:00
Shin Yamamoto 0a0f00172d Add FloatingPanelAdaptiveLayoutAnchor (#390)
* Rename PassThroughView to PassthroughView
* Refactor LayoutAnchor initializer
* Add FloatignPanelAdaptiveLayoutAnchor
* Add samples for FloatingPanelAdaptiveLayoutAnchor
* Revise updateStaticConstraint
2020-12-01 19:33:19 +09:00
Shin Yamamoto 25ca9487fb Open the default behavior class (#406)
* Allow open access to FloatingPanelDefaultBehavior
* Move the default presenting & dismissing animators
* Add the public initializer of FloatingPanelDefaultBehavior
2020-11-30 21:21:21 +09:00
Shin Yamamoto 231ee4c9af Create FUNDING.yml for GitHub Sponsors
According to #400 request.
2020-11-28 10:32:03 +09:00
Ryan McLeod c872218446 Add escape gesture to close modal for VoiceOver users (#383)
* Allow dismissing of panel with Accessibility escape gesture
2020-11-07 11:29:23 +09:00
Takao Horiguchi 8e8c6527d4 update readme url for Transitioning.swift (#398) 2020-10-23 19:24:10 +09:00
Shin Yamamoto 3b4e237eba Merge pull request #397 from SCENEE/release-2.0.1
Release 2.0.1
2020-10-21 20:41:34 +09:00
Shin Yamamoto 9b1cb68f0a Version 2.0.1 2020-10-19 19:22:53 +09:00
Shin Yamamoto 4ba8acaf08 Ease the default velocity for a panel removal (#395)
Because the current threshold(10.0) makes it hard to remove a panel.

Resolve #389
2020-10-19 19:20:16 +09:00
Shin Yamamoto 02d8d4516c Reset the moveAnimator prop after a move animation (#396)
* Reset the moveAnimator prop after a move animation

If `moveAnimator` isn't null, `FloatingPanelPanGestureRecognizer.touchesBegan`
detects `began` state quickly so that it doesn’t allow to work a tap gesture or
tap action in a panel.

Resolve #392

* Add 2 buttons in DebugTableViewController
2020-10-19 19:17:53 +09:00
Federico Zanetello 94829d2749 add missing commas (#387) 2020-10-07 20:11:04 +09:00
Shin Yamamoto 1522a0990f Merge pull request #372 from SCENEE/v2-dev
Release FloatingPanel 2.0.0
2020-10-03 09:01:19 +09:00
Shin Yamamoto 2e3bbaeec2 Merge pull request #386 from SCENEE/release-1.7.6
Release 1.7.6
2020-09-19 12:10:10 +09:00
23 changed files with 411 additions and 104 deletions
+1
View File
@@ -0,0 +1 @@
github: SCENEE
@@ -194,8 +194,8 @@ class MyPanelIntrinsicLayout: FloatingPanelLayout {
var initialState: FloatingPanelState { .full }
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 16.0, referenceGuide: .safeArea)
.half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea)
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 16.0, referenceGuide: .safeArea),
.half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea)
]
}
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" 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="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"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -28,6 +28,7 @@
<blurEffect style="prominent"/>
</visualEffectView>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="5Jw-n2-Cpw" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailing" id="1Fg-Be-qfh"/>
@@ -39,7 +40,6 @@
<constraint firstItem="6Tk-OE-BBY" firstAttribute="top" secondItem="d9i-3g-8Ja" secondAttribute="bottom" id="Y1G-hr-aEX"/>
<constraint firstItem="d9i-3g-8Ja" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="diB-Ij-HN3"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="mapView" destination="5Jw-n2-Cpw" id="WVC-rU-mLe"/>
@@ -233,6 +233,7 @@
<blurEffect style="prominent"/>
</visualEffectView>
</subviews>
<viewLayoutGuide key="safeArea" id="G74-X7-Za8"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="Ye3-uU-bq3" firstAttribute="leading" secondItem="G74-X7-Za8" secondAttribute="leading" id="Kr2-sU-ZWZ"/>
@@ -240,7 +241,6 @@
<constraint firstItem="Ye3-uU-bq3" firstAttribute="trailing" secondItem="G74-X7-Za8" secondAttribute="trailing" id="fEL-8y-Acc"/>
<constraint firstItem="Ye3-uU-bq3" firstAttribute="top" secondItem="Ncl-E9-yRn" secondAttribute="top" id="w77-ba-FrJ"/>
</constraints>
<viewLayoutGuide key="safeArea" id="G74-X7-Za8"/>
</view>
<connections>
<outlet property="searchBar" destination="Zcj-SE-gb8" id="BH7-Gy-RG5"/>
@@ -262,7 +262,7 @@
<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"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="9fL-a5-0LS">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -282,8 +282,8 @@
<blurEffect style="extraLight"/>
</visualEffectView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<viewLayoutGuide key="safeArea" id="ctv-Dd-JUc"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="EDp-D2-xcT" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
@@ -21,6 +21,9 @@ extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
deactivate(searchBar: searchVC.searchBar)
// Show a detail panel
switch indexPath.row {
case 0:
+12 -4
View File
@@ -114,19 +114,27 @@ extension FloatingPanelController {
// MARK: - UISearchBarDelegate
extension ViewController: UISearchBarDelegate {
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
func activate(searchBar: UISearchBar) {
searchBar.showsCancelButton = true
searchVC.showHeader(animated: true)
searchVC.tableView.alpha = 1.0
detailVC.dismiss(animated: true, completion: nil)
}
func deactivate(searchBar: UISearchBar) {
searchBar.resignFirstResponder()
searchBar.showsCancelButton = false
searchVC.hideHeader(animated: true)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
deactivate(searchBar: searchBar)
UIView.animate(withDuration: 0.25) {
self.fpc.move(to: .half, animated: false)
}
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searchBar.showsCancelButton = true
searchVC.showHeader(animated: true)
searchVC.tableView.alpha = 1.0
activate(searchBar: searchBar)
UIView.animate(withDuration: 0.25) { [weak self] in
self?.fpc.move(to: .full, animated: false)
}
@@ -1,6 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "IMG_0003.jpg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" 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="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="RoN-h0-uBD">
<device id="retina5_9" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
<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"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -293,6 +294,71 @@
</objects>
<point key="canvasLocation" x="2753" y="734"/>
</scene>
<!--Image View Controller-->
<scene sceneID="NAI-Rh-ZQ6">
<objects>
<viewController storyboardIdentifier="ImageViewController" id="VWY-cF-RoY" customClass="ImageViewController" customModule="Samples" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="dAf-gD-ghB">
<rect key="frame" x="0.0" y="0.0" width="375" height="778"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="Gs4-S6-Goh">
<rect key="frame" x="0.0" y="0.0" width="375" height="778"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="t7x-eG-MKh">
<rect key="frame" x="0.0" y="0.0" width="375" height="49"/>
<color key="backgroundColor" systemColor="systemOrangeColor"/>
<constraints>
<constraint firstAttribute="height" constant="49" id="DmG-pt-gij"/>
</constraints>
</view>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" alwaysBounceHorizontal="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kRA-qy-GpJ">
<rect key="frame" x="0.0" y="49" width="375" height="680"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="1000" verticalHuggingPriority="1000" image="IMG_0003" translatesAutoresizingMaskIntoConstraints="NO" id="rGf-jW-WNf">
<rect key="frame" x="0.0" y="0.0" width="750" height="501"/>
<color key="backgroundColor" systemColor="systemPurpleColor"/>
</imageView>
</subviews>
<color key="backgroundColor" red="0.0078431372550000003" green="0.72156862749999995" blue="0.45882352939999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="rGf-jW-WNf" secondAttribute="trailing" id="472-Be-cUl"/>
<constraint firstAttribute="bottom" secondItem="rGf-jW-WNf" secondAttribute="bottom" id="ncs-tN-3Wx"/>
<constraint firstItem="rGf-jW-WNf" firstAttribute="leading" secondItem="kRA-qy-GpJ" secondAttribute="leading" id="rlv-5Y-utR"/>
<constraint firstItem="rGf-jW-WNf" firstAttribute="top" secondItem="kRA-qy-GpJ" secondAttribute="top" id="zum-Zl-Wzz"/>
</constraints>
</scrollView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SEa-7Y-wa9">
<rect key="frame" x="0.0" y="729" width="375" height="49"/>
<color key="backgroundColor" systemColor="systemTealColor"/>
<constraints>
<constraint firstAttribute="height" constant="49" id="3mS-zi-8BP"/>
</constraints>
</view>
</subviews>
</stackView>
</subviews>
<viewLayoutGuide key="safeArea" id="hCg-v5-nJs"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="Gs4-S6-Goh" secondAttribute="trailing" id="6z7-Md-pxr"/>
<constraint firstAttribute="bottom" secondItem="Gs4-S6-Goh" secondAttribute="bottom" id="PcQ-bu-yT3"/>
<constraint firstItem="Gs4-S6-Goh" firstAttribute="top" secondItem="dAf-gD-ghB" secondAttribute="top" id="zGx-Wd-hjz"/>
<constraint firstItem="Gs4-S6-Goh" firstAttribute="leading" secondItem="dAf-gD-ghB" secondAttribute="leading" id="zxi-Ty-U7L"/>
</constraints>
</view>
<size key="freeformSize" width="375" height="778"/>
<connections>
<outlet property="footerView" destination="SEa-7Y-wa9" id="Gzj-dP-YXl"/>
<outlet property="headerView" destination="t7x-eG-MKh" id="njM-un-U8q"/>
<outlet property="scrollView" destination="kRA-qy-GpJ" id="iWC-o4-APi"/>
<outlet property="stackView" destination="Gs4-S6-Goh" id="f1D-bO-mjr"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="pnR-69-Ek4" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3388" y="734.48275862068965"/>
</scene>
<!--Tab Bar View Controller-->
<scene sceneID="nQ5-PV-qFw">
<objects>
@@ -725,4 +791,19 @@ Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
<inferredMetricsTieBreakers>
<segue reference="r1P-2i-NDe"/>
</inferredMetricsTieBreakers>
<resources>
<image name="IMG_0003" width="750" height="501"/>
<systemColor name="systemBackgroundColor">
<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"/>
</systemColor>
<systemColor name="systemPurpleColor">
<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"/>
</systemColor>
</resources>
</document>
+105 -7
View File
@@ -23,7 +23,9 @@ class SampleListViewController: UIViewController {
case showContentInset
case showContainerMargins
case showNavigationController
case showBottomEdgeInteraction
case showTopPositionedPanel
case showAdaptivePanel
case showAdaptivePanelWithCustomGuide
var name: String {
switch self {
@@ -43,7 +45,9 @@ class SampleListViewController: UIViewController {
case .showContentInset: return "Show with ContentInset"
case .showContainerMargins: return "Show with ContainerMargins"
case .showNavigationController: return "Show Navigation Controller"
case .showBottomEdgeInteraction: return "Show bottom edge interaction"
case .showTopPositionedPanel: return "Show Top Positioned Panel"
case .showAdaptivePanel: return "Show Adaptive Panel"
case .showAdaptivePanelWithCustomGuide: return "Show Adaptive Panel (Custom Layout Guide)"
}
}
@@ -65,7 +69,10 @@ class SampleListViewController: UIViewController {
case .showContentInset: return nil
case .showContainerMargins: return nil
case .showNavigationController: return "RootNavigationController"
case .showBottomEdgeInteraction: return nil
case .showTopPositionedPanel: return nil
case .showAdaptivePanel,
.showAdaptivePanelWithCustomGuide:
return "ImageViewController"
}
}
}
@@ -142,6 +149,8 @@ class SampleListViewController: UIViewController {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(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 = self
@@ -154,7 +163,7 @@ class SampleListViewController: UIViewController {
mainPanelVC.backdropView.dismissalTapGestureRecognizer.isEnabled = true
case .showNavigationController:
mainPanelVC.contentInsetAdjustmentBehavior = .never
case .showBottomEdgeInteraction: // For debug
case .showTopPositionedPanel: // For debug
let contentVC = UIViewController()
contentVC.view.backgroundColor = .red
mainPanelVC.set(contentViewController: contentVC)
@@ -182,6 +191,17 @@ class SampleListViewController: UIViewController {
rootVC.loadViewIfNeeded()
mainPanelVC.track(scrollView: rootVC.tableView)
}
case let contentVC as ImageViewController:
if #available(iOS 11.0, *) {
let mode: ImageViewController.Mode = (currentMenu == .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
}
@@ -417,8 +437,8 @@ extension SampleListViewController: FloatingPanelControllerDelegate {
}
switch currentMenu {
case .showBottomEdgeInteraction:
return BottomEdgeInteractionLayout()
case .showTopPositionedPanel:
return TopPositionedPanelLayout()
case .showRemovablePanel:
return newCollection.verticalSizeClass == .compact ? RemovablePanelLandscapeLayout() : RemovablePanelLayout()
case .showIntrinsicView:
@@ -504,7 +524,7 @@ extension SampleListViewController: UIPageViewControllerDelegate {
}
}
class BottomEdgeInteractionLayout: FloatingPanelLayout {
class TopPositionedPanelLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .top
let initialState: FloatingPanelState = .full
@@ -684,6 +704,8 @@ class DebugTableViewController: InspectableViewController {
case animateScroll = "Animate Scroll"
case changeContentSize = "Change content size"
case reorder = "Reorder"
case moveToFull = "Move to Full"
case moveToHalf = "Move to Half"
}
var reorderButton: UIButton!
@@ -725,6 +747,10 @@ class DebugTableViewController: InspectableViewController {
case .reorder:
button.addTarget(self, action: #selector(reorderItems), for: .touchUpInside)
reorderButton = button
case .moveToFull:
button.addTarget(self, action: #selector(moveToFull), for: .touchUpInside)
case .moveToHalf:
button.addTarget(self, action: #selector(moveToHalf), for: .touchUpInside)
}
buttonStackView.addArrangedSubview(button)
}
@@ -785,6 +811,14 @@ class DebugTableViewController: InspectableViewController {
tableView.reloadData()
}
@objc func moveToFull() {
(self.parent as! FloatingPanelController).move(to: .full, animated: true)
}
@objc func moveToHalf() {
(self.parent as! FloatingPanelController).move(to: .half, animated: true)
}
@objc func close(sender: UIButton) {
// Remove FloatingPanel from a view
(self.parent as! FloatingPanelController).removePanelFromParent(animated: true, completion: nil)
@@ -1282,3 +1316,67 @@ final class MultiPanelController: FloatingPanelController, FloatingPanelControll
}
}
}
class ImageViewController: UIViewController {
class PanelLayout: FloatingPanelLayout {
weak var targetGuide: UILayoutGuide?
init(targetGuide: UILayoutGuide?) {
self.targetGuide = targetGuide
}
let position: FloatingPanelPosition = .bottom
let initialState: FloatingPanelState = .full
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
if #available(iOS 11.0, *), let targetGuide = targetGuide {
return [
.full: FloatingPanelAdaptiveLayoutAnchor(absoluteOffset: 0,
contentLayout: targetGuide,
referenceGuide: .superview),
.half: FloatingPanelAdaptiveLayoutAnchor(fractionalOffset: 0.5,
contentLayout: targetGuide,
referenceGuide: .superview)
]
} else {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 500,
edge: .bottom,
referenceGuide: .superview)
]
}
}
}
@IBOutlet weak var headerView: UIView!
@IBOutlet weak var footerView: UIView!
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var stackView: UIStackView!
enum Mode {
case onlyImage
case withHeaderFooter
}
@available(iOS 11.0, *)
func layoutGuideFor(mode: Mode) -> UILayoutGuide {
switch mode {
case .onlyImage:
self.headerView.isHidden = true
self.footerView.isHidden = true
return scrollView.contentLayoutGuide
case .withHeaderFooter:
self.headerView.isHidden = false
self.footerView.isHidden = false
let guide = UILayoutGuide()
view.addLayoutGuide(guide)
NSLayoutConstraint.activate([
scrollView.heightAnchor.constraint(equalTo: scrollView.contentLayoutGuide.heightAnchor),
guide.topAnchor.constraint(equalTo: stackView.topAnchor),
guide.leftAnchor.constraint(equalTo: stackView.leftAnchor),
guide.bottomAnchor.constraint(equalTo: stackView.bottomAnchor),
guide.rightAnchor.constraint(equalTo: stackView.rightAnchor),
])
return guide
}
}
}
@@ -1,6 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}
@@ -61,10 +61,10 @@
return @{
FloatingPanelState.Half: [[FloatingPanelLayoutAnchor alloc] initWithFractionalInset:0.5
edge:FloatingPanelReferenceEdgeTop
referenceGuide:FloatingPanelLayoutReferenceGuideSafeArea],
referenceGuide:FloatingPanelLayoutReferenceGuideSafeArea],
FloatingPanelState.Tip: [[FloatingPanelLayoutAnchor alloc] initWithAbsoluteInset:44.0
edge:FloatingPanelReferenceEdgeBottom
referenceGuide:FloatingPanelLayoutReferenceGuideSafeArea],
referenceGuide:FloatingPanelLayoutReferenceGuideSafeArea],
};
}
+1 -1
View File
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "FloatingPanel"
s.version = "2.0.0"
s.version = "2.1.0"
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.
+4 -4
View File
@@ -9,7 +9,7 @@
/* Begin PBXBuildFile section */
542753C622C49A6E00D17955 /* LayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542753C522C49A6E00D17955 /* LayoutTests.swift */; };
54352E9621A51A2500CBCA08 /* Transitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54352E9521A51A2500CBCA08 /* Transitioning.swift */; };
54352E9821A521CA00CBCA08 /* PassThroughView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54352E9721A521CA00CBCA08 /* PassThroughView.swift */; };
54352E9821A521CA00CBCA08 /* PassthroughView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54352E9721A521CA00CBCA08 /* PassthroughView.swift */; };
5450EEE421646DF500135936 /* Behavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5450EEE321646DF500135936 /* Behavior.swift */; };
545DB9CB2151169500CA77B8 /* FloatingPanel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 545DB9C12151169500CA77B8 /* FloatingPanel.framework */; };
545DB9D02151169500CA77B8 /* ControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DB9CF2151169500CA77B8 /* ControllerTests.swift */; };
@@ -56,7 +56,7 @@
542753C522C49A6E00D17955 /* LayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutTests.swift; sourceTree = "<group>"; };
542753C722C49A8F00D17955 /* TestSupports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSupports.swift; sourceTree = "<group>"; };
54352E9521A51A2500CBCA08 /* Transitioning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transitioning.swift; sourceTree = "<group>"; };
54352E9721A521CA00CBCA08 /* PassThroughView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassThroughView.swift; sourceTree = "<group>"; };
54352E9721A521CA00CBCA08 /* PassthroughView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassthroughView.swift; sourceTree = "<group>"; };
5450EEE321646DF500135936 /* Behavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Behavior.swift; sourceTree = "<group>"; };
545DB9C12151169500CA77B8 /* FloatingPanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FloatingPanel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
545DB9C42151169500CA77B8 /* FloatingPanel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FloatingPanel.h; sourceTree = "<group>"; };
@@ -142,7 +142,7 @@
5469F4B324B30F3500537F8A /* LayoutReferences.swift */,
5469F4AF24B30E1500537F8A /* LayoutAnchoring.swift */,
5450EEE321646DF500135936 /* Behavior.swift */,
54352E9721A521CA00CBCA08 /* PassThroughView.swift */,
54352E9721A521CA00CBCA08 /* PassthroughView.swift */,
54CDC5D2215B6D5A007D205C /* SurfaceView.swift */,
54CDC5D4215B6D8D007D205C /* BackdropView.swift */,
545DBA2A2152383100CA77B8 /* GrabberView.swift */,
@@ -328,7 +328,7 @@
54CFBFC3215CD045006B5735 /* Layout.swift in Sources */,
5469F4B424B30F3500537F8A /* LayoutReferences.swift in Sources */,
54CDC5D5215B6D8D007D205C /* BackdropView.swift in Sources */,
54352E9821A521CA00CBCA08 /* PassThroughView.swift in Sources */,
54352E9821A521CA00CBCA08 /* PassthroughView.swift in Sources */,
54CFBFC5215CD09C006B5735 /* Core.swift in Sources */,
54ABD7AF216CCFF7002E6C13 /* Logger.swift in Sources */,
545DB9E021511AC100CA77B8 /* Controller.swift in Sources */,
+1 -1
View File
@@ -163,7 +163,7 @@ self.present(fpc, animated: true, completion: nil)
You can show a floating panel over UINavigationController from the container view controllers as a modality of `.overCurrentContext` style.
:pencil2: FloatingPanelController has the custom presentation controller. If you would like to customize the presentation/dismissal, please see [FloatingPanelTransitioning](https://github.com/SCENEE/FloatingPanel/blob/master/Framework/Sources/FloatingPanelTransitioning.swift).
:pencil2: FloatingPanelController has the custom presentation controller. If you would like to customize the presentation/dismissal, please see [Transitioning](https://github.com/SCENEE/FloatingPanel/blob/master/Sources/Transitioning.swift).
## View hierarchy
+8 -19
View File
@@ -48,37 +48,26 @@ public protocol FloatingPanelBehavior {
/// The default behavior object for a panel
///
/// This behavior object is fine-tuned to behave as a search panel(card) in Apple Maps on iPhone portrait orientation.
public class FloatingPanelDefaultBehavior: FloatingPanelBehavior {
public var springDecelerationRate: CGFloat {
open class FloatingPanelDefaultBehavior: FloatingPanelBehavior {
public init() {}
open var springDecelerationRate: CGFloat {
return UIScrollView.DecelerationRate.fast.rawValue + 0.001
}
public var springResponseTime: CGFloat {
open var springResponseTime: CGFloat {
return 0.4
}
public var momentumProjectionRate: CGFloat {
open var momentumProjectionRate: CGFloat {
return UIScrollView.DecelerationRate.normal.rawValue
}
public func redirectionalProgress(_ fpc: FloatingPanelController, from: FloatingPanelState, to: FloatingPanelState) -> CGFloat {
open func redirectionalProgress(_ fpc: FloatingPanelController, from: FloatingPanelState, to: FloatingPanelState) -> CGFloat {
return 0.5
}
func addPanelAnimator(_ fpc: FloatingPanelController, to: FloatingPanelState) -> UIViewPropertyAnimator {
return UIViewPropertyAnimator(duration: 0.0,
timingParameters: UISpringTimingParameters(decelerationRate: UIScrollView.DecelerationRate.fast.rawValue,
frequencyResponse: 0.25))
}
func removePanelAnimator(_ fpc: FloatingPanelController, from: FloatingPanelState, with velocity: CGVector) -> UIViewPropertyAnimator {
return UIViewPropertyAnimator(duration: 0.0,
timingParameters: UISpringTimingParameters(decelerationRate: UIScrollView.DecelerationRate.fast.rawValue,
frequencyResponse: 0.25,
initialVelocity: velocity))
}
public func allowsRubberBanding(for edge: UIRectEdge) -> Bool {
open func allowsRubberBanding(for edge: UIRectEdge) -> Bool {
return false
}
}
+30 -1
View File
@@ -274,7 +274,7 @@ open class FloatingPanelController: UIViewController {
open override func loadView() {
assert(self.storyboard == nil, "Storyboard isn't supported")
let view = PassThroughView()
let view = PassthroughView()
view.backgroundColor = .clear
backdropView.frame = view.bounds
@@ -598,6 +598,14 @@ open class FloatingPanelController: UIViewController {
}
}
// MARK: - Accessibility
open override func accessibilityPerformEscape() -> Bool {
guard isRemovalInteractionEnabled else { return false }
dismiss(animated: true, completion: nil)
return true
}
// MARK: - Utilities
/// Updates the layout object from the delegate and lays out the views managed
@@ -641,6 +649,27 @@ extension FloatingPanelController {
#endif
delegate?.floatingPanelDidMove?(self)
}
func animatorForPresenting(to: FloatingPanelState) -> UIViewPropertyAnimator {
if let animator = delegate?.floatingPanel?(self, animatorForPresentingTo: to) {
return animator
}
let timingParameters = UISpringTimingParameters(decelerationRate: UIScrollView.DecelerationRate.fast.rawValue,
frequencyResponse: 0.25)
return UIViewPropertyAnimator(duration: 0.0,
timingParameters: timingParameters)
}
func animatorForDismissing(with velocity: CGVector) -> UIViewPropertyAnimator {
if let animator = delegate?.floatingPanel?(self, animatorForDismissingWith: velocity) {
return animator
}
let timingParameters = UISpringTimingParameters(decelerationRate: UIScrollView.DecelerationRate.fast.rawValue,
frequencyResponse: 0.25,
initialVelocity: velocity)
return UIViewPropertyAnimator(duration: 0.0,
timingParameters: timingParameters)
}
}
extension FloatingPanelController {
+10 -13
View File
@@ -68,11 +68,8 @@ class Core: NSObject, UIGestureRecognizerDelegate {
backdropView.backgroundColor = .black
backdropView.alpha = 0.0
self.layoutAdapter = LayoutAdapter(vc: vc,
surfaceView: surfaceView,
backdropView: backdropView,
layout: layout)
self.behaviorAdapter = BehaviorAdapter(vc: vc, behavior: behavior)
layoutAdapter = LayoutAdapter(vc: vc, layout: layout)
behaviorAdapter = BehaviorAdapter(vc: vc, behavior: behavior)
panGestureRecognizer = FloatingPanelPanGestureRecognizer()
@@ -123,14 +120,13 @@ class Core: NSObject, UIGestureRecognizerDelegate {
let animator: UIViewPropertyAnimator
switch (from, to) {
case (.hidden, let to):
animator = vc.delegate?.floatingPanel?(vc, animatorForPresentingTo: to)
?? FloatingPanelDefaultBehavior().addPanelAnimator(vc, to: to)
animator = vc.animatorForPresenting(to: to)
case (let from, .hidden):
let animationVector = CGVector(dx: abs(removalVector.dx), dy: abs(removalVector.dy))
animator = vc.delegate?.floatingPanel?(vc, animatorForDismissingWith: .zero)
?? FloatingPanelDefaultBehavior().removePanelAnimator(vc, from: from, with: animationVector)
animator = vc.animatorForDismissing(with: animationVector)
default:
move(to: to, with: 0) {
self.moveAnimator = nil
updateScrollView()
completion?()
}
@@ -717,15 +713,16 @@ class Core: NSObject, UIGestureRecognizerDelegate {
if let result = vc.delegate?.floatingPanel?(vc, shouldRemoveAt: vc.surfaceLocation, with: velocityVector) {
return result
}
let threshold = CGFloat(5.5)
switch layoutAdapter.position {
case .top:
return (velocityVector.dy <= -10.0)
return (velocityVector.dy <= -threshold)
case .left:
return (velocityVector.dx <= -10.0)
return (velocityVector.dx <= -threshold)
case .bottom:
return (velocityVector.dy >= 10.0)
return (velocityVector.dy >= threshold)
case .right:
return (velocityVector.dx >= 10.0)
return (velocityVector.dx >= threshold)
}
}
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.0.0</string>
<string>2.1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
+44 -16
View File
@@ -61,8 +61,6 @@ struct LayoutSegment {
class LayoutAdapter {
weak var vc: FloatingPanelController!
private weak var surfaceView: SurfaceView!
private weak var backdropView: BackdropView!
private let defaultLayout = FloatingPanelBottomLayout()
fileprivate var layout: FloatingPanelLayout {
@@ -71,6 +69,12 @@ class LayoutAdapter {
}
}
private var surfaceView: SurfaceView {
return vc.surfaceView
}
private var backdropView: BackdropView {
return vc.backdropView
}
private var safeAreaInsets: UIEdgeInsets {
return vc?.fp_safeAreaInsets ?? .zero
}
@@ -284,14 +288,9 @@ class LayoutAdapter {
}
}
init(vc: FloatingPanelController,
surfaceView: SurfaceView,
backdropView: BackdropView,
layout: FloatingPanelLayout) {
init(vc: FloatingPanelController, layout: FloatingPanelLayout) {
self.vc = vc
self.layout = layout
self.surfaceView = surfaceView
self.backdropView = backdropView
}
func surfaceLocation(for state: FloatingPanelState) -> CGPoint {
@@ -327,6 +326,22 @@ class LayoutAdapter {
}
return base - intrinsicLength + diff
}
case let anchor as FloatingPanelAdaptiveLayoutAnchor:
let dimension = layout.position.mainDimension(anchor.contentLayoutGuide.layoutFrame.size)
let diff = anchor.distance(from: dimension)
var referenceBoundsLength = layout.position.mainDimension(bounds.size)
switch layout.position {
case .top, .left:
if anchor.referenceGuide == .safeArea {
referenceBoundsLength += position.inset(safeAreaInsets)
}
return dimension - diff
case .bottom, .right:
if anchor.referenceGuide == .safeArea {
referenceBoundsLength -= position.inset(safeAreaInsets)
}
return referenceBoundsLength - dimension + diff
}
case let anchor as FloatingPanelLayoutAnchor:
let referenceBounds = anchor.referenceGuide == .safeArea ? bounds.inset(by: safeAreaInsets) : bounds
let diff = anchor.isAbsolute ? anchor.inset : position.mainDimension(referenceBounds.size) * anchor.inset
@@ -364,7 +379,8 @@ class LayoutAdapter {
private func referenceEdge(of anchor: FloatingPanelLayoutAnchoring) -> FloatingPanelReferenceEdge {
switch anchor {
case is FloatingPanelIntrinsicLayoutAnchor:
case is FloatingPanelIntrinsicLayoutAnchor,
is FloatingPanelAdaptiveLayoutAnchor:
switch position {
case .top: return .top
case .left: return .left
@@ -640,19 +656,31 @@ class LayoutAdapter {
}
let anchor = layout.anchors[self.edgeMostState]!
if anchor is FloatingPanelIntrinsicLayoutAnchor {
var constant = layout.position.mainDimension(surfaceView.intrinsicContentSize)
let surfaceAnchor = position.mainDimensionAnchor(surfaceView)
switch anchor {
case let anchor as FloatingPanelIntrinsicLayoutAnchor:
var constant = position.mainDimension(surfaceView.intrinsicContentSize)
if anchor.referenceGuide == .safeArea {
constant += position.inset(safeAreaInsets)
}
staticConstraint = position.mainDimensionAnchor(surfaceView).constraint(equalToConstant: constant)
} else {
staticConstraint = surfaceAnchor.constraint(equalToConstant: constant)
case let anchor as FloatingPanelAdaptiveLayoutAnchor:
let constant: CGFloat
if anchor.referenceGuide == .safeArea {
constant = position.inset(safeAreaInsets)
} else {
constant = 0.0
}
let baseAnchor = position.mainDimensionAnchor(anchor.contentLayoutGuide)
staticConstraint = surfaceAnchor.constraint(equalTo: baseAnchor, constant: constant)
default:
switch position {
case .top, .left:
staticConstraint = position.mainDimensionAnchor(surfaceView).constraint(equalToConstant: position(for: self.directionalMostState))
staticConstraint = surfaceAnchor.constraint(equalToConstant: position(for: self.directionalMostState))
case .bottom, .right:
staticConstraint = position.mainDimensionAnchor(vc.view).constraint(equalTo: position.mainDimensionAnchor(surfaceView),
constant: position(for: self.directionalLeastState))
let rootViewAnchor = position.mainDimensionAnchor(vc.view)
staticConstraint = rootViewAnchor.constraint(equalTo: surfaceAnchor,
constant: position(for: self.directionalLeastState))
}
}
+75 -12
View File
@@ -8,22 +8,25 @@ import UIKit
func layoutConstraints(_ fpc: FloatingPanelController, for position: FloatingPanelPosition) -> [NSLayoutConstraint]
}
/// A layout anchor object that anchors a panel in a state.
/// An object that defines how to settles a panel with insets from an edge of a reference rectangle.
@objc final public class FloatingPanelLayoutAnchor: NSObject, FloatingPanelLayoutAnchoring /*, NSCopying */ {
/// Initializes and returns a layout anchor object to specify an absolute inset value for the position of a panel.
/// Returns a layout anchor with the specified inset by an absolute value, edge and reference guide for a panel.
///
/// The inset is a distance from the edge of the specified layout guide.
/// The inset is an amount to inset a panel from an edge of the reference guide. The edge refers to a panel
/// positioning.
@objc public init(absoluteInset: CGFloat, edge: FloatingPanelReferenceEdge, referenceGuide: FloatingPanelLayoutReferenceGuide) {
self.inset = absoluteInset
self.referenceGuide = referenceGuide
self.referenceEdge = edge
self.isAbsolute = true
}
/// Initializes and returns a layout anchor object to specify a fractional inset value for the position of a panel.
/// Returns a layout anchor with the specified inset by a fractional value, edge and reference guide for a panel.
///
/// The inset is a distance from the edge of the specified layout guide. The value is a floating-point number
/// in the range 0.0 to 1.0, where 0.0 represents zero distance from the edge and 1.0 represents a distance
/// to the opposite edge.
/// The inset is an amount to inset a panel from the edge of the specified reference guide. The value is
/// a floating-point number in the range 0.0 to 1.0, where 0.0 represents zero distance from the edge and
/// 1.0 represents a distance to the opposite edge.
@objc public init(fractionalInset: CGFloat, edge: FloatingPanelReferenceEdge, referenceGuide: FloatingPanelLayoutReferenceGuide) {
self.inset = fractionalInset
self.referenceGuide = referenceGuide
@@ -91,19 +94,20 @@ public extension FloatingPanelLayoutAnchor {
}
}
/// A layout anchor object that anchors a panel in a state using the intrinsic size for a content.
/// An object that defines how to settles a panel with the intrinsic size for a content.
@objc final public class FloatingPanelIntrinsicLayoutAnchor: NSObject, FloatingPanelLayoutAnchoring /*, NSCopying */ {
/// Initializes and returns a layout anchor object to specify an absolute offset value for the position of a panel.
/// Returns a layout anchor with the specified offset by an absolute value and reference guide for a panel.
///
/// The offset is a distance from a position at which a panel displays the entire content.
/// The offset is an amount to offset a position of panel that displays the entire content from an edge of
/// the reference guide. The edge refers to a panel positioning.
@objc public init(absoluteOffset offset: CGFloat, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
self.offset = offset
self.referenceGuide = referenceGuide
self.isAbsolute = true
}
/// Initializes and returns a layout anchor object to specify a fractional offset value for the position of a panel.
/// Returns a layout anchor with the specified offset by a fractional value and reference guide for a panel.
///
/// The offset value is a floating-point number in the range 0.0 to 1.0, where 0.0 represents the full content
/// is displayed and 0.5 represents the half of content is displayed.
@@ -136,3 +140,62 @@ public extension FloatingPanelIntrinsicLayoutAnchor {
}
}
}
/// An object that defines how to settles a panel with a layout guide of a content view.
@objc final public class FloatingPanelAdaptiveLayoutAnchor: NSObject, FloatingPanelLayoutAnchoring /*, NSCopying */ {
/// Returns a layout anchor with the specified offset by an absolute value, layout guide to display content and reference guide for a panel.
///
/// The offset is an amount to offset a position of panel that displays the entire content of the specified guide from an edge of
/// the reference guide. The edge refers to a panel positioning.
@objc public init(absoluteOffset offset: CGFloat, contentLayout: UILayoutGuide, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
self.offset = offset
self.contentLayoutGuide = contentLayout
self.referenceGuide = referenceGuide
self.isAbsolute = true
}
/// Returns a layout anchor with the specified offset by a fractional value, layout guide to display content and reference guide for a panel.
///
/// The offset value is a floating-point number in the range 0.0 to 1.0, where 0.0 represents the full content
/// is displayed and 0.5 represents the half of content is displayed.
@objc public init(fractionalOffset offset: CGFloat, contentLayout: UILayoutGuide, referenceGuide: FloatingPanelLayoutReferenceGuide = .safeArea) {
self.offset = offset
self.contentLayoutGuide = contentLayout
self.referenceGuide = referenceGuide
self.isAbsolute = false
}
fileprivate let offset: CGFloat
fileprivate let isAbsolute: Bool
let contentLayoutGuide: UILayoutGuide
@objc public let referenceGuide: FloatingPanelLayoutReferenceGuide
}
public extension FloatingPanelAdaptiveLayoutAnchor {
func layoutConstraints(_ vc: FloatingPanelController, for position: FloatingPanelPosition) -> [NSLayoutConstraint] {
let layoutGuide = referenceGuide.layoutGuide(vc: vc)
let offsetAnchor: NSLayoutDimension
switch position {
case .top:
offsetAnchor = layoutGuide.topAnchor.anchorWithOffset(to: vc.surfaceView.bottomAnchor)
case .left:
offsetAnchor = layoutGuide.leftAnchor.anchorWithOffset(to: vc.surfaceView.rightAnchor)
case .bottom:
offsetAnchor = vc.surfaceView.topAnchor.anchorWithOffset(to: layoutGuide.bottomAnchor)
case .right:
offsetAnchor = vc.surfaceView.leftAnchor.anchorWithOffset(to: layoutGuide.rightAnchor)
}
if isAbsolute {
return [offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), constant: -offset)]
} else {
return [offsetAnchor.constraint(equalTo: position.mainDimensionAnchor(contentLayoutGuide), multiplier: (1 - offset))]
}
}
}
extension FloatingPanelAdaptiveLayoutAnchor {
func distance(from dimension: CGFloat) -> CGFloat {
return isAbsolute ? offset : dimension * offset
}
}
@@ -2,8 +2,8 @@
import UIKit
@objc(FloatingPanelPassThroughView)
class PassThroughView: UIView {
@objc(FloatingPanelPassthroughView)
class PassthroughView: UIView {
public weak var eventForwardingView: UIView?
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
+3 -5
View File
@@ -61,7 +61,7 @@ class PresentationController: UIPresentationController {
addFloatingPanel()
// Forward touch events to the presenting view controller
(fpc.view as? PassThroughView)?.eventForwardingView = presentingViewController.view
(fpc.view as? PassthroughView)?.eventForwardingView = presentingViewController.view
}
@objc func handleBackdrop(tapGesture: UITapGestureRecognizer) {
@@ -86,8 +86,7 @@ class ModalPresentTransition: NSObject, UIViewControllerAnimatedTransitioning {
let fpc = transitionContext?.viewController(forKey: .to) as? FloatingPanelController
else { fatalError()}
let animator = fpc.delegate?.floatingPanel?(fpc, animatorForPresentingTo: fpc.layout.initialState)
?? FloatingPanelDefaultBehavior().addPanelAnimator(fpc, to: fpc.layout.initialState)
let animator = fpc.animatorForPresenting(to: fpc.layout.initialState)
return TimeInterval(animator.duration)
}
@@ -108,8 +107,7 @@ class ModalDismissTransition: NSObject, UIViewControllerAnimatedTransitioning {
let fpc = transitionContext?.viewController(forKey: .from) as? FloatingPanelController
else { fatalError()}
let animator = fpc.delegate?.floatingPanel?(fpc, animatorForDismissingWith: .zero)
?? FloatingPanelDefaultBehavior().removePanelAnimator(fpc, from: fpc.state, with: .zero)
let animator = fpc.animatorForDismissing(with: .zero)
return TimeInterval(animator.duration)
}