Skip to content

Commit 0cf0f42

Browse files
committed
Replace FloatingPanelController.show(_:sender:) with the set(contentViewController:)
And add a sample code to test show(_:sender:) in ContentVC. `FloatingPanelController.show(_:sender:)` can block 'Show' segue in a content view controller. 'Show' segue should not be handled by 'FloatingPanelController`. So I replace this method.
1 parent c9ccea3 commit 0cf0f42

File tree

6 files changed

+142
-39
lines changed

6 files changed

+142
-39
lines changed

Examples/Maps/Maps/ViewController.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ class ViewController: UIViewController, MKMapViewDelegate, UISearchBarDelegate,
2626

2727
searchVC = storyboard?.instantiateViewController(withIdentifier: "SearchPanel") as? SearchPanelViewController
2828

29-
// Add a content view controller
30-
fpc.show(searchVC, sender: self)
29+
// Set a content view controller
30+
fpc.set(contentViewController: searchVC)
3131
fpc.track(scrollView: searchVC.tableView)
3232

3333
setupMapView()

Examples/Samples/Sources/Base.lproj/Main.storyboard

+30-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="RoN-h0-uBD">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="RoN-h0-uBD">
33
<device id="retina4_7" orientation="portrait">
44
<adaptation id="fullscreen"/>
55
</device>
66
<dependencies>
77
<deployment identifier="iOS"/>
8-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
8+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
99
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
1010
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
1111
</dependencies>
@@ -356,7 +356,7 @@
356356
</connections>
357357
</swipeGestureRecognizer>
358358
</objects>
359-
<point key="canvasLocation" x="1239" y="806"/>
359+
<point key="canvasLocation" x="1311" y="806"/>
360360
</scene>
361361
<!--Detail View Controller-->
362362
<scene sceneID="b6k-zi-3wn">
@@ -390,14 +390,36 @@
390390
<constraint firstAttribute="height" constant="44" id="DQJ-cY-cKx"/>
391391
</constraints>
392392
</view>
393+
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="22" translatesAutoresizingMaskIntoConstraints="NO" id="tP3-oJ-4EB">
394+
<rect key="frame" x="130.5" y="108" width="114" height="82"/>
395+
<subviews>
396+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="c5r-jU-haj">
397+
<rect key="frame" x="0.0" y="0.0" width="114" height="30"/>
398+
<state key="normal" title="Show"/>
399+
<connections>
400+
<action selector="buttonPressed:" destination="YC8-ae-15L" eventType="touchUpInside" id="Mi1-o6-TWt"/>
401+
</connections>
402+
</button>
403+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wmd-ab-Nz3">
404+
<rect key="frame" x="0.0" y="52" width="114" height="30"/>
405+
<state key="normal" title="Present Modallly"/>
406+
<connections>
407+
<action selector="buttonPressed:" destination="YC8-ae-15L" eventType="touchUpInside" id="tjH-Ev-kpx"/>
408+
<segue destination="bYI-y3-Rzb" kind="presentation" identifier="PresentModallySegue" id="3yq-HE-Tgn"/>
409+
</connections>
410+
</button>
411+
</subviews>
412+
</stackView>
393413
</subviews>
394414
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
395415
<gestureRecognizers/>
396416
<constraints>
397417
<constraint firstItem="noi-1a-5bZ" firstAttribute="top" secondItem="g7l-kO-y7q" secondAttribute="top" constant="12" id="EQy-cr-F2Y"/>
418+
<constraint firstItem="tP3-oJ-4EB" firstAttribute="centerX" secondItem="g7l-kO-y7q" secondAttribute="centerX" id="EsD-Vf-dNZ"/>
398419
<constraint firstItem="8yw-OC-Ubk" firstAttribute="bottom" secondItem="g7l-kO-y7q" secondAttribute="bottom" id="JOL-wC-w74"/>
399420
<constraint firstItem="8yw-OC-Ubk" firstAttribute="leading" secondItem="tAi-nk-rDB" secondAttribute="leading" id="RiJ-Hb-OOZ"/>
400421
<constraint firstItem="8yw-OC-Ubk" firstAttribute="trailing" secondItem="tAi-nk-rDB" secondAttribute="trailing" id="Sof-yL-mwK"/>
422+
<constraint firstItem="tP3-oJ-4EB" firstAttribute="top" secondItem="tAi-nk-rDB" secondAttribute="top" constant="88" id="Zhb-Ss-epe"/>
401423
<constraint firstItem="Kva-Z7-0qY" firstAttribute="trailing" secondItem="tAi-nk-rDB" secondAttribute="trailing" id="kkp-Yo-FQW"/>
402424
<constraint firstItem="tAi-nk-rDB" firstAttribute="trailing" secondItem="noi-1a-5bZ" secondAttribute="trailing" constant="12" id="lv9-Nf-HNB"/>
403425
<constraint firstItem="Kva-Z7-0qY" firstAttribute="leading" secondItem="tAi-nk-rDB" secondAttribute="leading" id="oVC-i1-TwS"/>
@@ -413,6 +435,7 @@
413435
<size key="freeformSize" width="375" height="778"/>
414436
<connections>
415437
<outlet property="closeButton" destination="noi-1a-5bZ" id="eWQ-ha-8y7"/>
438+
<segue destination="bYI-y3-Rzb" kind="show" identifier="ShowSegue" id="r1P-2i-NDe"/>
416439
</connections>
417440
</viewController>
418441
<placeholder placeholderIdentifier="IBFirstResponder" id="Wqk-xl-O3I" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -432,7 +455,7 @@
432455
</connections>
433456
</pongPressGestureRecognizer>
434457
</objects>
435-
<point key="canvasLocation" x="1442" y="-23"/>
458+
<point key="canvasLocation" x="1440.8" y="-23.388305847076463"/>
436459
</scene>
437460
<!--Debug Text View Controller-->
438461
<scene sceneID="Bkq-O7-q4A">
@@ -507,4 +530,7 @@ Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
507530
<point key="canvasLocation" x="729" y="-23"/>
508531
</scene>
509532
</scenes>
533+
<inferredMetricsTieBreakers>
534+
<segue reference="3yq-HE-Tgn"/>
535+
</inferredMetricsTieBreakers>
510536
</document>

