Merge pull request #232 from SCENEE/refactor-layout-adapter

Refactor layout adapter
This commit is contained in:
Shin Yamamoto
2019-07-03 14:24:47 +09:00
committed by GitHub
10 changed files with 122 additions and 39 deletions
@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
542753C622C49A6E00D17955 /* FloatingPanelLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542753C522C49A6E00D17955 /* FloatingPanelLayoutTests.swift */; };
542753C822C49A8F00D17955 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542753C722C49A8F00D17955 /* Utils.swift */; };
54352E9621A51A2500CBCA08 /* FloatingPanelTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54352E9521A51A2500CBCA08 /* FloatingPanelTransitioning.swift */; };
54352E9821A521CA00CBCA08 /* FloatingPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54352E9721A521CA00CBCA08 /* FloatingPanelView.swift */; };
5450EEE421646DF500135936 /* FloatingPanelBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5450EEE321646DF500135936 /* FloatingPanelBehavior.swift */; };
@@ -18,7 +20,7 @@
545DBA2B2152383100CA77B8 /* GrabberHandleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DBA2A2152383100CA77B8 /* GrabberHandleView.swift */; };
54A6B6B122968B530077F348 /* FloatingPanelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A6B6B022968B530077F348 /* FloatingPanelTests.swift */; };
54A6B6B622968F710077F348 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54A6B6B522968F710077F348 /* LaunchScreen.storyboard */; };
54A6B6B82296A8520077F348 /* FloatingPanelViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A6B6B72296A8520077F348 /* FloatingPanelViewTests.swift */; };
54A6B6B82296A8520077F348 /* FloatingPanelSurfaceViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54A6B6B72296A8520077F348 /* FloatingPanelSurfaceViewTests.swift */; };
54ABD7AF216CCFF7002E6C13 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54ABD7AE216CCFF7002E6C13 /* Logger.swift */; };
54CDC5D3215B6D5A007D205C /* FloatingPanelSurfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D2215B6D5A007D205C /* FloatingPanelSurfaceView.swift */; };
54CDC5D5215B6D8D007D205C /* FloatingPanelBackdropView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CDC5D4215B6D8D007D205C /* FloatingPanelBackdropView.swift */; };
@@ -45,6 +47,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
542753C522C49A6E00D17955 /* FloatingPanelLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelLayoutTests.swift; sourceTree = "<group>"; };
542753C722C49A8F00D17955 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
54352E9521A51A2500CBCA08 /* FloatingPanelTransitioning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelTransitioning.swift; sourceTree = "<group>"; };
54352E9721A521CA00CBCA08 /* FloatingPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelView.swift; sourceTree = "<group>"; };
5450EEE321646DF500135936 /* FloatingPanelBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelBehavior.swift; sourceTree = "<group>"; };
@@ -59,7 +63,7 @@
545DBA2A2152383100CA77B8 /* GrabberHandleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrabberHandleView.swift; sourceTree = "<group>"; };
54A6B6B022968B530077F348 /* FloatingPanelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelTests.swift; sourceTree = "<group>"; };
54A6B6B522968F710077F348 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
54A6B6B72296A8520077F348 /* FloatingPanelViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelViewTests.swift; sourceTree = "<group>"; };
54A6B6B72296A8520077F348 /* FloatingPanelSurfaceViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelSurfaceViewTests.swift; sourceTree = "<group>"; };
54ABD7AE216CCFF7002E6C13 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
54CDC5D2215B6D5A007D205C /* FloatingPanelSurfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelSurfaceView.swift; sourceTree = "<group>"; };
54CDC5D4215B6D8D007D205C /* FloatingPanelBackdropView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingPanelBackdropView.swift; sourceTree = "<group>"; };
@@ -140,8 +144,10 @@
isa = PBXGroup;
children = (
54A6B6B022968B530077F348 /* FloatingPanelTests.swift */,
54A6B6B72296A8520077F348 /* FloatingPanelViewTests.swift */,
542753C522C49A6E00D17955 /* FloatingPanelLayoutTests.swift */,
54A6B6B72296A8520077F348 /* FloatingPanelSurfaceViewTests.swift */,
545DB9CF2151169500CA77B8 /* FloatingPanelControllerTests.swift */,
542753C722C49A8F00D17955 /* Utils.swift */,
545DB9D12151169500CA77B8 /* Info.plist */,
);
path = Tests;
@@ -309,6 +315,7 @@
545DBA2B2152383100CA77B8 /* GrabberHandleView.swift in Sources */,
54352E9621A51A2500CBCA08 /* FloatingPanelTransitioning.swift in Sources */,
545DB9DE215118C800CA77B8 /* UIExtensions.swift in Sources */,
542753C822C49A8F00D17955 /* Utils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -318,7 +325,8 @@
files = (
54A6B6B122968B530077F348 /* FloatingPanelTests.swift in Sources */,
545DB9D02151169500CA77B8 /* FloatingPanelControllerTests.swift in Sources */,
54A6B6B82296A8520077F348 /* FloatingPanelViewTests.swift in Sources */,
542753C622C49A6E00D17955 /* FloatingPanelLayoutTests.swift in Sources */,
54A6B6B82296A8520077F348 /* FloatingPanelSurfaceViewTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -29,7 +29,9 @@
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
skipped = "NO"
parallelizable = "YES"
testExecutionOrdering = "random">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "545DB9C92151169500CA77B8"
+2 -14
View File
@@ -662,21 +662,9 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
}
private func distance(to targetPosition: FloatingPanelPosition) -> CGFloat {
let topY = layoutAdapter.topY
let middleY = layoutAdapter.middleY
let bottomY = layoutAdapter.bottomY
let currentY = surfaceView.frame.minY
switch targetPosition {
case .full:
return CGFloat(abs(currentY - topY))
case .half:
return CGFloat(abs(currentY - middleY))
case .tip:
return CGFloat(abs(currentY - bottomY))
case .hidden:
fatalError("Now .hidden must not be used for a user interaction")
}
let targetY = layoutAdapter.positionY(for: targetPosition)
return CGFloat(abs(currentY - targetY))
}
private func directionalPosition(at currentY: CGFloat, with translation: CGPoint) -> FloatingPanelPosition {
@@ -68,6 +68,10 @@ public enum FloatingPanelPosition: Int {
case half
case tip
case hidden
static var allCases: [FloatingPanelPosition] {
return [.full, .half, .tip, .hidden]
}
}
///
@@ -145,7 +149,7 @@ open class FloatingPanelController: UIViewController, UIScrollViewDelegate, UIGe
}
private var _contentViewController: UIViewController?
private var floatingPanel: FloatingPanel!
private(set) var floatingPanel: FloatingPanel!
private var preSafeAreaInsets: UIEdgeInsets = .zero // Capture the latest one
private var safeAreaInsetsObservation: NSKeyValueObservation?
private let modalTransition = FloatingPanelModalTransition()
+5 -7
View File
@@ -181,13 +181,11 @@ class FloatingPanelLayoutAdapter {
}
var topMostState: FloatingPanelPosition {
if supportedPositions.contains(.full) {
return .full
}
if supportedPositions.contains(.half) {
return .half
}
return .tip
return supportedPositions.sorted(by: { $0.rawValue < $1.rawValue }).first ?? .hidden
}
var bottomMostState: FloatingPanelPosition {
return supportedPositions.sorted(by: { $0.rawValue < $1.rawValue }).last ?? .hidden
}
var topY: CGFloat {
@@ -30,12 +30,8 @@ class FloatingPanelControllerTests: XCTestCase {
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else { fatalError() }
let fpc = FloatingPanelController()
fpc.addPanel(toParent: rootVC)
waitRunLoop(secs: 1.0)
XCTAssert(fpc.surfaceView.frame.minY == (fpc.view.bounds.height - fpc.layoutInsets.bottom) - fpc.layout.insetFor(position: .half)!)
fpc.move(to: .tip, animated: true)
waitRunLoop(secs: 1.0)
fpc.move(to: .tip, animated: false)
XCTAssert(fpc.surfaceView.frame.minY == (fpc.view.bounds.height - fpc.layoutInsets.bottom) - fpc.layout.insetFor(position: .tip)!)
}
@@ -58,11 +54,7 @@ class FloatingPanelControllerTests: XCTestCase {
}
}
func waitRunLoop(secs: TimeInterval = 0) {
RunLoop.main.run(until: Date(timeIntervalSinceNow: secs))
}
class MyZombieViewController: UIViewController, FloatingPanelLayout, FloatingPanelBehavior, FloatingPanelControllerDelegate {
private class MyZombieViewController: UIViewController, FloatingPanelLayout, FloatingPanelBehavior, FloatingPanelControllerDelegate {
var fpc: FloatingPanelController?
override func viewDidLoad() {
fpc = FloatingPanelController(delegate: self)
@@ -0,0 +1,41 @@
//
// Created by Shin Yamamoto on 2019/06/27.
// Copyright © 2019 scenee. All rights reserved.
//
import XCTest
@testable import FloatingPanel
class FloatingPanelLayoutTests: XCTestCase {
override func setUp() {}
override func tearDown() {}
func test_layoutAdapter_topAndBottomMostState() {
let fpc = FloatingPanelController(delegate: nil)
fpc.loadViewIfNeeded()
fpc.view.frame = CGRect(x: 0, y: 0, width: 375, height: 667)
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.topMostState, .full)
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.bottomMostState, .tip)
class FloatingPanelLayoutWithHidden: FloatingPanelLayout {
func insetFor(position: FloatingPanelPosition) -> CGFloat? { return nil }
let initialPosition: FloatingPanelPosition = .hidden
let supportedPositions: Set<FloatingPanelPosition> = [.hidden, .half, .full]
}
class FloatingPanelLayout2Positions: FloatingPanelLayout {
func insetFor(position: FloatingPanelPosition) -> CGFloat? { return nil }
let initialPosition: FloatingPanelPosition = .tip
let supportedPositions: Set<FloatingPanelPosition> = [.tip, .half]
}
let delegate = FloatingPanelTestDelegate()
delegate.layout = FloatingPanelLayoutWithHidden()
fpc.delegate = delegate
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.topMostState, .full)
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.bottomMostState, .half) // Will fixed on fix-hidden-position branch
delegate.layout = FloatingPanelLayout2Positions()
fpc.delegate = delegate
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.topMostState, .half)
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.bottomMostState, .tip)
}
}
@@ -6,8 +6,7 @@
import XCTest
@testable import FloatingPanel
class FloatingPanelViewTests: XCTestCase {
class FloatingPanelSurfaceViewTests: XCTestCase {
override func setUp() {}
override func tearDown() {}
@@ -24,6 +23,26 @@ class FloatingPanelViewTests: XCTestCase {
XCTAssert(surface.backgroundColor == surface.containerView.backgroundColor)
}
func test_surfaceView_constraintsUpdate() {
let window = UIWindow()
let surface = FloatingPanelSurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
window.addSubview(surface)
window.makeKeyAndVisible()
XCTAssert(surface.contentView == nil)
surface.layoutIfNeeded()
XCTAssert(surface.grabberHandle.frame.minY == 6.0)
XCTAssert(surface.grabberHandle.frame.width == surface.grabberHandleWidth)
XCTAssert(surface.grabberHandle.frame.height == surface.grabberHandleHeight)
surface.grabberHandleWidth = 44.0
surface.grabberHandleHeight = 12.0
surface.layoutIfNeeded()
waitRunLoop(secs: 0.000_001)
XCTAssert(surface.grabberHandle.frame.width == surface.grabberHandleWidth, "\(surface.grabberHandle.frame.width) == \(surface.grabberHandleWidth)")
XCTAssert(surface.grabberHandle.frame.height == surface.grabberHandleHeight, "\(surface.grabberHandle.frame.height) == \(surface.grabberHandleHeight)")
window.resignKey()
}
func test_surfaceView_cornderRaduis() {
let surface = FloatingPanelSurfaceView(frame: CGRect(x: 0.0, y: 0.0, width: 320.0, height: 480.0))
XCTAssert(surface.cornerRadius == 0.0)
+13
View File
@@ -62,4 +62,17 @@ class FloatingPanelTests: XCTestCase {
XCTAssertEqual(contentVC2.tableView.showsVerticalScrollIndicator, false)
XCTAssertEqual(contentVC2.tableView.bounces, false)
}
}
private protocol FloatingPanelTestLayout: FloatingPanelLayout {}
private extension FloatingPanelTestLayout {
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
switch position {
case .full: return 18.0
case .half: return 262.0
case .tip: return 69.0
default: return nil
}
}
}
+18
View File
@@ -0,0 +1,18 @@
//
// Created by Shin Yamamoto on 2019/06/27.
// Copyright © 2019 scenee. All rights reserved.
//
import Foundation
@testable import FloatingPanel
func waitRunLoop(secs: TimeInterval = 0) {
RunLoop.main.run(until: Date(timeIntervalSinceNow: secs))
}
class FloatingPanelTestDelegate: FloatingPanelControllerDelegate {
var layout: FloatingPanelLayout?
func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? {
return layout
}
}