Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ccabb1914a | |||
| be2be99537 | |||
| afcf1ced36 | |||
| 5b33d3d5ff | |||
| dbef6a691a |
@@ -18,8 +18,11 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- swift: "5.9"
|
||||
xcode: "15.0.1"
|
||||
runsOn: macos-13
|
||||
- swift: "5.8"
|
||||
xcode: "14.3"
|
||||
xcode: "14.3.1"
|
||||
runsOn: macos-13
|
||||
- swift: "5.7"
|
||||
xcode: "14.1"
|
||||
@@ -52,6 +55,11 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: "17.0.1"
|
||||
xcode: "15.0.1"
|
||||
sim: "iPhone 15 Pro"
|
||||
parallel: NO # Stop random test job failures
|
||||
runsOn: macos-13
|
||||
- os: "16.4"
|
||||
xcode: "14.3.1"
|
||||
sim: "iPhone 14 Pro"
|
||||
|
||||
@@ -82,7 +82,10 @@ struct FloatingPanelView<Content: View, FloatingPanelContent: View>: UIViewContr
|
||||
/// Responsible to setup the view hierarchy and floating panel.
|
||||
final class Coordinator {
|
||||
private let parent: FloatingPanelView<Content, FloatingPanelContent>
|
||||
private lazy var fpc = FloatingPanelController()
|
||||
private lazy var fpc = {
|
||||
FloatingPanelController.enableDismissToRemove()
|
||||
return FloatingPanelController()
|
||||
}()
|
||||
|
||||
init(parent: FloatingPanelView<Content, FloatingPanelContent>) {
|
||||
self.parent = parent
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||
|
||||
import UIKit
|
||||
import FloatingPanel
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||
FloatingPanelController.enableDismissToRemove()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ extension MainViewController: UISearchBarDelegate {
|
||||
searchBar.showsCancelButton = true
|
||||
searchVC.showHeader(animated: true)
|
||||
searchVC.tableView.alpha = 1.0
|
||||
detailVC.dismiss(animated: true, completion: nil)
|
||||
detailFpc.removePanelFromParent(animated: true)
|
||||
}
|
||||
func deactivate(searchBar: UISearchBar) {
|
||||
searchBar.resignFirstResponder()
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||
|
||||
import UIKit
|
||||
import FloatingPanel
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||
FloatingPanelController.enableDismissToRemove()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
// Copyright 2018 the FloatingPanel authors. All rights reserved. MIT license.
|
||||
|
||||
#import "AppDelegate.h"
|
||||
@import FloatingPanel;
|
||||
|
||||
@interface AppDelegate ()
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey,id> *)launchOptions
|
||||
{
|
||||
[FloatingPanelController enableDismissToRemove];
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -81,7 +81,7 @@ class MainViewController: UIViewController, FloatingPanelControllerDelegate {
|
||||
}
|
||||
|
||||
private func hideStockTickerBanner() {
|
||||
// Dimiss top bar with dissolve animation
|
||||
// Dismiss top bar with dissolve animation
|
||||
UIView.animate(withDuration: 0.25) {
|
||||
self.topBannerView.alpha = 0.0
|
||||
self.labelStackView.alpha = 1.0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "FloatingPanel"
|
||||
s.version = "2.8.0"
|
||||
s.version = "2.8.1"
|
||||
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.
|
||||
|
||||
@@ -468,6 +468,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanel;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -499,6 +500,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanel;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -521,6 +523,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@@ -541,6 +544,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@@ -633,6 +637,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanel;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -657,6 +662,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.scenee.FloatingPanelTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
FloatingPanel is a simple and easy-to-use UI component designed for a user interface featured in Apple Maps, Shortcuts and Stocks app.
|
||||
The user interface displays related content and utilities alongside the main content.
|
||||
|
||||
Please see also [the API reference](https://floatingpanel.github.io/2.8.0/documentation/floatingpanel/) for more details.
|
||||
Please see also [the API reference](https://floatingpanel.github.io/2.8.1/documentation/floatingpanel/) for more details.
|
||||
|
||||

|
||||

|
||||
|
||||
+22
-10
@@ -288,8 +288,6 @@ open class FloatingPanelController: UIViewController {
|
||||
}
|
||||
|
||||
private func setUp() {
|
||||
_ = FloatingPanelController.dismissSwizzling
|
||||
|
||||
modalPresentationStyle = .custom
|
||||
transitioningDelegate = modalTransition
|
||||
|
||||
@@ -310,7 +308,7 @@ open class FloatingPanelController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- Overrides
|
||||
// MARK: - Overrides
|
||||
|
||||
/// Creates the view that the controller manages.
|
||||
open override func loadView() {
|
||||
@@ -381,7 +379,8 @@ open class FloatingPanelController: UIViewController {
|
||||
safeAreaInsetsObservation = nil
|
||||
}
|
||||
|
||||
// MARK:- Child view controller to consult
|
||||
// MARK: - Child view controller to consult
|
||||
|
||||
open override var childForStatusBarStyle: UIViewController? {
|
||||
return contentViewController
|
||||
}
|
||||
@@ -398,19 +397,19 @@ open class FloatingPanelController: UIViewController {
|
||||
return contentViewController
|
||||
}
|
||||
|
||||
// MARK:- Privates
|
||||
// MARK: - Privates
|
||||
|
||||
private func shouldUpdateLayout(from previous: UITraitCollection, to new: UITraitCollection) -> Bool {
|
||||
return previous.horizontalSizeClass != new.horizontalSizeClass
|
||||
|| previous.verticalSizeClass != new.verticalSizeClass
|
||||
|| previous.preferredContentSizeCategory != new.preferredContentSizeCategory
|
||||
|| previous.layoutDirection != new.layoutDirection
|
||||
|| previous.verticalSizeClass != new.verticalSizeClass
|
||||
|| previous.preferredContentSizeCategory != new.preferredContentSizeCategory
|
||||
|| previous.layoutDirection != new.layoutDirection
|
||||
}
|
||||
|
||||
private func update(safeAreaInsets: UIEdgeInsets) {
|
||||
guard
|
||||
preSafeAreaInsets != safeAreaInsets
|
||||
else { return }
|
||||
else { return }
|
||||
|
||||
os_log(msg, log: devLog, type: .debug, "Update safeAreaInsets = \(safeAreaInsets)")
|
||||
|
||||
@@ -540,7 +539,7 @@ open class FloatingPanelController: UIViewController {
|
||||
self.view.leftAnchor.constraint(equalTo: parent.view.leftAnchor, constant: 0.0),
|
||||
self.view.rightAnchor.constraint(equalTo: parent.view.rightAnchor, constant: 0.0),
|
||||
self.view.bottomAnchor.constraint(equalTo: parent.view.bottomAnchor, constant: 0.0),
|
||||
])
|
||||
])
|
||||
|
||||
show(animated: animated) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
@@ -694,6 +693,18 @@ open class FloatingPanelController: UIViewController {
|
||||
get { floatingPanel.layoutAdapter.surfaceLocation }
|
||||
set { floatingPanel.layoutAdapter.surfaceLocation = newValue }
|
||||
}
|
||||
|
||||
|
||||
/// Calling this will allow to invoke `removePanelFromParent(animated:completion:)` as needed by
|
||||
/// calling UIViewController's `dismiss` method
|
||||
///
|
||||
/// Previously, until v2.8, this was the default behavior. However, from v2.9 onwards, due to
|
||||
/// identified issues when used in conjunction with other libraries, it has been made an opt-in
|
||||
/// feature.
|
||||
@objc
|
||||
public static func enableDismissToRemove() {
|
||||
_ = FloatingPanelController.dismissSwizzling
|
||||
}
|
||||
}
|
||||
|
||||
extension FloatingPanelController {
|
||||
@@ -732,6 +743,7 @@ private var originalDismissImp: IMP?
|
||||
private typealias DismissFunction = @convention(c) (AnyObject, Selector, Bool, (() -> Void)?) -> Void
|
||||
extension FloatingPanelController {
|
||||
private static let dismissSwizzling: Void = {
|
||||
guard originalDismissImp == nil else { return }
|
||||
let aClass: AnyClass! = UIViewController.self //object_getClass(vc)
|
||||
if let originalMethod = class_getInstanceMethod(aClass, #selector(dismiss(animated:completion:))),
|
||||
let swizzledImp = class_getMethodImplementation(aClass, #selector(__swizzled_dismiss(animated:completion:))) {
|
||||
|
||||
+3
-1
@@ -209,6 +209,9 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
contentOffset = scrollView?.contentOffset
|
||||
}
|
||||
|
||||
if layoutAdapter.validStates.contains(state) == false {
|
||||
state = layoutAdapter.initialState
|
||||
}
|
||||
layoutAdapter.updateStaticConstraint()
|
||||
layoutAdapter.activateLayout(for: state, forceLayout: forceLayout)
|
||||
|
||||
@@ -725,7 +728,6 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
guard shouldAttract(to: target) else {
|
||||
self.state = target
|
||||
self.updateLayout(to: target)
|
||||
self.unlockScrollView()
|
||||
// The `floatingPanelDidEndDragging(_:willAttract:)` must be called after the state property changes.
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.8.0</string>
|
||||
<string>2.8.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
</dict>
|
||||
|
||||
@@ -782,12 +782,6 @@ class LayoutAdapter {
|
||||
NSLayoutConstraint.activate(constraint: self.fitToBoundsConstraint)
|
||||
}
|
||||
|
||||
var state = state
|
||||
|
||||
if validStates.contains(state) == false {
|
||||
state = layout.initialState
|
||||
}
|
||||
|
||||
// Recalculate the intrinsic size of a content view. This is because
|
||||
// UIView.systemLayoutSizeFitting() returns a different size between an
|
||||
// on-screen and off-screen view which includes
|
||||
|
||||
@@ -334,6 +334,36 @@ class ControllerTests: XCTestCase {
|
||||
fpc.move(to: .tip, animated: false)
|
||||
XCTAssertEqual(fpc.surfaceView.frame.height, fpc.view.bounds.height - fpc.surfaceLocation(for: .tip).y)
|
||||
}
|
||||
|
||||
func test_switching_layout() {
|
||||
final class FirstLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .half
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.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)
|
||||
]
|
||||
}
|
||||
final class SecondLayout: FloatingPanelLayout {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
let initialState: FloatingPanelState = .half
|
||||
let anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] = [
|
||||
.half: FloatingPanelLayoutAnchor(absoluteInset: 262, edge: .top, referenceGuide: .safeArea)
|
||||
]
|
||||
}
|
||||
let fpc = FloatingPanelController()
|
||||
fpc.layout = FirstLayout()
|
||||
fpc.showForTest()
|
||||
|
||||
fpc.move(to: .tip, animated: false)
|
||||
|
||||
// Switch to another layout
|
||||
fpc.layout = SecondLayout()
|
||||
fpc.invalidateLayout()
|
||||
|
||||
XCTAssertEqual(fpc.state, .half)
|
||||
}
|
||||
}
|
||||
|
||||
private class MyZombieViewController: UIViewController, FloatingPanelLayout, FloatingPanelBehavior, FloatingPanelControllerDelegate {
|
||||
|
||||
Reference in New Issue
Block a user