Update Samples.app
* Add BottomEdgeInteractionLayout sample
* Support v2
* Use FloatingPanelSurfaceAppearance
* Fix 'Tab Bar > Layout 3' in Samples.app
* Remove interactionBuffer(for:) from Samples.app
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15705" 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="16096" 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="15706"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -553,7 +553,7 @@
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="22" translatesAutoresizingMaskIntoConstraints="NO" id="tP3-oJ-4EB">
|
||||
<rect key="frame" x="130.66666666666666" y="132" width="114" height="134"/>
|
||||
<rect key="frame" x="130.66666666666666" y="88" width="114" height="134"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="c5r-jU-haj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="114" height="30"/>
|
||||
@@ -589,7 +589,7 @@
|
||||
<constraint firstItem="8yw-OC-Ubk" firstAttribute="bottom" secondItem="g7l-kO-y7q" secondAttribute="bottom" id="JOL-wC-w74"/>
|
||||
<constraint firstItem="8yw-OC-Ubk" firstAttribute="leading" secondItem="aOK-7l-cA6" secondAttribute="leading" id="RiJ-Hb-OOZ"/>
|
||||
<constraint firstItem="8yw-OC-Ubk" firstAttribute="trailing" secondItem="aOK-7l-cA6" secondAttribute="trailing" id="Sof-yL-mwK"/>
|
||||
<constraint firstItem="tP3-oJ-4EB" firstAttribute="top" secondItem="aOK-7l-cA6" secondAttribute="top" constant="88" id="Zhb-Ss-epe"/>
|
||||
<constraint firstItem="tP3-oJ-4EB" firstAttribute="top" secondItem="g7l-kO-y7q" secondAttribute="top" constant="88" id="Zhb-Ss-epe"/>
|
||||
<constraint firstItem="Kva-Z7-0qY" firstAttribute="trailing" secondItem="aOK-7l-cA6" secondAttribute="trailing" id="kkp-Yo-FQW"/>
|
||||
<constraint firstItem="aOK-7l-cA6" firstAttribute="trailing" secondItem="noi-1a-5bZ" secondAttribute="trailing" constant="12" id="lv9-Nf-HNB"/>
|
||||
<constraint firstItem="qux-uG-4o2" firstAttribute="top" secondItem="aOK-7l-cA6" secondAttribute="top" constant="8" id="naa-cf-ZIc"/>
|
||||
|
||||
@@ -29,6 +29,7 @@ class SampleListViewController: UIViewController {
|
||||
case showContentInset
|
||||
case showContainerMargins
|
||||
case showNavigationController
|
||||
case showBottomEdgeInteraction
|
||||
|
||||
var name: String {
|
||||
switch self {
|
||||
@@ -48,6 +49,7 @@ 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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +71,7 @@ class SampleListViewController: UIViewController {
|
||||
case .showContentInset: return nil
|
||||
case .showContainerMargins: return nil
|
||||
case .showNavigationController: return "RootNavigationController"
|
||||
case .showBottomEdgeInteraction: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,8 +136,9 @@ class SampleListViewController: UIViewController {
|
||||
mainPanelVC.delegate = self
|
||||
mainPanelVC.contentInsetAdjustmentBehavior = .always
|
||||
|
||||
mainPanelVC.surfaceView.cornerRadius = 6.0
|
||||
mainPanelVC.surfaceView.shadowHidden = false
|
||||
let appearance = FloatingPanelSurfaceAppearance()
|
||||
appearance.cornerRadius = 6.0
|
||||
mainPanelVC.surfaceView.appearance = appearance
|
||||
|
||||
mainPanelVC.set(contentViewController: contentVC)
|
||||
|
||||
@@ -145,6 +149,8 @@ class SampleListViewController: UIViewController {
|
||||
tapGesture.cancelsTouchesInView = false
|
||||
tapGesture.numberOfTapsRequired = 2
|
||||
mainPanelVC.surfaceView.addGestureRecognizer(tapGesture)
|
||||
case .showNestedScrollView:
|
||||
mainPanelVC.panGestureRecognizer.delegateProxy = self
|
||||
case .showPageContentView:
|
||||
if let page = (mainPanelVC.contentViewController as? UIPageViewController)?.viewControllers?.first {
|
||||
mainPanelVC.track(scrollView: (page as! DebugTableViewController).tableView)
|
||||
@@ -156,6 +162,12 @@ class SampleListViewController: UIViewController {
|
||||
mainPanelVC.backdropView.addGestureRecognizer(backdropTapGesture)
|
||||
case .showNavigationController:
|
||||
mainPanelVC.contentInsetAdjustmentBehavior = .never
|
||||
case .showBottomEdgeInteraction: // For debug
|
||||
let contentVC = UIViewController()
|
||||
contentVC.view.backgroundColor = .red
|
||||
mainPanelVC.set(contentViewController: contentVC)
|
||||
mainPanelVC.addPanel(toParent: self, animated: true)
|
||||
return
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -185,16 +197,16 @@ class SampleListViewController: UIViewController {
|
||||
// Add FloatingPanel to self.view
|
||||
if let oldMainPanelVC = oldMainPanelVC {
|
||||
oldMainPanelVC.removePanelFromParent(animated: true, completion: {
|
||||
self.mainPanelVC.addPanel(toParent: self, belowView: nil, animated: true)
|
||||
self.mainPanelVC.addPanel(toParent: self, animated: true)
|
||||
})
|
||||
} else {
|
||||
mainPanelVC.addPanel(toParent: self, belowView: nil, animated: true)
|
||||
mainPanelVC.addPanel(toParent: self, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func handleSurface(tapGesture: UITapGestureRecognizer) {
|
||||
switch mainPanelVC.position {
|
||||
switch mainPanelVC.state {
|
||||
case .full:
|
||||
mainPanelVC.move(to: .half, animated: true)
|
||||
default:
|
||||
@@ -220,9 +232,10 @@ class SampleListViewController: UIViewController {
|
||||
// Initialize FloatingPanelController
|
||||
settingsPanelVC = FloatingPanelController()
|
||||
|
||||
// Initialize FloatingPanelController and add the view
|
||||
settingsPanelVC.surfaceView.cornerRadius = 6.0
|
||||
settingsPanelVC.surfaceView.shadowHidden = false
|
||||
let appearance = FloatingPanelSurfaceAppearance()
|
||||
appearance.cornerRadius = 6.0
|
||||
settingsPanelVC.surfaceView.appearance = appearance
|
||||
|
||||
settingsPanelVC.isRemovalInteractionEnabled = true
|
||||
|
||||
let backdropTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleBackdrop(tapGesture:)))
|
||||
@@ -236,7 +249,7 @@ class SampleListViewController: UIViewController {
|
||||
settingsPanelVC.set(contentViewController: contentVC)
|
||||
|
||||
// Add FloatingPanel to self.view
|
||||
settingsPanelVC.addPanel(toParent: self, belowView: nil, animated: true)
|
||||
settingsPanelVC.addPanel(toParent: self, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,9 +300,9 @@ extension SampleListViewController: UITableViewDelegate {
|
||||
detailPanelVC = FloatingPanelController()
|
||||
detailPanelVC.delegate = self
|
||||
|
||||
// Initialize FloatingPanelController and add the view
|
||||
detailPanelVC.surfaceView.cornerRadius = 6.0
|
||||
detailPanelVC.surfaceView.shadowHidden = false
|
||||
let appearance = FloatingPanelSurfaceAppearance()
|
||||
appearance.cornerRadius = 6.0
|
||||
detailPanelVC.surfaceView.appearance = appearance
|
||||
|
||||
// Set a content view controller
|
||||
detailPanelVC.set(contentViewController: contentVC)
|
||||
@@ -298,7 +311,7 @@ extension SampleListViewController: UITableViewDelegate {
|
||||
(contentVC as? DetailViewController)?.intrinsicHeightConstraint.priority = .defaultLow
|
||||
|
||||
// Add FloatingPanel to self.view
|
||||
detailPanelVC.addPanel(toParent: self, belowView: nil, animated: true)
|
||||
detailPanelVC.addPanel(toParent: self, animated: true)
|
||||
case .showModal, .showTabBar:
|
||||
let modalVC = contentVC
|
||||
modalVC.modalPresentationStyle = .fullScreen
|
||||
@@ -308,6 +321,7 @@ extension SampleListViewController: UITableViewDelegate {
|
||||
pages = [UIColor.blue, .red, .green].compactMap({ (color) -> UIViewController in
|
||||
let page = FloatingPanelController(delegate: self)
|
||||
page.view.backgroundColor = color
|
||||
page.panGestureRecognizer.delegateProxy = self
|
||||
page.show()
|
||||
return page
|
||||
})
|
||||
@@ -342,8 +356,9 @@ extension SampleListViewController: UITableViewDelegate {
|
||||
fpc.set(contentViewController: contentVC)
|
||||
fpc.delegate = self
|
||||
|
||||
fpc.surfaceView.cornerRadius = 38.5
|
||||
fpc.surfaceView.shadowHidden = false
|
||||
let appearance = FloatingPanelSurfaceAppearance()
|
||||
appearance.cornerRadius = 38.5
|
||||
fpc.surfaceView.appearance = appearance
|
||||
|
||||
fpc.isRemovalInteractionEnabled = true
|
||||
|
||||
@@ -359,8 +374,10 @@ extension SampleListViewController: UITableViewDelegate {
|
||||
fpc.set(contentViewController: contentVC)
|
||||
fpc.delegate = self
|
||||
|
||||
fpc.surfaceView.cornerRadius = 38.5
|
||||
fpc.surfaceView.shadowHidden = false
|
||||
let apprearance = FloatingPanelSurfaceAppearance()
|
||||
apprearance.cornerRadius = 38.5
|
||||
apprearance.shadows = []
|
||||
fpc.surfaceView.appearance = apprearance
|
||||
fpc.isRemovalInteractionEnabled = true
|
||||
|
||||
let mvc = UIViewController()
|
||||
@@ -373,7 +390,7 @@ extension SampleListViewController: UITableViewDelegate {
|
||||
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.set(contentViewController: contentViewController)
|
||||
fpc.surfaceView.contentInsets = .init(top: 20, left: 20, bottom: 20, right: 20)
|
||||
fpc.surfaceView.contentPadding = .init(top: 20, left: 20, bottom: 20, right: 20)
|
||||
|
||||
fpc.delegate = self
|
||||
fpc.isRemovalInteractionEnabled = true
|
||||
@@ -381,9 +398,13 @@ extension SampleListViewController: UITableViewDelegate {
|
||||
|
||||
case .showContainerMargins:
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.surfaceView.cornerRadius = 38.5
|
||||
|
||||
let appearance = FloatingPanelSurfaceAppearance()
|
||||
appearance.cornerRadius = 38.5
|
||||
fpc.surfaceView.appearance = appearance
|
||||
|
||||
fpc.surfaceView.backgroundColor = .red
|
||||
fpc.surfaceView.containerMargins = .init(top: 24.0, left: 8.0, bottom: layoutInsets.bottom, right: 8.0)
|
||||
fpc.surfaceView.containerMargins = .init(top: 24.0, left: 8.0, bottom: max(layoutInsets.bottom, 8.0), right: 8.0)
|
||||
#if swift(>=5.1) // Actually Xcode 11 or later
|
||||
if #available(iOS 13.0, *) {
|
||||
fpc.surfaceView.layer.cornerCurve = .continuous
|
||||
@@ -412,12 +433,14 @@ extension SampleListViewController: FloatingPanelControllerDelegate {
|
||||
return CGPoint(x: 0.0, y: 0.0 - trackingScrollView.contentInset.top)
|
||||
}
|
||||
|
||||
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? {
|
||||
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
|
||||
if vc == settingsPanelVC {
|
||||
return IntrinsicPanelLayout()
|
||||
}
|
||||
|
||||
switch currentMenu {
|
||||
case .showBottomEdgeInteraction:
|
||||
return BottomEdgeInteractionLayout()
|
||||
case .showRemovablePanel:
|
||||
return newCollection.verticalSizeClass == .compact ? RemovablePanelLandscapeLayout() : RemovablePanelLayout()
|
||||
case .showIntrinsicView:
|
||||
@@ -428,25 +451,13 @@ extension SampleListViewController: FloatingPanelControllerDelegate {
|
||||
}
|
||||
fallthrough
|
||||
case .showContentInset:
|
||||
return NoInteractionBufferPanelLayout()
|
||||
return FloatingPanelBottomLayout()
|
||||
default:
|
||||
return (newCollection.verticalSizeClass == .compact) ? nil : self
|
||||
return (newCollection.verticalSizeClass == .compact) ? FloatingPanelBottomLayout() : self
|
||||
}
|
||||
}
|
||||
|
||||
func floatingPanel(_ vc: FloatingPanelController, shouldRecognizeSimultaneouslyWith gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
switch currentMenu {
|
||||
case .showNestedScrollView:
|
||||
return (vc.contentViewController as? NestedScrollViewController)?.nestedScrollView.gestureRecognizers?.contains(gestureRecognizer) ?? false
|
||||
case .showPageView:
|
||||
// Tips: Need to allow recognizing the pan gesture of UIPageViewController simultaneously.
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func floatingPanelDidEndRemove(_ vc: FloatingPanelController) {
|
||||
func floatingPanelDidRemove(_ vc: FloatingPanelController) {
|
||||
switch vc {
|
||||
case settingsPanelVC:
|
||||
settingsPanelVC = nil
|
||||
@@ -456,23 +467,37 @@ extension SampleListViewController: FloatingPanelControllerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
extension SampleListViewController: UIGestureRecognizerDelegate {
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
switch currentMenu {
|
||||
case .showNestedScrollView:
|
||||
return true
|
||||
case .showPageView:
|
||||
// Tips: Need to allow recognizing the pan gesture of UIPageViewController simultaneously.
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
- Attention: `FloatingPanelLayout` must not be applied by the parent view
|
||||
controller of a floating panel. But here `SampleListViewController` adopts it
|
||||
purposely to check if the library prints an appropriate warning.
|
||||
*/
|
||||
extension SampleListViewController: FloatingPanelLayout {
|
||||
var initialPosition: FloatingPanelPosition {
|
||||
return .half
|
||||
}
|
||||
|
||||
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
|
||||
switch position {
|
||||
case .full: return UIScreen.main.bounds.height == 667.0 ? 18.0 : 16.0
|
||||
case .half: return 262.0
|
||||
case .tip: return 69.0
|
||||
case .hidden: return nil
|
||||
}
|
||||
var position: FloatingPanelPosition { .bottom }
|
||||
var initialState: FloatingPanelState { .half }
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: UIScreen.main.bounds.height == 667.0 ? 18.0 : 16.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 262.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,79 +526,71 @@ extension SampleListViewController: UIPageViewControllerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
class IntrinsicPanelLayout: FloatingPanelIntrinsicLayout { }
|
||||
class BottomEdgeInteractionLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .top
|
||||
let initialState: FloatingPanelState = .full
|
||||
|
||||
class NoInteractionBufferPanelLayout: FloatingPanelLayout {
|
||||
var initialPosition: FloatingPanelPosition {
|
||||
return .full
|
||||
}
|
||||
|
||||
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
|
||||
switch position {
|
||||
case .full: return 0
|
||||
case .half: return 216
|
||||
case .tip: return 60
|
||||
case .hidden: return nil
|
||||
}
|
||||
}
|
||||
|
||||
var topInteractionBuffer: CGFloat {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
var bottomInteractionBuffer: CGFloat {
|
||||
return 0.0
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 88.0, edge: .bottom, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .top, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
class RemovablePanelLayout: FloatingPanelIntrinsicLayout {
|
||||
var supportedPositions: Set<FloatingPanelPosition> {
|
||||
return [.full, .half]
|
||||
class IntrinsicPanelLayout: FloatingPanelBottomLayout {
|
||||
override var initialState: FloatingPanelState { .full }
|
||||
override var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
var initialPosition: FloatingPanelPosition {
|
||||
return .half
|
||||
}
|
||||
var topInteractionBuffer: CGFloat {
|
||||
return 200.0
|
||||
}
|
||||
var bottomInteractionBuffer: CGFloat {
|
||||
return 261.0 - 22.0
|
||||
}
|
||||
|
||||
class RemovablePanelLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .half
|
||||
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 130.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
|
||||
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
|
||||
switch position {
|
||||
case .half: return 130.0
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
func backdropAlphaFor(position: FloatingPanelPosition) -> CGFloat {
|
||||
func backdropAlphaFor(position: FloatingPanelState) -> CGFloat {
|
||||
return 0.3
|
||||
}
|
||||
}
|
||||
|
||||
class RemovablePanelLandscapeLayout: FloatingPanelIntrinsicLayout {
|
||||
var supportedPositions: Set<FloatingPanelPosition> {
|
||||
return [.full, .half]
|
||||
class RemovablePanelLandscapeLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .full
|
||||
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.0, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 216.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
var bottomInteractionBuffer: CGFloat {
|
||||
return 261.0 - 22.0
|
||||
}
|
||||
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
|
||||
switch position {
|
||||
case .half: return 261.0
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
func backdropAlphaFor(position: FloatingPanelPosition) -> CGFloat {
|
||||
|
||||
func backdropAlphaFor(position: FloatingPanelState) -> CGFloat {
|
||||
return 0.3
|
||||
}
|
||||
}
|
||||
|
||||
class ModalPanelLayout: FloatingPanelIntrinsicLayout {
|
||||
var topInteractionBuffer: CGFloat {
|
||||
return 100.0
|
||||
class ModalPanelLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .full
|
||||
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0.0, referenceGuide: .safeArea),
|
||||
]
|
||||
}
|
||||
func backdropAlphaFor(position: FloatingPanelPosition) -> CGFloat {
|
||||
|
||||
func backdropAlphaFor(position: FloatingPanelState) -> CGFloat {
|
||||
return 0.3
|
||||
}
|
||||
}
|
||||
@@ -887,9 +904,9 @@ class ModalViewController: UIViewController, FloatingPanelControllerDelegate {
|
||||
fpc = FloatingPanelController()
|
||||
fpc.delegate = self
|
||||
|
||||
// Initialize FloatingPanelController and add the view
|
||||
fpc.surfaceView.cornerRadius = 6.0
|
||||
fpc.surfaceView.shadowHidden = false
|
||||
let appearance = FloatingPanelSurfaceAppearance()
|
||||
appearance.cornerRadius = 6.0
|
||||
fpc.surfaceView.appearance = appearance
|
||||
|
||||
// Set a content view controller and track the scroll view
|
||||
let consoleVC = storyboard?.instantiateViewController(withIdentifier: "ConsoleViewController") as! DebugTextViewController
|
||||
@@ -899,7 +916,7 @@ class ModalViewController: UIViewController, FloatingPanelControllerDelegate {
|
||||
self.consoleVC = consoleVC
|
||||
|
||||
// Add FloatingPanel to self.view
|
||||
fpc.addPanel(toParent: self, belowView: safeAreaView)
|
||||
fpc.addPanel(toParent: self, at: view.subviews.firstIndex(of: safeAreaView) ?? -1)
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
@@ -927,27 +944,25 @@ class ModalViewController: UIViewController, FloatingPanelControllerDelegate {
|
||||
@IBAction func updateLayout(_ sender: Any) {
|
||||
isNewlayout = !isNewlayout
|
||||
UIView.animate(withDuration: 0.5) {
|
||||
self.fpc.updateLayout()
|
||||
self.fpc.layout = (self.isNewlayout) ? ModalSecondLayout() : FloatingPanelBottomLayout()
|
||||
self.fpc.invalidateLayout()
|
||||
}
|
||||
}
|
||||
|
||||
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? {
|
||||
return (isNewlayout) ? ModalSecondLayout() : nil
|
||||
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
|
||||
return (isNewlayout) ? ModalSecondLayout() : FloatingPanelBottomLayout()
|
||||
}
|
||||
}
|
||||
|
||||
class ModalSecondLayout: FloatingPanelLayout {
|
||||
var initialPosition: FloatingPanelPosition {
|
||||
return .half
|
||||
}
|
||||
|
||||
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
|
||||
switch position {
|
||||
case .full: return 18.0
|
||||
case .half: return 262.0
|
||||
case .tip: return 44.0
|
||||
case .hidden: return nil
|
||||
}
|
||||
var position: FloatingPanelPosition = .bottom
|
||||
var initialState: FloatingPanelState { .half }
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 262, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -973,8 +988,10 @@ class TabBarContentViewController: UIViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
fpc.delegate = self
|
||||
fpc.surfaceView.cornerRadius = 6.0
|
||||
fpc.surfaceView.shadowHidden = false
|
||||
|
||||
let appearance = FloatingPanelSurfaceAppearance()
|
||||
appearance.cornerRadius = 6.0
|
||||
fpc.surfaceView.appearance = appearance
|
||||
|
||||
// Set a content view controller and track the scroll view
|
||||
let consoleVC = storyboard?.instantiateViewController(withIdentifier: "ConsoleViewController") as! DebugTextViewController
|
||||
@@ -987,7 +1004,10 @@ class TabBarContentViewController: UIViewController {
|
||||
fpc.addPanel(toParent: self)
|
||||
|
||||
|
||||
if tabBarItem.tag == 2 {
|
||||
switch tabBarItem.tag {
|
||||
case 1:
|
||||
fpc.behavior = TwoTabBarPanelBehavior()
|
||||
case 2:
|
||||
let switcher = UISwitch()
|
||||
fpc.view.addSubview(switcher)
|
||||
switcher.translatesAutoresizingMaskIntoConstraints = false
|
||||
@@ -1014,12 +1034,14 @@ class TabBarContentViewController: UIViewController {
|
||||
|
||||
// Turn off the mask instead of content inset change
|
||||
consoleVC.textView.clipsToBounds = false
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
fpc.updateLayout()
|
||||
fpc.invalidateLayout()
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
@@ -1050,7 +1072,7 @@ extension TabBarContentViewController: UITextViewDelegate {
|
||||
// Using KVO of `scrollView.contentOffset`). Because it can lead to an
|
||||
// infinite loop if a user also resets a content offset as below and,
|
||||
// in the situation, a user has to modify the library.
|
||||
if fpc.position != .full, fpc.surfaceView.frame.minY > fpc.originYOfSurface(for: .full) {
|
||||
if fpc.state != .full, fpc.surfaceLocation.y > fpc.surfaceLocation(for: .full).y {
|
||||
scrollView.contentOffset = .zero
|
||||
}
|
||||
}
|
||||
@@ -1059,7 +1081,7 @@ extension TabBarContentViewController: UITextViewDelegate {
|
||||
extension TabBarContentViewController: FloatingPanelControllerDelegate {
|
||||
// MARK: - FloatingPanel
|
||||
|
||||
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? {
|
||||
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
|
||||
switch self.tabBarItem.tag {
|
||||
case 0:
|
||||
return OneTabBarPanelLayout()
|
||||
@@ -1069,28 +1091,20 @@ extension TabBarContentViewController: FloatingPanelControllerDelegate {
|
||||
threeLayout = ThreeTabBarPanelLayout(parent: self)
|
||||
return threeLayout
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func floatingPanel(_ vc: FloatingPanelController, behaviorFor newCollection: UITraitCollection) -> FloatingPanelBehavior? {
|
||||
switch self.tabBarItem.tag {
|
||||
case 1:
|
||||
return TwoTabBarPanelBehavior()
|
||||
default:
|
||||
return nil
|
||||
return FloatingPanelBottomLayout()
|
||||
}
|
||||
}
|
||||
|
||||
func floatingPanelDidMove(_ vc: FloatingPanelController) {
|
||||
guard self.tabBarItem.tag == 2 else { return }
|
||||
|
||||
switch tab3Mode {
|
||||
case .changeAutoLayout:
|
||||
/* Good solution: Manipulate top constraint */
|
||||
assert(consoleVC.textViewTopConstraint != nil)
|
||||
if vc.surfaceView.frame.minY + threeLayout.topPadding < vc.layoutInsets.top {
|
||||
consoleVC.textViewTopConstraint?.constant = vc.layoutInsets.top - vc.surfaceView.frame.minY
|
||||
let safeAreaTop = vc.layoutInsets.top
|
||||
if vc.surfaceLocation.y + threeLayout.topPadding < safeAreaTop {
|
||||
consoleVC.textViewTopConstraint?.constant = min(safeAreaTop - vc.surfaceLocation.y,
|
||||
safeAreaTop)
|
||||
} else {
|
||||
consoleVC.textViewTopConstraint?.constant = threeLayout.topPadding
|
||||
}
|
||||
@@ -1116,105 +1130,37 @@ extension TabBarContentViewController: FloatingPanelControllerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
if vc.surfaceView.frame.minY > vc.originYOfSurface(for: .half) {
|
||||
let progress = (vc.surfaceView.frame.minY - vc.originYOfSurface(for: .half)) / (vc.originYOfSurface(for: .tip) - vc.originYOfSurface(for: .half))
|
||||
if vc.surfaceLocation.y > vc.surfaceLocation(for: .half).y {
|
||||
let progress = (vc.surfaceLocation.y - vc.surfaceLocation(for: .half).y)
|
||||
/ (vc.surfaceLocation(for: .tip).y - vc.surfaceLocation(for: .half).y)
|
||||
threeLayout.leftConstraint.constant = max(min(progress, 1.0), 0.0) * threeLayout.sideMargin
|
||||
threeLayout.rightConstraint.constant = -max(min(progress, 1.0), 0.0) * threeLayout.sideMargin
|
||||
} else {
|
||||
threeLayout.leftConstraint.constant = 0.0
|
||||
threeLayout.rightConstraint.constant = 0.0
|
||||
}
|
||||
|
||||
vc.view.layoutIfNeeded() // MUST
|
||||
}
|
||||
|
||||
func floatingPanelDidChangePosition(_ vc: FloatingPanelController) {
|
||||
guard self.tabBarItem.tag == 2 else { return }
|
||||
|
||||
switch tab3Mode {
|
||||
case .changeAutoLayout:
|
||||
/* Good Solution: Manipulate top constraint */
|
||||
assert(consoleVC.textViewTopConstraint != nil)
|
||||
consoleVC.textViewTopConstraint?.constant = (vc.position == .full) ? vc.layoutInsets.top : 17.0
|
||||
|
||||
case .changeOffset:
|
||||
/* Bad Solution: Manipulate scroll 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)
|
||||
}
|
||||
}
|
||||
|
||||
if vc.position == .tip {
|
||||
threeLayout.leftConstraint.constant = threeLayout.sideMargin
|
||||
threeLayout.rightConstraint.constant = -threeLayout.sideMargin
|
||||
} else {
|
||||
threeLayout.leftConstraint.constant = 0.0
|
||||
threeLayout.rightConstraint.constant = 0.0
|
||||
}
|
||||
// Can call it, but it's not necessary because it will be also called
|
||||
// by FloatingPanelController after the delegate method
|
||||
vc.view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
extension FloatingPanelLayout {
|
||||
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
||||
if #available(iOS 11.0, *) {
|
||||
return [
|
||||
surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 0.0),
|
||||
surfaceView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: 0.0),
|
||||
]
|
||||
} else {
|
||||
return [
|
||||
surfaceView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0.0),
|
||||
surfaceView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0.0),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OneTabBarPanelLayout: FloatingPanelLayout {
|
||||
var initialPosition: FloatingPanelPosition {
|
||||
return .tip
|
||||
}
|
||||
var supportedPositions: Set<FloatingPanelPosition> {
|
||||
return [.full, .tip]
|
||||
}
|
||||
|
||||
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
|
||||
switch position {
|
||||
case .full: return 16.0
|
||||
case .tip: return 22.0
|
||||
default: return nil
|
||||
}
|
||||
var initialState: FloatingPanelState { .tip }
|
||||
var position: FloatingPanelPosition { .bottom }
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 22.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
class TwoTabBarPanelLayout: FloatingPanelLayout {
|
||||
var initialPosition: FloatingPanelPosition {
|
||||
return .half
|
||||
}
|
||||
var supportedPositions: Set<FloatingPanelPosition> {
|
||||
return [.full, .half]
|
||||
}
|
||||
var topInteractionBuffer: CGFloat {
|
||||
return 100.0
|
||||
}
|
||||
var bottomInteractionBuffer: CGFloat {
|
||||
return 261.0 - 22.0
|
||||
}
|
||||
|
||||
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
|
||||
switch position {
|
||||
case .full: return 100.0
|
||||
case .half: return 261.0
|
||||
default: return nil
|
||||
}
|
||||
var initialState: FloatingPanelState { .half }
|
||||
var position: FloatingPanelPosition { .bottom }
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 100.0, edge: .top, referenceGuide: .safeArea),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 261.0, edge: .bottom, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1225,7 +1171,7 @@ class TwoTabBarPanelBehavior: FloatingPanelBehavior {
|
||||
}
|
||||
|
||||
|
||||
class ThreeTabBarPanelLayout: FloatingPanelFullScreenLayout {
|
||||
class ThreeTabBarPanelLayout: FloatingPanelLayout {
|
||||
weak var parentVC: UIViewController!
|
||||
|
||||
var leftConstraint: NSLayoutConstraint!
|
||||
@@ -1238,23 +1184,17 @@ class ThreeTabBarPanelLayout: FloatingPanelFullScreenLayout {
|
||||
parentVC = parent
|
||||
}
|
||||
|
||||
var bottomInteractionBuffer: CGFloat = 44.0
|
||||
var initialState: FloatingPanelState { .half }
|
||||
var position: FloatingPanelPosition { .bottom }
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 0.0, edge: .top, referenceGuide: .superview),
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 261.0 + parentVC.layoutInsets.bottom, edge: .bottom, referenceGuide: .superview),
|
||||
.tip: FloatingPanelLayoutAnchor(absoluteInset: 88.0 + parentVC.layoutInsets.bottom, edge: .bottom, referenceGuide: .superview),
|
||||
]
|
||||
}
|
||||
|
||||
var initialPosition: FloatingPanelPosition {
|
||||
return .half
|
||||
}
|
||||
var supportedPositions: Set<FloatingPanelPosition> {
|
||||
return [.full, .half, .tip]
|
||||
}
|
||||
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
|
||||
switch position {
|
||||
case .full: return 0.0
|
||||
case .half: return 261.0 + parentVC.layoutInsets.bottom
|
||||
case .tip: return 88.0 + parentVC.layoutInsets.bottom
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
func backdropAlphaFor(position: FloatingPanelPosition) -> CGFloat {
|
||||
func backdropAlphaFor(position: FloatingPanelState) -> CGFloat {
|
||||
return 0.3
|
||||
}
|
||||
func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
|
||||
@@ -1345,7 +1285,7 @@ final class MultiPanelController: FloatingPanelController, FloatingPanelControll
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
delegate = self
|
||||
layout = FirstViewLayout()
|
||||
isRemovalInteractionEnabled = true
|
||||
|
||||
let vc = FirstPanelContentViewController()
|
||||
@@ -1354,17 +1294,12 @@ final class MultiPanelController: FloatingPanelController, FloatingPanelControll
|
||||
}
|
||||
|
||||
private final class FirstViewLayout: FloatingPanelLayout {
|
||||
let initialPosition: FloatingPanelPosition = .full
|
||||
let supportedPositions: Set<FloatingPanelPosition> = [.full]
|
||||
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
|
||||
switch position {
|
||||
case .full: return 40.0
|
||||
default: return nil
|
||||
}
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .full
|
||||
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
|
||||
return [
|
||||
.full: FloatingPanelLayoutAnchor(absoluteInset: 40.0, edge: .top, referenceGuide: .superview)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? {
|
||||
return FirstViewLayout()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user