Compare commits

..

5 Commits

Author SHA1 Message Date
Shin Yamamoto ccabb1914a Opt-in the dismissSwizzling call as needed 2023-12-02 09:46:51 +09:00
Shin Yamamoto be2be99537 Fix a typo 2023-12-02 09:03:31 +09:00
Shin Yamamoto afcf1ced36 ci: support xcode 15.0.1 2023-11-18 11:51:15 +09:00
Shin Yamamoto 5b33d3d5ff Version 2.8.1 2023-11-04 21:17:09 +09:00
Shin Yamamoto dbef6a691a Fix an invalid behavior after switching to a new layout object (#611)
* Added a test for the use case, ControllerTests.test_switching_layout()
2023-11-04 13:25:25 +09:00
15 changed files with 96 additions and 25 deletions
+9 -1
View File
@@ -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
+6 -1
View File
@@ -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
}
}
+1 -1
View File
@@ -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 -1
View File
@@ -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.
+6
View File
@@ -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;
+1 -1
View File
@@ -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.
![Maps](https://github.com/SCENEE/FloatingPanel/blob/master/assets/maps.gif)
![Stocks](https://github.com/SCENEE/FloatingPanel/blob/master/assets/stocks.gif)
+22 -10
View File
@@ -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
View File
@@ -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
View File
@@ -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>
-6
View File
@@ -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
+30
View File
@@ -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 {