Examples/Samples/Sources/ViewController.swift

+24-12
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@ class SampleListViewController: UIViewController, UITableViewDataSource, UITable
7070
mainPanelVC.surfaceView.cornerRadius = 6.0
7171
mainPanelVC.surfaceView.shadowHidden = false
7272

73-
// Add a content view controller and connect with the scroll view
74-
mainPanelVC.show(contentVC, sender: self)
73+
// Set a content view controller
74+
mainPanelVC.set(contentViewController: contentVC)
7575

76+
// Track a scroll view
7677
switch contentVC {
7778
case let consoleVC as DebugTextViewController:
7879
mainPanelVC.track(scrollView: consoleVC.textView)
@@ -128,10 +129,8 @@ class SampleListViewController: UIViewController, UITableViewDataSource, UITable
128129
detailPanelVC.surfaceView.cornerRadius = 6.0
129130
detailPanelVC.surfaceView.shadowHidden = false
130131

131-
// Add a content view controller and connect with the scroll view
132-
detailPanelVC.show(contentVC, sender: self)
133-
134-
// (contentVC as? DetailViewController)?.closeButton?.addTarget(self, action: #selector(dismissDetailPanelVC), for: .touchUpInside)
132+
// Set a content view controller
133+
detailPanelVC.set(contentViewController: contentVC)
135134

136135
// Add FloatingPanel to self.view
137136
detailPanelVC.addPanel(toParent: self, belowView: nil, animated: true)
@@ -299,6 +298,18 @@ class DetailViewController: UIViewController {
299298
// dismiss(animated: true, completion: nil)
300299
(self.parent as? FloatingPanelController)?.removePanelFromParent(animated: true, completion: nil)
301300
}
301+
302+
@IBAction func buttonPressed(_ sender: UIButton) {
303+
switch sender.titleLabel?.text {
304+
case "Show":
305+
performSegue(withIdentifier: "ShowSegue", sender: self)
306+
case "Present Modally":
307+
performSegue(withIdentifier: "PresentModallySegue", sender: self)
308+
default:
309+
break
310+
}
311+
}
312+
302313
@IBAction func tapped(_ sender: Any) {
303314
print("Detail panel is tapped!")
304315
}
@@ -323,12 +334,13 @@ class ModalViewController: UIViewController {
323334
fpc.surfaceView.cornerRadius = 6.0
324335
fpc.surfaceView.shadowHidden = false
325336

326-
// Add a content view controller and connect with the scroll view
337+
// Set a content view controller and track the scroll view
327338
let consoleVC = storyboard?.instantiateViewController(withIdentifier: "ConsoleViewController") as! DebugTextViewController
328-
fpc.show(consoleVC, sender: self)
329-
self.consoleVC = consoleVC
339+
fpc.set(contentViewController: consoleVC)
330340
fpc.track(scrollView: consoleVC.textView)
331341

342+
self.consoleVC = consoleVC
343+
332344
// Add FloatingPanel to self.view
333345
fpc.addPanel(toParent: self, belowView: safeAreaView)
334346
}
@@ -370,11 +382,11 @@ class TabBarContentViewController: UIViewController, FloatingPanelControllerDele
370382
fpc.surfaceView.cornerRadius = 6.0
371383
fpc.surfaceView.shadowHidden = false
372384

373-
// Add a content view controller and connect with the scroll view
385+
// Set a content view controller and track the scroll view
374386
let consoleVC = storyboard?.instantiateViewController(withIdentifier: "ConsoleViewController") as! DebugTextViewController
375-
fpc.show(consoleVC, sender: self)
376-
self.consoleVC = consoleVC
387+
fpc.set(contentViewController: consoleVC)
377388
fpc.track(scrollView: consoleVC.textView)
389+
self.consoleVC = consoleVC
378390

379391
// Add FloatingPanel to self.view
380392
fpc.addPanel(toParent: self)

Examples/Stocks/Stocks/ViewController.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
3434

3535
newsVC = storyboard?.instantiateViewController(withIdentifier: "News") as? NewsViewController
3636

37-
// Add a content view controller
38-
fpc.show(newsVC, sender: self)
37+
// Set a content view controller
38+
fpc.set(contentViewController: newsVC)
3939
fpc.track(scrollView: newsVC.scrollView)
4040

4141
fpc.addPanel(toParent: self, belowView: bottomToolView, animated: false)

Framework/Sources/FloatingPanelController.swift

+44-13
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
9797
get { return floatingPanel.isRemovalInteractionEnabled }
9898
}
9999

