Compare commits

...

8 Commits

Author SHA1 Message Date
Shin Yamamoto 5325e707e6 Release v1.6.5 2019-08-31 12:51:01 +09:00
Nikolay Derkach 1dc0a6b76a Support bottom content inset for container view (#257)
and also fix height of a content view resized by the inset
Fix #256
2019-08-31 12:49:59 +09:00
David Hart 2689d68bab Improve floatingPanelDidChangePosition and tigger it on removal 2019-08-31 12:49:57 +09:00
Shin Yamamoto 218a12962f Don't unregister safeAreaInsetsObservation in hide()
Because the observation can't be active when a user makes a panel visible
by move(to:) instead of show().
2019-08-24 15:37:15 +09:00
Shin Yamamoto 916d2ec76a Add move-to-hidden tests 2019-08-24 15:37:15 +09:00
Shin Yamamoto 1b1ba5deef Update README for UISearchController issue 2019-08-24 15:37:15 +09:00
Shin Yamamoto 58b2df4996 Fix UISearchBar's _searchField access 2019-08-24 09:54:54 +09:00
Shin Yamamoto 3b812be84e Return true for FloatingPanelSurfaceView.requiresConstraintBasedLayout 2019-08-13 15:27:27 +09:00
12 changed files with 91 additions and 19 deletions
+13 -3
View File
@@ -148,11 +148,9 @@ class SearchPanelViewController: UIViewController, UITableViewDataSource, UITabl
tableView.dataSource = self
tableView.delegate = self
searchBar.placeholder = "Search for a place or address"
let textField = searchBar.value(forKey: "_searchField") as! UITextField
textField.font = UIFont(name: textField.font!.fontName, size: 15.0)
searchBar.setSearchText(fontSize: 15.0)
hideHeader()
}
override func viewDidLayoutSubviews() {
@@ -274,3 +272,15 @@ class SearchHeaderView: UIView {
self.clipsToBounds = true
}
}
extension UISearchBar {
func setSearchText(fontSize: CGFloat) {
#if swift(>=5.1) // Xcode 11 or later
let font = searchTextField.font
searchTextField.font = font?.withSize(fontSize)
#else
let textField = value(forKey: "_searchField") as! UITextField
textField.font = textField.font?.withSize(fontSize)
#endif
}
}
@@ -324,7 +324,7 @@
</connections>
</button>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="44" translatesAutoresizingMaskIntoConstraints="NO" id="9p4-06-y2T">
<rect key="frame" x="139.66666666666666" y="132" width="96" height="252"/>
<rect key="frame" x="134.66666666666666" y="132" width="106" height="326"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="i9x-x5-n1q">
<rect key="frame" x="0.0" y="0.0" width="80" height="30"/>
@@ -347,8 +347,15 @@
<action selector="moveToTipWithSender:" destination="bYI-y3-Rzb" eventType="touchUpInside" id="BmL-91-9ai"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="swr-XM-GzZ">
<rect key="frame" x="0.0" y="222" width="106" height="30"/>
<state key="normal" title="Move to hidden"/>
<connections>
<action selector="moveToHiddenWithSender:" destination="bYI-y3-Rzb" eventType="touchUpInside" id="jfJ-0f-fdk"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="szf-HE-QTk">
<rect key="frame" x="0.0" y="222" width="96" height="30"/>
<rect key="frame" x="0.0" y="296" width="96" height="30"/>
<state key="normal" title="Update layout"/>
<connections>
<action selector="updateLayout:" destination="bYI-y3-Rzb" eventType="touchUpInside" id="Woz-a7-YMJ"/>
+31 -4
View File
@@ -306,8 +306,8 @@ extension SampleListViewController: UITableViewDelegate {
let fpc = FloatingPanelController()
fpc.set(contentViewController: contentViewController)
fpc.surfaceView.contentInsets = .init(top: 20, left: 20, bottom: 0, right: 20)
fpc.surfaceView.contentInsets = .init(top: 20, left: 20, bottom: 20, right: 20)
fpc.delegate = self
fpc.isRemovalInteractionEnabled = true
self.present(fpc, animated: true, completion: nil)
@@ -340,8 +340,10 @@ extension SampleListViewController: FloatingPanelControllerDelegate {
return ModalPanelLayout()
}
fallthrough
case .showContentInset:
return NoInteractionBufferPanelLayout()
default:
return (newCollection.verticalSizeClass == .compact) ? nil : self
return (newCollection.verticalSizeClass == .compact) ? nil : self
}
}
@@ -406,6 +408,29 @@ extension SampleListViewController: UIPageViewControllerDataSource {
class IntrinsicPanelLayout: FloatingPanelIntrinsicLayout { }
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
}
}
class RemovablePanelLayout: FloatingPanelIntrinsicLayout {
var supportedPositions: Set<FloatingPanelPosition> {
return [.full, .half]
@@ -799,7 +824,9 @@ class ModalViewController: UIViewController, FloatingPanelControllerDelegate {
@IBAction func moveToTip(sender: UIButton) {
fpc.move(to: .tip, animated: true)
}
@IBAction func moveToHidden(sender: UIButton) {
fpc.move(to: .hidden, animated: true)
}
@IBAction func updateLayout(_ sender: Any) {
isNewlayout = !isNewlayout
UIView.animate(withDuration: 0.5) {
+1 -1
View File
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "FloatingPanel"
s.version = "1.6.4"
s.version = "1.6.5"
s.summary = "FloatingPanel is a clean and easy-to-use UI component of a floating panel interface."
s.description = <<-DESC
FloatingPanel is a clean and easy-to-use UI component for a new interface introduced in Apple Maps, Shortcuts and Stocks app.
+1
View File
@@ -623,6 +623,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
let animator = behavior.removalInteractionAnimator(vc, with: velocityVector)
animator.addAnimations { [weak self] in
self?.state = .hidden
self?.updateLayout(to: .hidden)
}
animator.addCompletion({ _ in
@@ -12,7 +12,9 @@ public protocol FloatingPanelControllerDelegate: class {
// if it returns nil, FloatingPanelController uses the default behavior
func floatingPanel(_ vc: FloatingPanelController, behaviorFor newCollection: UITraitCollection) -> FloatingPanelBehavior?
func floatingPanelDidChangePosition(_ vc: FloatingPanelController) // changed the settled position in the model layer
/// Called when the floating panel has changed to a new position. Can be called inside an animation block, so any
/// view properties set inside this function will be automatically animated alongside the panel.
func floatingPanelDidChangePosition(_ vc: FloatingPanelController)
/// Asks the delegate if dragging should begin by the pan gesture recognizer.
func floatingPanelShouldBeginDragging(_ vc: FloatingPanelController) -> Bool
@@ -361,7 +363,6 @@ open class FloatingPanelController: UIViewController, UIScrollViewDelegate, UIGe
/// Hides the surface view to the hidden position
public func hide(animated: Bool = false, completion: (() -> Void)? = nil) {
safeAreaInsetsObservation = nil
move(to: .hidden,
animated: animated,
completion: completion)
@@ -36,8 +36,6 @@ public class FloatingPanelSurfaceView: UIView {
public weak var contentView: UIView!
/// The content insets specifying the insets around the content view.
///
/// - important: Currently the `bottom` inset is ignored.
public var contentInsets: UIEdgeInsets = .zero {
didSet {
// Needs update constraints
@@ -115,6 +113,8 @@ public class FloatingPanelSurfaceView: UIView {
private lazy var grabberHandleHeightConstraint: NSLayoutConstraint = grabberHandle.heightAnchor.constraint(equalToConstant: grabberHandleHeight)
private lazy var grabberHandleTopConstraint: NSLayoutConstraint = grabberHandle.topAnchor.constraint(equalTo: topAnchor, constant: grabberTopPadding)
public override class var requiresConstraintBasedLayout: Bool { return true }
override init(frame: CGRect) {
super.init(frame: frame)
addSubViews()
@@ -155,7 +155,7 @@ public class FloatingPanelSurfaceView: UIView {
contentViewTopConstraint?.constant = contentInsets.top
contentViewLeftConstraint?.constant = contentInsets.left
contentViewRightConstraint?.constant = contentInsets.right
contentViewHeightConstraint?.constant = -containerTopInset
contentViewHeightConstraint?.constant = -(containerTopInset + contentInsets.top + contentInsets.bottom)
grabberHandleTopConstraint.constant = grabberTopPadding
grabberHandleWidthConstraint.constant = grabberHandleWidth
@@ -221,7 +221,7 @@ public class FloatingPanelSurfaceView: UIView {
let topConstraint = contentView.topAnchor.constraint(equalTo: topAnchor, constant: contentInsets.top)
let leftConstraint = contentView.leftAnchor.constraint(equalTo: leftAnchor, constant: contentInsets.left)
let rightConstraint = rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: contentInsets.right)
let heightConstraint = contentView.heightAnchor.constraint(equalTo: heightAnchor, constant: -containerTopInset)
let heightConstraint = contentView.heightAnchor.constraint(equalTo: heightAnchor, constant: -(containerTopInset + contentInsets.top + contentInsets.bottom))
NSLayoutConstraint.activate([
topConstraint,
leftConstraint,
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.6.4</string>
<string>1.6.5</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
@@ -52,44 +52,58 @@ class FloatingPanelControllerTests: XCTestCase {
}
func test_moveTo() {
let fpc = FloatingPanelController(delegate: nil)
let delegate = FloatingPanelTestDelegate()
let fpc = FloatingPanelController(delegate: delegate)
XCTAssertEqual(delegate.position, .hidden)
fpc.showForTest()
XCTAssertEqual(delegate.position, .half)
fpc.hide()
XCTAssertEqual(delegate.position, .hidden)
fpc.move(to: .full, animated: false)
XCTAssertEqual(fpc.position, .full)
XCTAssertEqual(delegate.position, .full)
XCTAssertEqual(fpc.surfaceView.frame.minY, fpc.originYOfSurface(for: .full))
fpc.move(to: .half, animated: false)
XCTAssertEqual(fpc.position, .half)
XCTAssertEqual(delegate.position, .half)
XCTAssertEqual(fpc.surfaceView.frame.minY, fpc.originYOfSurface(for: .half))
fpc.move(to: .tip, animated: false)
XCTAssertEqual(fpc.position, .tip)
XCTAssertEqual(delegate.position, .tip)
XCTAssertEqual(fpc.surfaceView.frame.minY, fpc.originYOfSurface(for: .tip))
fpc.move(to: .hidden, animated: false)
XCTAssertEqual(fpc.position, .hidden)
XCTAssertEqual(delegate.position, .hidden)
XCTAssertEqual(fpc.surfaceView.frame.minY, fpc.originYOfSurface(for: .hidden))
fpc.move(to: .full, animated: true)
waitRunLoop(secs: 0.3)
XCTAssertEqual(fpc.position, .full)
XCTAssertEqual(delegate.position, .full)
XCTAssertEqual(fpc.surfaceView.frame.minY, fpc.originYOfSurface(for: .full))
fpc.move(to: .half, animated: true)
waitRunLoop(secs: 0.3)
XCTAssertEqual(fpc.position, .half)
XCTAssertEqual(delegate.position, .half)
XCTAssertEqual(fpc.surfaceView.frame.minY, fpc.originYOfSurface(for: .half))
fpc.move(to: .tip, animated: true)
waitRunLoop(secs: 0.3)
XCTAssertEqual(fpc.position, .tip)
XCTAssertEqual(delegate.position, .tip)
XCTAssertEqual(fpc.surfaceView.frame.minY, fpc.originYOfSurface(for: .tip))
fpc.move(to: .hidden, animated: true)
waitRunLoop(secs: 0.3)
XCTAssertEqual(fpc.position, .hidden)
XCTAssertEqual(delegate.position, .hidden)
XCTAssertEqual(fpc.surfaceView.frame.minY, fpc.originYOfSurface(for: .hidden))
}
func test_originSurfaceY() {
@@ -12,6 +12,7 @@ class FloatingPanelSurfaceViewTests: XCTestCase {
func test_surfaceView() {
let surface = FloatingPanelSurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
XCTAssertTrue(FloatingPanelSurfaceView.requiresConstraintBasedLayout)
XCTAssert(surface.contentView == nil)
surface.layoutIfNeeded()
XCTAssert(surface.grabberHandle.frame.minY == 6.0)
+4
View File
@@ -21,12 +21,16 @@ extension FloatingPanelController {
class FloatingPanelTestDelegate: FloatingPanelControllerDelegate {
var layout: FloatingPanelLayout?
var behavior: FloatingPanelBehavior?
var position: FloatingPanelPosition = .hidden
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? {
return layout
}
func floatingPanel(_ vc: FloatingPanelController, behaviorFor newCollection: UITraitCollection) -> FloatingPanelBehavior? {
return behavior
}
func floatingPanelDidChangePosition(_ vc: FloatingPanelController) {
position = vc.position
}
}
protocol FloatingPanelTestLayout: FloatingPanelFullScreenLayout {}
+7
View File
@@ -43,6 +43,7 @@ The new interface displays the related contents and utilities in parallel as a u
- [Work your contents together with a floating panel behavior](#work-your-contents-together-with-a-floating-panel-behavior)
- [Notes](#notes)
- ['Show' or 'Show Detail' Segues from `FloatingPanelController`'s content view controller](#show-or-show-detail-segues-from-floatingpanelcontrollers-content-view-controller)
- [UISearchController issue](#uisearchcontroller-issue)
- [FloatingPanelSurfaceView's issue on iOS 10](#floatingpanelsurfaceviews-issue-on-ios-10)
- [Author](#author)
- [License](#license)
@@ -439,6 +440,12 @@ A `FloatingPanelController` object proxies an action for `show(_:sender)` to the
It's a great way to decouple between a floating panel and the content VC.
### UISearchController issue
`UISearchController` isn't able to be used with `FloatingPanelController` by the system design.
Because `UISearchController` automatically presents itself modally when a user interacts with the search bar, and then it swaps the superview of the search bar to the view managed by itself while it displays. As a result, `FloatingPanelController` can't control the search bar when it's active, as you can see from [the screen shot](https://github.com/SCENEE/FloatingPanel/issues/248#issuecomment-521263831).
### FloatingPanelSurfaceView's issue on iOS 10
* 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.