Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5325e707e6 | |||
| 1dc0a6b76a | |||
| 2689d68bab | |||
| 218a12962f | |||
| 916d2ec76a | |||
| 1b1ba5deef | |||
| 58b2df4996 | |||
| 3b812be84e |
@@ -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"/>
|
||||
|
||||
@@ -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,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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user