Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d668c8525 | |||
| ebd4a32bfc | |||
| 6aa739231d | |||
| 8877d32ced | |||
| 7f025ae845 | |||
| 1b2dae2135 | |||
| 6e4e9df616 | |||
| 49e868a505 | |||
| f1b70e0367 | |||
| 1d0e747578 | |||
| 2c72d07cab | |||
| 31c057f9f8 | |||
| d3033df9da | |||
| 459d82b1c6 | |||
| 85d7ca640e | |||
| c1b7f2f092 | |||
| 0412bdc996 |
@@ -0,0 +1,27 @@
|
||||
> Please fill out this template when filing an issue.
|
||||
>
|
||||
> Please remove this line and everything above it before submitting.
|
||||
|
||||
### Short description
|
||||
|
||||
### Expected behavior
|
||||
|
||||
### Actual behavior
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
**Code example that reproduces the issue**
|
||||
|
||||
### Environment
|
||||
|
||||
**Library version**
|
||||
|
||||
**Installation method**
|
||||
|
||||
- [ ] CocoaPods
|
||||
- [ ] Carthage
|
||||
- [ ] Git submodules
|
||||
|
||||
**iOS version(s)**
|
||||
|
||||
**Xcode version**
|
||||
@@ -162,7 +162,7 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="708" y="-200"/>
|
||||
</scene>
|
||||
<!--Item 2-->
|
||||
<!--Layout 2-->
|
||||
<scene sceneID="lRc-OZ-sL4">
|
||||
<objects>
|
||||
<viewController id="RpE-lI-27a" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
@@ -170,12 +170,6 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Item 2" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AiP-dx-mFn">
|
||||
<rect key="frame" x="163.66666666666666" y="395.66666666666669" width="48" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IvG-yp-yzI">
|
||||
<rect key="frame" x="20" y="44" width="39" height="30"/>
|
||||
<state key="normal" title="Close"/>
|
||||
@@ -188,19 +182,49 @@
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="IvG-yp-yzI" firstAttribute="top" secondItem="954-Dk-zvc" secondAttribute="top" id="18k-sV-PgT"/>
|
||||
<constraint firstItem="AiP-dx-mFn" firstAttribute="centerY" secondItem="JER-jz-KSq" secondAttribute="centerY" id="NUc-tM-0dN"/>
|
||||
<constraint firstItem="AiP-dx-mFn" firstAttribute="centerX" secondItem="954-Dk-zvc" secondAttribute="centerX" id="hwP-mu-Vmz"/>
|
||||
<constraint firstItem="954-Dk-zvc" firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="IvG-yp-yzI" secondAttribute="trailing" id="mpr-u5-MZu"/>
|
||||
<constraint firstItem="IvG-yp-yzI" firstAttribute="leading" secondItem="954-Dk-zvc" secondAttribute="leading" constant="20" id="pYt-jE-CTF"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="954-Dk-zvc"/>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" tag="1" title="Item 2" id="qb3-RB-B28"/>
|
||||
<tabBarItem key="tabBarItem" tag="1" title="Layout 2" id="qb3-RB-B28"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="NhZ-u5-Beh" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-308" y="1546"/>
|
||||
</scene>
|
||||
<!--Item 1-->
|
||||
<!--Layout 3-->
|
||||
<scene sceneID="r9h-Ql-gIv">
|
||||
<objects>
|
||||
<viewController id="pOk-Zm-vD9" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="85d-ub-G8k">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NbG-e8-HdI">
|
||||
<rect key="frame" x="20" y="44" width="39" height="30"/>
|
||||
<state key="normal" title="Close"/>
|
||||
<connections>
|
||||
<action selector="closeWithSender:" destination="pOk-Zm-vD9" eventType="touchUpInside" id="111-PD-Pop"/>
|
||||
<action selector="closeWithSender:" destination="bYI-y3-Rzb" eventType="touchUpInside" id="1Rg-YG-TtU"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="0ao-SI-QZW" firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="NbG-e8-HdI" secondAttribute="trailing" id="K9F-6x-KWn"/>
|
||||
<constraint firstItem="NbG-e8-HdI" firstAttribute="top" secondItem="0ao-SI-QZW" secondAttribute="top" id="nsE-so-rTl"/>
|
||||
<constraint firstItem="NbG-e8-HdI" firstAttribute="leading" secondItem="0ao-SI-QZW" secondAttribute="leading" constant="20" id="sF4-Dm-aoY"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="0ao-SI-QZW"/>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" tag="2" title="Layout 3" id="RJD-TF-Sdh"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Oe3-FT-q1C" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="332" y="1546"/>
|
||||
</scene>
|
||||
<!--Layout 1-->
|
||||
<scene sceneID="m6X-j6-yBM">
|
||||
<objects>
|
||||
<viewController id="lto-Zc-Vtp" customClass="TabBarContentViewController" customModule="Samples" customModuleProvider="target" sceneMemberID="viewController">
|
||||
@@ -208,12 +232,6 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Item 1" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uoW-c8-9wx">
|
||||
<rect key="frame" x="164.66666666666666" y="395.66666666666669" width="46" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eFN-tN-4Ct">
|
||||
<rect key="frame" x="20" y="44" width="39" height="30"/>
|
||||
<state key="normal" title="Close"/>
|
||||
@@ -226,13 +244,12 @@
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="eFN-tN-4Ct" firstAttribute="leading" secondItem="5Ns-4l-Ufg" secondAttribute="leading" constant="20" id="5BT-yZ-EKe"/>
|
||||
<constraint firstItem="uoW-c8-9wx" firstAttribute="centerY" secondItem="ji9-Ez-N7i" secondAttribute="centerY" id="Nyw-Wt-78z"/>
|
||||
<constraint firstItem="5Ns-4l-Ufg" firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="eFN-tN-4Ct" secondAttribute="trailing" id="OzZ-Dz-RNF"/>
|
||||
<constraint firstItem="eFN-tN-4Ct" firstAttribute="top" secondItem="5Ns-4l-Ufg" secondAttribute="top" id="hUV-3a-XkY"/>
|
||||
<constraint firstItem="uoW-c8-9wx" firstAttribute="centerX" secondItem="5Ns-4l-Ufg" secondAttribute="centerX" id="wDv-OH-7PX"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="5Ns-4l-Ufg"/>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Item 1" id="HEV-kf-jxH"/>
|
||||
<tabBarItem key="tabBarItem" title="Layout 1" id="HEV-kf-jxH"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="bkL-bc-hZC" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
@@ -280,6 +297,7 @@
|
||||
<connections>
|
||||
<segue destination="lto-Zc-Vtp" kind="relationship" relationship="viewControllers" id="6hP-AH-YiH"/>
|
||||
<segue destination="RpE-lI-27a" kind="relationship" relationship="viewControllers" id="g6X-Sq-uSW"/>
|
||||
<segue destination="pOk-Zm-vD9" kind="relationship" relationship="viewControllers" id="OPp-iO-iDK"/>
|
||||
</connections>
|
||||
</tabBarController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Z9x-EI-p2b" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
@@ -654,6 +672,8 @@ Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
|
||||
<size key="freeformSize" width="375" height="778"/>
|
||||
<connections>
|
||||
<outlet property="textView" destination="rN1-HL-YHv" id="gmr-Uf-jd8"/>
|
||||
<outlet property="textViewTopConstraint" destination="fiO-LL-nSC" id="Rum-TN-c2e"/>
|
||||
<outlet property="view" destination="9YG-0j-Zzg" id="jhb-eT-nEn"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="x1h-y1-h8q" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
|
||||
@@ -384,6 +384,7 @@ class NestedScrollViewController: UIViewController {
|
||||
|
||||
class DebugTextViewController: UIViewController, UITextViewDelegate {
|
||||
@IBOutlet weak var textView: UITextView!
|
||||
@IBOutlet weak var textViewTopConstraint: NSLayoutConstraint!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
@@ -760,12 +761,60 @@ class TabBarContentViewController: UIViewController, FloatingPanelControllerDele
|
||||
case 0:
|
||||
return OneTabBarPanelLayout()
|
||||
case 1:
|
||||
return TwoTabBarPanel2Layout()
|
||||
return TwoTabBarPanelLayout()
|
||||
case 2:
|
||||
return ThreeTabBarPanelLayout()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func floatingPanelDidMove(_ vc: FloatingPanelController) {
|
||||
guard self.tabBarItem.tag == 2 else { return }
|
||||
|
||||
/* Solution 1: Manipulate scoll content inset */
|
||||
/*
|
||||
guard let scrollView = consoleVC.textView else { return }
|
||||
var insets = vc.adjustedContentInsets
|
||||
if vc.surfaceView.frame.minY < vc.layoutInsets.top {
|
||||
insets.top = vc.layoutInsets.top - vc.surfaceView.frame.minY
|
||||
} else {
|
||||
insets.top = 0.0
|
||||
}
|
||||
scrollView.contentInset = insets
|
||||
*/
|
||||
|
||||
// Solution 2: Manipulate top constraint
|
||||
assert(consoleVC.textViewTopConstraint != nil)
|
||||
if vc.surfaceView.frame.minY + 17.0 < vc.layoutInsets.top {
|
||||
consoleVC.textViewTopConstraint?.constant = vc.layoutInsets.top - vc.surfaceView.frame.minY
|
||||
} else {
|
||||
consoleVC.textViewTopConstraint?.constant = 17.0
|
||||
}
|
||||
consoleVC.view.layoutIfNeeded()
|
||||
}
|
||||
|
||||
func floatingPanelDidChangePosition(_ vc: FloatingPanelController) {
|
||||
guard self.tabBarItem.tag == 2 else { return }
|
||||
|
||||
/* Solution 1: Manipulate scoll content inset */
|
||||
/*
|
||||
guard let scrollView = consoleVC.textView else { return }
|
||||
var insets = vc.adjustedContentInsets
|
||||
insets.top = (vc.position == .full) ? vc.layoutInsets.top : 0.0
|
||||
scrollView.contentInset = insets
|
||||
if scrollView.contentOffset.y - scrollView.contentInset.top < 0.0 {
|
||||
scrollView.contentOffset = CGPoint(x: 0.0,
|
||||
y: 0.0 - scrollView.contentInset.top)
|
||||
}
|
||||
*/
|
||||
|
||||
// Solution 2: Manipulate top constraint
|
||||
assert(consoleVC.textViewTopConstraint != nil)
|
||||
consoleVC.textViewTopConstraint?.constant = (vc.position == .full) ? vc.layoutInsets.top : 17.0
|
||||
consoleVC.view.layoutIfNeeded()
|
||||
}
|
||||
|
||||
@IBAction func close(sender: UIButton) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
@@ -804,7 +853,7 @@ class OneTabBarPanelLayout: FloatingPanelLayout {
|
||||
}
|
||||
}
|
||||
|
||||
class TwoTabBarPanel2Layout: FloatingPanelLayout {
|
||||
class TwoTabBarPanelLayout: FloatingPanelLayout {
|
||||
var initialPosition: FloatingPanelPosition {
|
||||
return .half
|
||||
}
|
||||
@@ -824,6 +873,25 @@ class TwoTabBarPanel2Layout: FloatingPanelLayout {
|
||||
}
|
||||
}
|
||||
|
||||
class ThreeTabBarPanelLayout: FloatingPanelFullScreenLayout {
|
||||
var initialPosition: FloatingPanelPosition {
|
||||
return .half
|
||||
}
|
||||
var supportedPositions: Set<FloatingPanelPosition> {
|
||||
return [.full, .half]
|
||||
}
|
||||
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
|
||||
switch position {
|
||||
case .full: return 0.0
|
||||
case .half: return 261.0
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
func backdropAlphaFor(position: FloatingPanelPosition) -> CGFloat {
|
||||
return 0.3
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsViewController: InspectableViewController {
|
||||
@IBOutlet weak var largeTitlesSwicth: UISwitch!
|
||||
@IBOutlet weak var translucentSwicth: UISwitch!
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "FloatingPanel"
|
||||
s.version = "1.3.2"
|
||||
s.version = "1.3.3"
|
||||
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.
|
||||
|
||||
@@ -41,6 +41,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate
|
||||
private var animator: UIViewPropertyAnimator?
|
||||
private var initialFrame: CGRect = .zero
|
||||
private var initialScrollOffset: CGPoint = .zero
|
||||
private var initialScrollInset: UIEdgeInsets = .zero
|
||||
private var transOffsetY: CGFloat = 0
|
||||
|
||||
var interactionInProgress: Bool = false
|
||||
@@ -249,7 +250,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate
|
||||
return
|
||||
}
|
||||
// Fix the scroll offset in moving the panel from half and tip.
|
||||
scrollView.contentOffset.y = initialScrollOffset.y
|
||||
scrollView.contentOffset.y = initialScrollOffset.y + (initialScrollInset.top - scrollView.contentInset.top)
|
||||
case .hidden:
|
||||
fatalError("A floating panel hidden must not be used by a user")
|
||||
}
|
||||
@@ -437,6 +438,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate
|
||||
initialFrame = surfaceView.frame
|
||||
if let scrollView = scrollView {
|
||||
initialScrollOffset = scrollView.contentOffset
|
||||
initialScrollInset = scrollView.contentInset
|
||||
}
|
||||
transOffsetY = translation.y
|
||||
|
||||
|
||||
@@ -212,6 +212,11 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
|
||||
floatingPanel.behavior = fetchBehavior(for: newCollection)
|
||||
}
|
||||
|
||||
public override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
safeAreaInsetsObservation = nil
|
||||
}
|
||||
|
||||
// MARK:- Privates
|
||||
|
||||
private func fetchLayout(for traitCollection: UITraitCollection) -> FloatingPanelLayout {
|
||||
@@ -235,7 +240,7 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
|
||||
else { return }
|
||||
|
||||
log.debug("Update safeAreaInsets", safeAreaInsets)
|
||||
|
||||
|
||||
floatingPanel.layoutAdapter.safeAreaInsets = safeAreaInsets
|
||||
|
||||
setUpLayout()
|
||||
@@ -325,7 +330,6 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
|
||||
|
||||
parent.addChildViewController(self)
|
||||
|
||||
|
||||
view.frame = parent.view.bounds // Needed for a correct safe area configuration
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
@@ -376,6 +380,12 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
|
||||
vc.willMove(toParentViewController: nil)
|
||||
vc.view.removeFromSuperview()
|
||||
vc.removeFromParentViewController()
|
||||
|
||||
if let scrollView = floatingPanel.scrollView,
|
||||
let delegate = floatingPanel.userScrollViewDelegate,
|
||||
vc.view.subviews.contains(scrollView) {
|
||||
scrollView.delegate = delegate
|
||||
}
|
||||
}
|
||||
|
||||
if let vc = contentViewController {
|
||||
@@ -387,7 +397,7 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
|
||||
|
||||
_contentViewController = contentViewController
|
||||
}
|
||||
|
||||
|
||||
@available(*, unavailable, renamed: "set(contentViewController:)")
|
||||
public override func show(_ vc: UIViewController, sender: Any?) {
|
||||
if let target = self.parent?.targetViewController(forAction: #selector(UIViewController.show(_:sender:)), sender: sender) {
|
||||
|
||||
@@ -5,12 +5,21 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
/// FloatingPanelFullScreenLayout
|
||||
///
|
||||
/// Use the layout protocol if you want to configure a full inset from Superview.Top, not SafeArea.Top.
|
||||
/// It can't be used with FloatingPanelIntrinsicLayout.
|
||||
public protocol FloatingPanelFullScreenLayout: FloatingPanelLayout { }
|
||||
|
||||
/// FloatingPanelIntrinsicLayout
|
||||
///
|
||||
/// Use the layout protocol if you want to layout a panel using the intrinsic height.
|
||||
/// It can't be used with FloatingPanelFullScreenLayout.
|
||||
///
|
||||
/// - Attention:
|
||||
/// `insetFor(position:)` must return `nil` for full position because the inset is determined automatically.
|
||||
/// You can customize insets only for half, tip and hidden positions
|
||||
/// on FloatingPanelIntrinsicLayout.
|
||||
/// `insetFor(position:)` must return `nil` for the full position. Because
|
||||
/// the inset is determined automatically by the intrinsic height.
|
||||
/// You can customize insets only for the half, tip and hidden positions.
|
||||
public protocol FloatingPanelIntrinsicLayout: FloatingPanelLayout { }
|
||||
|
||||
public extension FloatingPanelIntrinsicLayout {
|
||||
@@ -34,7 +43,7 @@ public protocol FloatingPanelLayout: class {
|
||||
/// Returns a set of FloatingPanelPosition objects to tell the applicable
|
||||
/// positions of the floating panel controller.
|
||||
///
|
||||
/// By default, it returns all position exepct for `hidden` position. Because
|
||||
/// By default, it returns all position except for `hidden` position. Because
|
||||
/// it's always supported by `FloatingPanelController` so you don't need to return it.
|
||||
var supportedPositions: Set<FloatingPanelPosition> { get }
|
||||
|
||||
@@ -46,7 +55,7 @@ public protocol FloatingPanelLayout: class {
|
||||
|
||||
/// Returns a CGFloat value to determine a Y coordinate of a floating panel for each position(full, half, tip and hidden).
|
||||
///
|
||||
/// Its returning value indicates a different inset for each positiion.
|
||||
/// Its returning value indicates a different inset for each position.
|
||||
/// For full position, a top inset from a safe area in `FloatingPanelController.view`.
|
||||
/// For half or tip position, a bottom inset from the safe area.
|
||||
/// For hidden position, a bottom inset from `FloatingPanelController.view`.
|
||||
@@ -72,7 +81,7 @@ public extension FloatingPanelLayout {
|
||||
var supportedPositions: Set<FloatingPanelPosition> {
|
||||
return Set([.full, .half, .tip])
|
||||
}
|
||||
|
||||
|
||||
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
||||
return [
|
||||
surfaceView.leftAnchor.constraint(equalTo: view.sideLayoutGuide.leftAnchor, constant: 0.0),
|
||||
@@ -131,7 +140,6 @@ class FloatingPanelLayoutAdapter {
|
||||
|
||||
var safeAreaInsets: UIEdgeInsets = .zero
|
||||
|
||||
private var heightBuffer: CGFloat = 88.0 // For bounce
|
||||
private var fixedConstraints: [NSLayoutConstraint] = []
|
||||
private var fullConstraints: [NSLayoutConstraint] = []
|
||||
private var halfConstraints: [NSLayoutConstraint] = []
|
||||
@@ -164,9 +172,12 @@ class FloatingPanelLayoutAdapter {
|
||||
|
||||
var topY: CGFloat {
|
||||
if supportedPositions.contains(.full) {
|
||||
if layout is FloatingPanelIntrinsicLayout {
|
||||
switch layout {
|
||||
case is FloatingPanelIntrinsicLayout:
|
||||
return surfaceView.superview!.bounds.height - surfaceView.bounds.height
|
||||
} else {
|
||||
case is FloatingPanelFullScreenLayout:
|
||||
return fullInset
|
||||
default:
|
||||
return (safeAreaInsets.top + fullInset)
|
||||
}
|
||||
} else {
|
||||
@@ -194,7 +205,9 @@ class FloatingPanelLayoutAdapter {
|
||||
return surfaceView.superview!.bounds.height - (safeAreaInsets.bottom + hiddenInset)
|
||||
}
|
||||
|
||||
var topMaxY: CGFloat { return -safeAreaInsets.top }
|
||||
var topMaxY: CGFloat {
|
||||
return layout is FloatingPanelFullScreenLayout ? 0.0 : -safeAreaInsets.top
|
||||
}
|
||||
var bottomMaxY: CGFloat { return safeAreaBottomY }
|
||||
|
||||
var adjustedContentInsets: UIEdgeInsets {
|
||||
@@ -263,14 +276,22 @@ class FloatingPanelLayoutAdapter {
|
||||
fixedConstraints = surfaceConstraints + backdropConstraints
|
||||
|
||||
// Flexible surface constarints for full, half, tip and off
|
||||
if layout is FloatingPanelIntrinsicLayout {
|
||||
switch layout {
|
||||
case is FloatingPanelIntrinsicLayout:
|
||||
// Set up on updateHeight()
|
||||
} else {
|
||||
break
|
||||
case is FloatingPanelFullScreenLayout:
|
||||
fullConstraints = [
|
||||
surfaceView.topAnchor.constraint(equalTo: vc.view.topAnchor,
|
||||
constant: fullInset),
|
||||
]
|
||||
default:
|
||||
fullConstraints = [
|
||||
surfaceView.topAnchor.constraint(equalTo: vc.layoutGuide.topAnchor,
|
||||
constant: fullInset),
|
||||
]
|
||||
}
|
||||
|
||||
halfConstraints = [
|
||||
surfaceView.topAnchor.constraint(equalTo: vc.layoutGuide.bottomAnchor,
|
||||
constant: -halfInset),
|
||||
@@ -292,20 +313,27 @@ class FloatingPanelLayoutAdapter {
|
||||
|
||||
NSLayoutConstraint.deactivate(heightConstraints)
|
||||
|
||||
if layout is FloatingPanelIntrinsicLayout {
|
||||
switch layout {
|
||||
case is FloatingPanelIntrinsicLayout:
|
||||
updateIntrinsicHeight()
|
||||
heightConstraints = [
|
||||
surfaceView.heightAnchor.constraint(equalToConstant: intrinsicHeight + safeAreaInsets.bottom),
|
||||
]
|
||||
} else {
|
||||
case is FloatingPanelFullScreenLayout:
|
||||
heightConstraints = [
|
||||
surfaceView.heightAnchor.constraint(equalTo: vc.view.heightAnchor,
|
||||
constant: -fullInset),
|
||||
]
|
||||
default:
|
||||
heightConstraints = [
|
||||
surfaceView.heightAnchor.constraint(equalTo: vc.view.heightAnchor,
|
||||
constant: -(safeAreaInsets.top + fullInset)),
|
||||
]
|
||||
}
|
||||
|
||||
NSLayoutConstraint.activate(heightConstraints)
|
||||
|
||||
surfaceView.bottomOverflow = heightBuffer + layout.topInteractionBuffer
|
||||
surfaceView.bottomOverflow = vc.view.bounds.height + layout.topInteractionBuffer
|
||||
|
||||
if layout is FloatingPanelIntrinsicLayout {
|
||||
NSLayoutConstraint.deactivate(fullConstraints)
|
||||
|
||||
@@ -22,7 +22,11 @@ class FloatingPanelModalTransition: NSObject, UIViewControllerTransitioningDeleg
|
||||
}
|
||||
|
||||
class FloatingPanelPresentationController: UIPresentationController {
|
||||
override func presentationTransitionWillBegin() { }
|
||||
override func presentationTransitionWillBegin() {
|
||||
// Must call here even if duplicating on in containerViewWillLayoutSubviews()
|
||||
// Because it let the floating panel present correclty with the presentation animation
|
||||
addFloatingPanel()
|
||||
}
|
||||
|
||||
override func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
// For non-animated presentation
|
||||
@@ -39,32 +43,21 @@ class FloatingPanelPresentationController: UIPresentationController {
|
||||
}
|
||||
fpc.view.removeFromSuperview()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override func containerViewWillLayoutSubviews() {
|
||||
guard
|
||||
let containerView = self.containerView,
|
||||
let fpc = presentedViewController as? FloatingPanelController,
|
||||
let fpView = fpc.view
|
||||
let fpc = presentedViewController as? FloatingPanelController
|
||||
else { fatalError() }
|
||||
|
||||
/*
|
||||
* Layout the views managed by `FloatingPanelController` here for the
|
||||
* sake of the presentation and disimissal modally from the controller.
|
||||
*/
|
||||
containerView.addSubview(fpView)
|
||||
fpView.frame = containerView.bounds
|
||||
fpView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
fpView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0.0),
|
||||
fpView.leftAnchor.constraint(equalTo: containerView.leftAnchor, constant: 0.0),
|
||||
fpView.rightAnchor.constraint(equalTo: containerView.rightAnchor, constant: 0.0),
|
||||
fpView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0.0),
|
||||
])
|
||||
addFloatingPanel()
|
||||
|
||||
// Forward touch events to the presenting view controller
|
||||
(fpView as? FloatingPanelPassThroughView)?.eventForwardingView = presentingViewController.view
|
||||
(fpc.view as? FloatingPanelPassThroughView)?.eventForwardingView = presentingViewController.view
|
||||
|
||||
// Set tap-to-dimiss in the backdrop view
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleBackdrop(tapGesture:)))
|
||||
@@ -74,6 +67,17 @@ class FloatingPanelPresentationController: UIPresentationController {
|
||||
@objc func handleBackdrop(tapGesture: UITapGestureRecognizer) {
|
||||
presentedViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
private func addFloatingPanel() {
|
||||
guard
|
||||
let containerView = self.containerView,
|
||||
let fpc = presentedViewController as? FloatingPanelController
|
||||
else { fatalError() }
|
||||
|
||||
containerView.addSubview(fpc.view)
|
||||
fpc.view.frame = containerView.bounds
|
||||
fpc.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
}
|
||||
}
|
||||
|
||||
class FloatingPanelModalPresentTransition: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
@@ -91,9 +95,6 @@ class FloatingPanelModalPresentTransition: NSObject, UIViewControllerAnimatedTra
|
||||
let fpc = transitionContext.viewController(forKey: .to) as? FloatingPanelController
|
||||
else { fatalError() }
|
||||
|
||||
// Must set the container's bounds to the floating panel view
|
||||
fpc.view?.frame = transitionContext.containerView.bounds
|
||||
|
||||
fpc.show(animated: true) {
|
||||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user