100+
/// The view controller responsible for the content portion of the floating panel.
101+
public var contentViewController: UIViewController? {
102+
set { set(contentViewController: newValue) }
103+
get { return _contentViewController }
104+
}
105+
private var _contentViewController: UIViewController?
106+
100107
private var floatingPanel: FloatingPanel!
101108

102109
required init?(coder aDecoder: NSCoder) {
@@ -252,20 +259,44 @@ public class FloatingPanelController: UIViewController, UIScrollViewDelegate, UI
252259
floatingPanel.move(to: to, animated: animated, completion: completion)
253260
}
254261

255-
/// Presents the specified view controller as the content view controller in the surface view interface.
262+
/// Sets the view controller responsible for the content portion of the floating panel..
263+
public func set(contentViewController: UIViewController?) {
264+
if let vc = _contentViewController {
265+
vc.willMove(toParent: nil)
266+
vc.view.removeFromSuperview()
267+
vc.removeFromParent()
268+
}
269+
270+
if let vc = contentViewController {
271+
let surfaceView = self.view as! FloatingPanelSurfaceView
272+
surfaceView.contentView.addSubview(vc.view)
273+
vc.view.frame = surfaceView.contentView.bounds
274+
vc.view.translatesAutoresizingMaskIntoConstraints = false
275+
NSLayoutConstraint.activate([
276+
vc.view.topAnchor.constraint(equalTo: surfaceView.contentView.topAnchor, constant: 0.0),
277+
vc.view.leftAnchor.constraint(equalTo: surfaceView.contentView.leftAnchor, constant: 0.0),
278+
vc.view.rightAnchor.constraint(equalTo: surfaceView.contentView.rightAnchor, constant: 0.0),
279+
vc.view.bottomAnchor.constraint(equalTo: surfaceView.contentView.bottomAnchor, constant: 0.0),
280+
])
281+
addChild(vc)
282+
vc.didMove(toParent: self)
283+
}
284+
285+
_contentViewController = contentViewController
286+
}
287+
288+
@available(*, unavailable, renamed: "set(contentViewController:)")
256289
public override func show(_ vc: UIViewController, sender: Any?) {
257-
let surfaceView = self.view as! FloatingPanelSurfaceView
258-
surfaceView.contentView.addSubview(vc.view)
259-
vc.view.frame = surfaceView.contentView.bounds
260-
vc.view.translatesAutoresizingMaskIntoConstraints = false
261-
NSLayoutConstraint.activate([
262-
vc.view.topAnchor.constraint(equalTo: surfaceView.contentView.topAnchor, constant: 0.0),
263-
vc.view.leftAnchor.constraint(equalTo: surfaceView.contentView.leftAnchor, constant: 0.0),
264-
vc.view.rightAnchor.constraint(equalTo: surfaceView.contentView.rightAnchor, constant: 0.0),
265-
vc.view.bottomAnchor.constraint(equalTo: surfaceView.contentView.bottomAnchor, constant: 0.0),
266-
])
267-
addChild(vc)
268-
vc.didMove(toParent: self)
290+
if let target = self.parent?.targetViewController(forAction: #selector(UIViewController.show(_:sender:)), sender: sender) {
291+
target.show(vc, sender: sender)
292+
}
293+
}
294+
295+
@available(*, unavailable, renamed: "set(contentViewController:)")
296+
public override func showDetailViewController(_ vc: UIViewController, sender: Any?) {
297+
if let target = self.parent?.targetViewController(forAction: #selector(UIViewController.showDetailViewController(_:sender:)), sender: sender) {
298+
target.showDetailViewController(vc, sender: sender)
299+
}
269300
}
270301

