Skip to content

Commit dc162ae

Browse files
committed
translate at top logic improved, sheet didPresent delegate method added
1 parent a1f496a commit dc162ae

File tree

6 files changed

+105
-65
lines changed

6 files changed

+105
-65
lines changed

Example/UBottomSheet.xcodeproj/project.pbxproj

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,27 @@
8484
/* End PBXFrameworksBuildPhase section */
8585

8686
/* Begin PBXGroup section */
87+
0C3118AF2458AF9B008F63CC /* Cells */ = {
88+
isa = PBXGroup;
89+
children = (
90+
0CC171412334257C002A02FD /* SimpleTableCell.swift */,
91+
0CDF2F68245638C700AAE66E /* EmbeddedCell.swift */,
92+
0CDF2F66245638B100AAE66E /* SimpleCollectionCell.swift */,
93+
);
94+
name = Cells;
95+
sourceTree = "<group>";
96+
};
97+
0C3118B02458AFAE008F63CC /* ViewControllers */ = {
98+
isa = PBXGroup;
99+
children = (
100+
0CC171422334257C002A02FD /* ViewController.swift */,
101+
0CA1808B245377CF00C6CBDE /* MapsDemoBottomSheetController.swift */,
102+
0CDF2F6A245647ED00AAE66E /* LabelViewController.swift */,
103+
0CDF2F6B245647ED00AAE66E /* LabelViewController.xib */,
104+
);
105+
name = ViewControllers;
106+
sourceTree = "<group>";
107+
};
87108
607FACC71AFB9204008FA782 = {
88109
isa = PBXGroup;
89110
children = (
@@ -109,15 +130,10 @@
109130
isa = PBXGroup;
110131
children = (
111132
607FACD51AFB9204008FA782 /* AppDelegate.swift */,
112-
0CC171422334257C002A02FD /* ViewController.swift */,
113-
0CC171412334257C002A02FD /* SimpleTableCell.swift */,
114-
0CDF2F68245638C700AAE66E /* EmbeddedCell.swift */,
115-
0CDF2F66245638B100AAE66E /* SimpleCollectionCell.swift */,
116-
0CA1808B245377CF00C6CBDE /* MapsDemoBottomSheetController.swift */,
133+
0C3118AF2458AF9B008F63CC /* Cells */,
134+
0C3118B02458AFAE008F63CC /* ViewControllers */,
117135
0CC17148233425AA002A02FD /* Images.xcassets */,
118136
0CC1714623342585002A02FD /* Main.storyboard */,
119-
0CDF2F6A245647ED00AAE66E /* LabelViewController.swift */,
120-
0CDF2F6B245647ED00AAE66E /* LabelViewController.xib */,
121137
607FACDE1AFB9204008FA782 /* LaunchScreen.xib */,
122138
607FACD31AFB9204008FA782 /* Supporting Files */,
123139
);

Example/UBottomSheet/ViewController.swift

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import UBottomSheet
1111

1212
class ViewController: UIViewController {
1313
var sheetCoordinator: UBottomSheetCoordinator!
14+
var backView: PassThroughView?
1415

1516
override func viewDidLoad() {
1617
super.viewDidLoad()
@@ -20,50 +21,53 @@ class ViewController: UIViewController {
2021
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MapsDemoBottomSheetController") as! MapsDemoBottomSheetController
2122
vc.sheetCoordinator = sheetCoordinator
2223
sheetCoordinator.addSheet(vc, to: self, didContainerCreate: { container in
23-
self.addBackDimmingBackView(below: container)
2424
let f = self.view.frame
2525
let rect = CGRect(x: f.minX, y: f.minY, width: f.width, height: f.height)
2626
container.roundCorners(corners: [.topLeft, .topRight], radius: 10, rect: rect)
2727
})
2828
sheetCoordinator.setCornerRadius(10)
2929

3030
}
31-
32-
override func viewWillLayoutSubviews() {
33-
super.viewWillLayoutSubviews()
34-
sheetCoordinator.addDropShadowIfNotExist()
35-
}
3631

3732
private func addBackDimmingBackView(below container: UIView){
38-
let backView = PassThroughView()
39-
self.view.insertSubview(backView, belowSubview: container)
40-
backView.translatesAutoresizingMaskIntoConstraints = false
41-
backView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
42-
backView.bottomAnchor.constraint(equalTo: container.topAnchor, constant: 0).isActive = true
43-
backView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
44-
backView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
33+
backView = PassThroughView()
34+
self.view.insertSubview(backView!, belowSubview: container)
35+
backView!.translatesAutoresizingMaskIntoConstraints = false
36+
backView!.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
37+
backView!.bottomAnchor.constraint(equalTo: container.topAnchor, constant: 10).isActive = true
38+
backView!.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
39+
backView!.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
4540
}
4641
}
4742

4843
extension ViewController: UBottomSheetCoordinatorDelegate{
44+
45+
func bottomSheet(_ container: UIView?, didPresent state: SheetTranslationState) {
46+
// self.addBackDimmingBackView(below: container!)
47+
self.sheetCoordinator.addDropShadowIfNotExist()
48+
self.handleState(state)
49+
}
4950

5051
func bottomSheet(_ container: UIView?, didChange state: SheetTranslationState) {
51-
// switch state {
52-
// case .progressing(_, let percent):
53-
// self.backView?.backgroundColor = UIColor.black.withAlphaComponent(max(0.2, percent/100 * 0.8))
54-
// case .finished(_, let percent):
55-
// self.backView?.backgroundColor = UIColor.black.withAlphaComponent(max(0.2, percent/100 * 0.8))
56-
// default:
57-
// break
58-
// }
52+
handleState(state)
5953
}
6054

6155
func bottomSheet(_ container: UIView?, finishTranslateWith extraAnimation: @escaping ((CGFloat) -> Void) -> Void) {
6256
extraAnimation({ percent in
63-
// self.backView?.backgroundColor = UIColor.black.withAlphaComponent(max(0.2, percent/100 * 0.8))
57+
self.backView?.backgroundColor = UIColor.black.withAlphaComponent(percent/100 * 0.8)
6458
})
6559
}
6660

61+
func handleState(_ state: SheetTranslationState){
62+
switch state {
63+
case .progressing(_, let percent):
64+
self.backView?.backgroundColor = UIColor.black.withAlphaComponent(percent/100 * 0.8)
65+
case .finished(_, let percent):
66+
self.backView?.backgroundColor = UIColor.black.withAlphaComponent(percent/100 * 0.8)
67+
default:
68+
break
69+
}
70+
}
6771
}
6872

6973
extension UIView{

UBottomSheet.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
Pod::Spec.new do |s|
1010
s.name = 'UBottomSheet'
11-
s.version = '1.0.3'
11+
s.version = '1.0.4'
1212
s.summary = 'Mimics the iPhone Maps App bottom sheet'
1313
s.swift_version = '5.0'
1414

UBottomSheet/Classes/Extensions.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import UIKit
1010

1111
extension UIViewController {
12-
func ub_add(_ child: UIViewController, in container: UIView, animated: Bool = true, topInset: CGFloat) {
12+
func ub_add(_ child: UIViewController, in container: UIView, animated: Bool = true, topInset: CGFloat, completion: (()->Void)? = nil) {
1313
addChild(child)
1414
container.addSubview(child.view)
1515
child.didMove(toParent: self)
@@ -20,10 +20,12 @@ extension UIViewController {
2020
UIView.animate(withDuration: 0.3, animations: {
2121
container.frame = f
2222
}) { (_) in
23-
self.view.layoutIfNeeded()
23+
completion?()
2424
}
2525
}else{
26-
child.view.pinToEdges(to: container)
26+
container.frame = f
27+
child.view.frame = container.bounds
28+
completion?()
2729
}
2830

2931
}

UBottomSheet/Classes/UBottomSheetCoordinator.swift

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public class UBottomSheetCoordinator {
2525
private var draggables: [DraggableItem] = []
2626
///Drop shadow view behind container.
2727
private var dropShadowView: PassThroughView?
28+
/// accept difference equal if in tolerance
29+
private var tolerance: CGFloat = 0.0000001
2830

2931
public var availableHeight: CGFloat{
3032
return parent.view.frame.height
@@ -118,13 +120,12 @@ public class UBottomSheetCoordinator {
118120
let container = PassThroughView()
119121
self.container = container
120122
parent.view.addSubview(container)
121-
parent.ub_add(item, in: container, topInset: dataSource.sheetPositions(availableHeight).min()!)
123+
let position = dataSource.initialPosition(availableHeight)
124+
parent.ub_add(item, in: container, topInset: position){[weak self] in
125+
guard let sSelf = self else { return }
126+
sSelf.delegate?.bottomSheet(container, didPresent: .finished(position, sSelf.calculatePercent(at: position)))
127+
}
122128
didContainerCreate?(container)
123-
container.translatesAutoresizingMaskIntoConstraints = true
124-
let y = dataSource.sheetPositions(availableHeight)[0]
125-
container.frame = CGRect(x: 0, y: y, width: parent.view.frame.width, height: parent.view.frame.height - y)
126-
// container.pinToEdges(to: parent.view)
127-
// container.constraint(parent, for: .top)?.constant = dataSource.sheetPositions(availableHeight)[0]
128129
setPosition(dataSource.initialPosition(availableHeight), animated: false)
129130
}
130131

@@ -138,10 +139,11 @@ public class UBottomSheetCoordinator {
138139
}
139140

140141
public func addDropShadowIfNotExist(_ config: ((UIView)->Void)? = nil){
141-
guard self.dropShadowView == nil else {return}
142-
self.dropShadowView = PassThroughView()
142+
guard dropShadowView == nil else {return}
143+
dropShadowView = PassThroughView()
143144
parent.view.insertSubview(dropShadowView!, belowSubview: container!)
144-
self.dropShadowView?.pinToEdges(to: container!, insets: UIEdgeInsets(top: -container!.frame.minY, left: 0, bottom: 0, right: 0))
145+
dropShadowView?.pinToEdges(to: container!, insets: UIEdgeInsets(top: -getInitialFrame().minY, left: 0, bottom: 0, right: 0))
146+
145147
self.dropShadowView?.layer.masksToBounds = false
146148
if config == nil{
147149
applyDefaultShadowParams()
@@ -151,18 +153,33 @@ public class UBottomSheetCoordinator {
151153
}
152154
}
153155

156+
private func getInitialFrame() -> CGRect{
157+
let minY = parent.view.bounds.minY + dataSource.initialPosition(availableHeight)
158+
return CGRect(x: parent.view.bounds.minX,
159+
y: minY,
160+
width: parent.view.bounds.width,
161+
height: parent.view.bounds.maxY - minY)
162+
}
163+
154164
private func applyDefaultShadowParams(){
155-
dropShadowView?.layer.shadowPath = UIBezierPath(roundedRect: container!.frame, cornerRadius: cornerRadius).cgPath
165+
dropShadowView?.layer.shadowPath = UIBezierPath(roundedRect: getInitialFrame(), cornerRadius: cornerRadius).cgPath
156166
dropShadowView?.layer.shadowColor = UIColor.black.cgColor
157167
dropShadowView?.layer.shadowRadius = CGFloat.init(10)
158-
dropShadowView?.layer.shadowOpacity = Float.init(0.5)
168+
// dropShadowView?.layer.shadowOpacity = Float.init(0.5)
159169
dropShadowView?.layer.shadowOffset = CGSize.init(width: 0.0, height: 4.0)
170+
let animation = CABasicAnimation(keyPath: "shadowOpacity")
171+
animation.fromValue = 0.0
172+
animation.toValue = 0.5
173+
animation.isRemovedOnCompletion = false
174+
animation.fillMode = .forwards
175+
animation.duration = 0.3
176+
dropShadowView?.layer.add(animation, forKey: "fadeout")
160177
}
161178

162179
private func clearShadowBackground(){
163180
let p = CGMutablePath()
164-
p.addRect(parent.view.bounds)
165-
p.addPath(UIBezierPath(roundedRect: container!.frame, cornerRadius: cornerRadius).cgPath)
181+
p.addRect(parent.view.bounds.insetBy(dx: 0, dy: -availableHeight))
182+
p.addPath(UIBezierPath(roundedRect: getInitialFrame(), cornerRadius: cornerRadius).cgPath)
166183
let mask = CAShapeLayer()
167184
mask.path = p
168185
mask.fillRule = .evenOdd
@@ -294,7 +311,7 @@ public class UBottomSheetCoordinator {
294311
case .changed:
295312
if let scroll = scrollView{
296313
switch dragDirection(vel) {
297-
case .up where (container!.frame.minY - minSheetPosition! > 0.001):
314+
case .up where (container!.frame.minY - minSheetPosition! > tolerance):
298315
translate(dy: dy - lastY)
299316
scroll.contentOffset.y = lastContentOffset.y
300317
case .down where scroll.contentOffset.y <= 0 && !scroll.isDecelerating:
@@ -303,30 +320,25 @@ public class UBottomSheetCoordinator {
303320
default:
304321
break
305322
}
306-
// if vel.y < 0 /*dragging up*/ && (container!.frame.minY - minSheetPosition! > 0.001){
307-
// translate(dy: dy - lastY)
308-
// scroll.contentOffset.y = lastContentOffset.y
309-
// }else if vel.y > 0 /*dragging down*/ && scroll.contentOffset.y <= 0 && !scroll.isDecelerating{
310-
// translate(dy: dy - lastY)
311-
// scroll.contentOffset.y = 0
312-
// }
313323
}else{
314324
translate(dy: dy)
315325
}
316-
case .ended, .cancelled, .failed:
326+
case .ended,
327+
.cancelled,
328+
.failed:
317329
if let scroll = scrollView{
318330
let minY = container!.frame.minY
319331
switch dragDirection(vel) {
320-
case .up where minY - minSheetPosition! > 0.001:
332+
case .up where minY - minSheetPosition! > tolerance:
321333
scroll.setContentOffset(lastContentOffset, animated: false)
322-
self.finishDragging(with: vel)
334+
self.finishDragging(with: vel, position: minY)
323335
default:
324336
if !isSheetPosition(minY){
325-
self.finishDragging(with: vel)
337+
self.finishDragging(with: vel, position: minY)
326338
}
327339
}
328340
}else{
329-
self.finishDragging(with: vel)
341+
self.finishDragging(with: vel, position: container!.frame.minY + dy)
330342
}
331343
default: break
332344
}
@@ -346,7 +358,7 @@ public class UBottomSheetCoordinator {
346358
*/
347359
private func isSheetPosition(_ point: CGFloat) -> Bool{
348360
return dataSource.sheetPositions(availableHeight).first(where: { (p) -> Bool in
349-
abs(p - point) < 0.001
361+
abs(p - point) < tolerance
350362
}) != nil
351363
}
352364

@@ -422,13 +434,17 @@ public class UBottomSheetCoordinator {
422434
let oldFrame = container!.frame
423435
var newY = oldFrame.minY
424436

425-
if hasExceededTopLimit(oldFrame.minY + dy, topLimit){
437+
if hasExceededTopLimit(oldFrame.minY, topLimit){
426438
totalTranslationMinY -= dy
439+
totalTranslationMaxY = maxSheetPosition!
427440
newY = dataSource.rubberBandLogicTop(totalTranslationMinY, topLimit)
428-
}else if hasExceededBottomLimit(oldFrame.minY + dy, bottomLimit){
441+
}else if hasExceededBottomLimit(oldFrame.minY, bottomLimit){
442+
totalTranslationMinY = minSheetPosition!
429443
totalTranslationMaxY += dy
430444
newY = dataSource.rubberBandLogicBottom(totalTranslationMaxY, bottomLimit)
431445
}else{
446+
totalTranslationMinY = minSheetPosition!
447+
totalTranslationMaxY = maxSheetPosition!
432448
newY += dy
433449
}
434450

@@ -443,10 +459,10 @@ public class UBottomSheetCoordinator {
443459
Pan gesture finish event
444460

445461
- parameter velocity: Pan gesture velocity
462+
- parameter position: new top constraint value
446463
*/
447-
private func finishDragging(with velocity: CGPoint){
448-
let top = container!.frame.minY
449-
let y = filteredPositions(velocity, currentPosition: top).nearest(to: top)
464+
private func finishDragging(with velocity: CGPoint, position: CGFloat){
465+
let y = filteredPositions(velocity, currentPosition: position).nearest(to: position)
450466
endTranslate(to: y, animated: true)
451467
}
452468

UBottomSheet/Classes/UBottomSheetCoordinatorDelegate.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ import UIKit
1212
public protocol UBottomSheetCoordinatorDelegate: class {
1313
func bottomSheet(_ container: UIView?, finishTranslateWith extraAnimation: @escaping ((_ percent: CGFloat)->Void)->Void)
1414
func bottomSheet(_ container: UIView?, didChange state: SheetTranslationState)
15+
func bottomSheet(_ container: UIView?, didPresent state: SheetTranslationState)
1516
}
1617

1718
///Default empty implementations
1819
extension UBottomSheetCoordinatorDelegate{
1920
public func bottomSheet(_ container: UIView?, finishTranslateWith extraAnimation: @escaping ((_ percent: CGFloat)->Void)->Void){ }
2021
public func bottomSheet(_ container: UIView?, didChange state: SheetTranslationState){ }
22+
public func bottomSheet(_ container: UIView?, didPresent state: SheetTranslationState){ }
2123
}

0 commit comments

Comments
 (0)