Skip to content

Commit b941f91

Browse files
authored
Fix the backdrop alpha when the view size or its size class changes (#573)
This fixes #572 to change the backdrop alpha when the view size or its size class changes. The main change is that `true` is passed as a `forceLayout` parameter into `viewWillTransition(to:with:)` callbacks. Because it's necessary for the backdrop alpha's update when the view size or its size class changes. This also fixes a regression at `9c45c31` commit. ```diff - layoutAdapter.activateLayout(for: state, forceLayout: true) + layoutAdapter.activateLayout(for: state, forceLayout: forceLayout) ``` The behavior before the above change indicates that the method has worked well even when `forceLayout` is set to `true` in their callbacks. Additional improvements: * Format `activateLayout(forceLayout:contentInsetAdjustmentBehavior:)` * Add `_floor` function for `test_updateBackdropAlpha()`
1 parent f917316 commit b941f91

File tree

4 files changed

+74
-13
lines changed

4 files changed

+74
-13
lines changed

Sources/Controller.swift

+6-4
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ open class FloatingPanelController: UIViewController {
316316
// Change a layout for the new view size
317317
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: size) {
318318
layout = newLayout
319-
activateLayout(forceLayout: false)
319+
activateLayout(forceLayout: true)
320320
}
321321

322322
if view.translatesAutoresizingMaskIntoConstraints {
@@ -335,7 +335,7 @@ open class FloatingPanelController: UIViewController {
335335
// Change a layout for the new trait collection
336336
if let newLayout = self.delegate?.floatingPanel?(self, layoutFor: newCollection) {
337337
self.layout = newLayout
338-
activateLayout(forceLayout: false)
338+
activateLayout(forceLayout: true)
339339
}
340340
}
341341

@@ -401,8 +401,10 @@ open class FloatingPanelController: UIViewController {
401401
}
402402

403403
private func activateLayout(forceLayout: Bool = false) {
404-
floatingPanel.activateLayout(forceLayout: forceLayout,
405-
contentInsetAdjustmentBehavior: contentInsetAdjustmentBehavior)
404+
floatingPanel.activateLayout(
405+
forceLayout: forceLayout,
406+
contentInsetAdjustmentBehavior: contentInsetAdjustmentBehavior
407+
)
406408
}
407409

408410
func remove() {

Sources/Core.swift

+5-3
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,10 @@ class Core: NSObject, UIGestureRecognizerDelegate {
197197

198198
// MARK: - Layout update
199199

200-
func activateLayout(forceLayout: Bool = false,
201-
contentInsetAdjustmentBehavior: FloatingPanelController.ContentInsetAdjustmentBehavior) {
200+
func activateLayout(
201+
forceLayout: Bool = false,
202+
contentInsetAdjustmentBehavior: FloatingPanelController.ContentInsetAdjustmentBehavior
203+
) {
202204
layoutAdapter.prepareLayout()
203205

204206
// preserve the current content offset if contentInsetAdjustmentBehavior is `.always`
@@ -208,7 +210,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
208210
}
209211

210212
layoutAdapter.updateStaticConstraint()
211-
layoutAdapter.activateLayout(for: state, forceLayout: true)
213+
layoutAdapter.activateLayout(for: state, forceLayout: forceLayout)
212214

213215
// Update the backdrop alpha only when called in `Controller.show(animated:completion:)`
214216
// Because that prevents a backdrop flicking just before presenting a panel(#466).

Tests/CoreTests.swift

+41-6
Original file line numberDiff line numberDiff line change
@@ -195,26 +195,41 @@ class CoreTests: XCTestCase {
195195
}
196196
}
197197
}
198-
let fpc = FloatingPanelController()
198+
class BackdropTestLayout2: FloatingPanelTestLayout {
199+
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
200+
return 0.0
201+
}
202+
}
203+
class TestDelegate: FloatingPanelControllerDelegate {
204+
var layout: FloatingPanelLayout = BackdropTestLayout2()
205+
func floatingPanel(_ fpc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout { layout }
206+
func floatingPanel(_ fpc: FloatingPanelController, layoutFor size: CGSize) -> FloatingPanelLayout { layout }
207+
}
208+
func _floor(_ alpha: CGFloat) -> CGFloat {
209+
return floor(fpc.backdropView.alpha * 1e+06) / 1e+06
210+
}
211+
212+
let delegate = TestDelegate()
213+
let fpc = FloatingPanelController(delegate: delegate)
199214
fpc.layout = BackdropTestLayout()
200215

201216
fpc.showForTest()
202217

203218
fpc.move(to: .full, animated: false)
204-
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
219+
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
205220

206221
fpc.move(to: .half, animated: false)
207222
XCTAssertEqual(fpc.backdropView.alpha, 0.0)
208223

209224
fpc.move(to: .tip, animated: false)
210-
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
225+
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
211226

212227
let exp1 = expectation(description: "move to full with animation")
213228
fpc.move(to: .full, animated: true) {
214229
exp1.fulfill()
215230
}
216231
wait(for: [exp1], timeout: 1.0)
217-
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
232+
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
218233

219234
let exp2 = expectation(description: "move to half with animation")
220235
fpc.move(to: .half, animated: true) {
@@ -223,14 +238,34 @@ class CoreTests: XCTestCase {
223238
wait(for: [exp2], timeout: 1.0)
224239
XCTAssertEqual(fpc.backdropView.alpha, 0.0)
225240

241+
// Test a content mode change of FloatingPanelController
242+
226243
let exp3 = expectation(description: "move to tip with animation")
227244
fpc.move(to: .tip, animated: true) {
228245
exp3.fulfill()
229246
}
230247
fpc.contentMode = .fitToBounds
231-
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must not affect the backdrop alpha by changing the content mode
248+
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must not affect the backdrop alpha by changing the content mode
232249
wait(for: [exp3], timeout: 1.0)
233-
XCTAssertEqual(floor(fpc.backdropView.alpha * 1000_000) / 1000_000, 0.3)
250+
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
251+
252+
// Test a size class change of FloatingPanelController.view
253+
254+
fpc.move(to: .full, animated: false)
255+
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
256+
fpc.willTransition(to: UITraitCollection(horizontalSizeClass: .regular), with: MockTransitionCoordinator())
257+
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must update the alpha by BackdropTestLayout2 in TestDelegate.
258+
259+
// Test a view size change of FloatingPanelController.view
260+
261+
fpc.move(to: .full, animated: false)
262+
delegate.layout = BackdropTestLayout()
263+
fpc.invalidateLayout()
264+
XCTAssertEqual(_floor(fpc.backdropView.alpha), 0.3)
265+
266+
delegate.layout = BackdropTestLayout2()
267+
fpc.viewWillTransition(to: CGSize.zero, with: MockTransitionCoordinator())
268+
XCTAssertEqual(fpc.backdropView.alpha, 0.0) // Must update the alpha by BackdropTestLayout2 in TestDelegate.
234269
}
235270

236271
func test_targetPosition_1positions() {

Tests/TestSupports.swift

+22
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,25 @@ class FloatingPanelProjectableBehavior: FloatingPanelBehavior {
7777
return true
7878
}
7979
}
80+
81+
class MockTransitionCoordinator: NSObject, UIViewControllerTransitionCoordinator {
82+
func animate(alongsideTransition animation: ((UIViewControllerTransitionCoordinatorContext) -> Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Void)? = nil) -> Bool { true }
83+
func animateAlongsideTransition(in view: UIView?, animation: ((UIViewControllerTransitionCoordinatorContext) -> Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Void)? = nil) -> Bool { true }
84+
func notifyWhenInteractionEnds(_ handler: @escaping (UIViewControllerTransitionCoordinatorContext) -> Void) {}
85+
func notifyWhenInteractionChanges(_ handler: @escaping (UIViewControllerTransitionCoordinatorContext) -> Void) {}
86+
var isAnimated: Bool = false
87+
var presentationStyle: UIModalPresentationStyle = .fullScreen
88+
var initiallyInteractive: Bool = false
89+
var isInterruptible: Bool = false
90+
var isInteractive: Bool = false
91+
var isCancelled: Bool = false
92+
var transitionDuration: TimeInterval = 0.25
93+
var percentComplete: CGFloat = 0
94+
var completionVelocity: CGFloat = 0
95+
var completionCurve: UIView.AnimationCurve = .easeInOut
96+
func viewController(forKey key: UITransitionContextViewControllerKey) -> UIViewController? { nil }
97+
func view(forKey key: UITransitionContextViewKey) -> UIView? { nil }
98+
var containerView: UIView { UIView() }
99+
var targetTransform: CGAffineTransform = .identity
100+
}
101+

0 commit comments

Comments
 (0)