271302
// MARK: - Scroll view tracking

README.md

+40-6
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ The new interface displays the related contents and utilities in parallel as a u
2525
- [Getting Started](#getting-started)
2626
- [Usage](#usage)
2727
- [Customize the layout of a floating panel with `FloatingPanelLayout` protocol](#customize-the-layout-of-a-floating-panel-with--floatingpanellayout-protocol)
28-
- [Change the initial position, supported positions and height](#change-the-initial-position-supported-positions-and-height)
28+
- [Change the initial position and height](#change-the-initial-position-and-height)
2929
- [Support your landscape layout](#support-your-landscape-layout)
3030
- [Customize the behavior with `FloatingPanelBehavior` protocol](#customize-the-behavior-with-floatingpanelbehavior-protocol)
3131
- [Modify your floating panel's interaction](#modify-your-floating-panels-interaction)
3232
- [Create an additional floating panel for a detail](#create-an-additional-floating-panel-for-a-detail)
3333
- [Move a position with an animation](#move-a-position-with-an-animation)
3434
- [Make your contents correspond with a floating panel behavior](#make-your-contents-correspond-with-a-floating-panel-behavior)
3535
- [Notes](#notes)
36+
- ['Show' or 'Show Detail' Segues from `FloatingPanelController`'s content view controller](#show-or-show-detail-segues-from-floatingpanelcontrollers-content-view-controller)
3637
- [FloatingPanelSurfaceView's issue on iOS 10](#floatingpanelsurfaceviews-issue-on-ios-10)
3738
- [Author](#author)
3839
- [License](#license)
@@ -96,14 +97,14 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
9697
// Assign self as the delegate of the controller.
9798
fpc.delegate = self // Optional
9899

99-
// Add a content view controller.
100+
// Set a content view controller.
100101
let contentVC = ContentViewController()
101-
fpc.show(contentVC, sender: nil)
102+
fpc.set(viewController: contentVC)
102103

103104
// Track a scroll view(or the siblings) in the content view controller.
104105
fpc.track(scrollView: contentVC.tableView)
105106

106-
// Add the views managed by the `FloatingPanelController` object to self.view.
107+
// Add and show the views managed by the `FloatingPanelController` object to self.view.
107108
fpc.addPanel(toParent: self)
108109
}
109110

@@ -222,7 +223,7 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
222223
self.searchPanelVC = FloatingPanelController()
223224

224225
let searchVC = SearchViewController()
225-
self.searchPanelVC.show(searchVC, sender: nil)
226+
self.searchPanelVC.set(viewController: searchVC)
226227
self.searchPanelVC.track(scrollView: contentVC.tableView)
227228

228229
self.searchPanelVC.addPanel(toParent: self)
@@ -231,7 +232,7 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
231232
self.detailPanelVC = FloatingPanelController()
232233

233234
let contentVC = ContentViewController()
234-
self.detailPanelVC.show(contentVC, sender: nil)
235+
self.searchPanelVC.set(viewController: contentVC)
235236
self.detailPanelVC.track(scrollView: contentVC.scrollView)
236237

237238
self.detailPanelVC.addPanel(toParent: self)
@@ -279,6 +280,39 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
279280

280281
## Notes
281282

283+
### 'Show' or 'Show Detail' Segues from `FloatingPanelController`'s content view controller
284+
285+
'Show' or 'Show Detail' segues from a content view controller will be managed by a view controller(hereinafter called 'master VC') adding a floating panel. Because a floating panel is just a subview of the master VC.
286+
287+
`FloatingPanelController` has no way to manage a stack of view controllers like `UINavigationController`. If so, it would be so complicated and the interface will become `UINavigationController`. This component should not have the responsibility to manage the stack.
288+
289+
By the way, a content view controller can present a view controller modally with `present(_:animated:completion:)` or 'Present Modally' segue.
290+
291+
However, sometimes you want to show a destination view controller of 'Show' or 'Show Detail' segue with another floating panel. It's possible to override `show(_:sender)` of the master VC!
292+
293+
Here is an example.
294+
295+
```swift
296+
class ViewController: UIViewController {
297+
var fpc: FloatingPanelController!
298+
var secondFpc: FloatingPanelController!
299+
300+
...
301+
override func show(_ vc: UIViewController, sender: Any?) {
302+
secondFpc = FloatingPanelController()
303+
304+
secondFpc.set(contentViewController: vc)
305+
306+
secondFpc.addPanel(toParent: self)
307+
}
308+
...
309+
}
310+
```
311+
312+
A `FloatingPanelController` object proxies an action for `show(_:sender)` to the master VC. That's why the master VC can handle a destination view controller of a 'Show' or 'Show Detail' segue and you can hook `show(_:sender)` to show a secondally floating panel set the destination view controller to the content.
313+
314+
It's a greate way to decouple between a floating panel and the content VC.
315+
282316
### FloatingPanelSurfaceView's issue on iOS 10
283317

284318
* On iOS 10, `FloatingPanelSurfaceView.cornerRadius` isn't not automatically masked with the top rounded corners because of UIVisualEffectView issue. See https://forums.developer.apple.com/thread/50854.

0 commit comments

Comments
 (0)