Skip to content

Commit 4f61411

Browse files
authored
Merge pull request #178 from mrsnow-git/master
FloatGrowingNotificationBanner implementation
2 parents 6efe186 + 730c680 commit 4f61411

7 files changed

+368
-70
lines changed

NotificationBanner/Classes/BannerPositionFrame.swift

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ class BannerPositionFrame: NSObject {
3232
init(bannerPosition: BannerPosition,
3333
bannerWidth: CGFloat,
3434
bannerHeight: CGFloat,
35-
maxY: CGFloat) {
35+
maxY: CGFloat,
36+
edgeInsets: UIEdgeInsets?) {
3637
super.init()
37-
self.startFrame = startFrame(for: bannerPosition, bannerWidth: bannerWidth, bannerHeight: bannerHeight, maxY: maxY)
38-
self.endFrame = endFrame(for: bannerPosition, bannerWidth: bannerWidth, bannerHeight: bannerHeight, maxY: maxY)
38+
self.startFrame = startFrame(for: bannerPosition, bannerWidth: bannerWidth, bannerHeight: bannerHeight, maxY: maxY, edgeInsets: edgeInsets)
39+
self.endFrame = endFrame(for: bannerPosition, bannerWidth: bannerWidth, bannerHeight: bannerHeight, maxY: maxY, edgeInsets: edgeInsets)
3940
}
4041

4142
/**
@@ -45,21 +46,26 @@ class BannerPositionFrame: NSObject {
4546
- parameter bannerHeight: The height of the notification banner
4647
- parameter maxY: The maximum `y` position the banner can slide in from. This value is only used
4748
if the bannerPosition is .bottom
49+
- parameter edgeInsets: The sides edges insets from superview
4850
*/
4951
private func startFrame(for bannerPosition: BannerPosition,
5052
bannerWidth: CGFloat,
5153
bannerHeight: CGFloat,
52-
maxY: CGFloat) -> CGRect {
54+
maxY: CGFloat,
55+
edgeInsets: UIEdgeInsets?) -> CGRect {
56+
57+
let edgeInsets = edgeInsets ?? .zero
58+
5359
switch bannerPosition {
5460
case .bottom:
55-
return CGRect(x: 0,
61+
return CGRect(x: edgeInsets.left,
5662
y: maxY,
57-
width: bannerWidth,
63+
width: bannerWidth - edgeInsets.left - edgeInsets.right,
5864
height: bannerHeight)
5965
case .top:
60-
return CGRect(x: 0,
66+
return CGRect(x: edgeInsets.left ?? 0,
6167
y: -bannerHeight,
62-
width: bannerWidth,
68+
width: bannerWidth - edgeInsets.left - edgeInsets.right,
6369
height: bannerHeight)
6470

6571
}
@@ -70,25 +76,28 @@ class BannerPositionFrame: NSObject {
7076
- parameter bannerPosition: The position the notification banners should slide in from
7177
- parameter bannerWidth: The width of the notification banner
7278
- parameter bannerHeight: The height of the notification banner
73-
- parameter maxY: The maximum `y` position the banner can slide in from. This value is only used
74-
if the bannerPosition is .bottom
79+
- parameter maxY: The maximum `y` position the banner can slide in from. This value is only used if the bannerPosition is .bottom
80+
- parameter edgeInsets: The sides edges insets from superview
7581
*/
7682
private func endFrame(for bannerPosition: BannerPosition,
7783
bannerWidth: CGFloat,
7884
bannerHeight: CGFloat,
79-
maxY: CGFloat) -> CGRect {
85+
maxY: CGFloat,
86+
edgeInsets: UIEdgeInsets?) -> CGRect {
87+
88+
let edgeInsets = edgeInsets ?? .zero
89+
8090
switch bannerPosition {
8191
case .bottom:
82-
return CGRect(x: 0,
83-
y: maxY - bannerHeight,
84-
width: bannerWidth,
85-
height: bannerHeight)
92+
return CGRect(x: edgeInsets.left,
93+
y: maxY - bannerHeight - edgeInsets.bottom,
94+
width: startFrame.width,
95+
height: startFrame.height)
8696
case .top:
87-
return CGRect(x: 0,
88-
y: 0,
97+
return CGRect(x: edgeInsets.left,
98+
y: edgeInsets.top,
8999
width: startFrame.width,
90100
height: startFrame.height)
91-
92101
}
93102
}
94103

NotificationBanner/Classes/BaseNotificationBanner.swift

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ public class BaseNotificationBanner: UIView {
116116
/// A view that helps the spring animation look nice when the banner appears
117117
internal var spacerView: UIView!
118118

119+
/// The default offset for spacerView top or bottom
120+
internal var spacerViewDefaultOffset: CGFloat = 10.0
121+
119122
/// The default padding between edges and views
120123
internal var padding: CGFloat = 15.0
121124

@@ -134,8 +137,17 @@ public class BaseNotificationBanner: UIView {
134137
/// The position the notification banner should slide in from
135138
private(set) var bannerPosition: BannerPosition!
136139

140+
/// The notification banner sides edges insets from superview. If presented - spacerView color will be transparent
141+
internal(set) var bannerEdgeInsets: UIEdgeInsets? = nil {
142+
didSet {
143+
if bannerEdgeInsets != nil {
144+
spacerView.backgroundColor = .clear
145+
}
146+
}
147+
}
148+
137149
/// Object that stores the start and end frames for the notification banner based on the provided banner position
138-
private var bannerPositionFrame: BannerPositionFrame!
150+
internal var bannerPositionFrame: BannerPositionFrame!
139151

140152
/// The user info that gets passed to each notification
141153
private var notificationUserInfo: [String: BaseNotificationBanner] {
@@ -184,14 +196,13 @@ public class BaseNotificationBanner: UIView {
184196
/**
185197
Creates the proper banner constraints based on the desired banner position
186198
*/
187-
188199
private func createBannerConstraints(for bannerPosition: BannerPosition) {
189200

190201
spacerView.snp.remakeConstraints { (make) in
191202
if bannerPosition == .top {
192-
make.top.equalToSuperview().offset(-10)
203+
make.top.equalToSuperview().offset(-spacerViewDefaultOffset)
193204
} else {
194-
make.bottom.equalToSuperview().offset(10)
205+
make.bottom.equalToSuperview().offset(spacerViewDefaultOffset)
195206
}
196207
make.left.equalToSuperview()
197208
make.right.equalToSuperview()
@@ -216,11 +227,8 @@ public class BaseNotificationBanner: UIView {
216227
/**
217228
Updates the spacer view height. Specifically used for orientation changes.
218229
*/
219-
220230
private func updateSpacerViewHeight(make: ConstraintMaker? = nil) {
221-
let finalHeight = NotificationBannerUtilities.isNotchFeaturedIPhone()
222-
&& UIApplication.shared.statusBarOrientation.isPortrait
223-
&& (parentViewController?.navigationController?.isNavigationBarHidden ?? true) ? 40.0 : 10.0
231+
let finalHeight = spacerViewHeight()
224232
if let make = make {
225233
make.height.equalTo(finalHeight)
226234
} else {
@@ -230,6 +238,12 @@ public class BaseNotificationBanner: UIView {
230238
}
231239
}
232240

241+
internal func spacerViewHeight() -> CGFloat {
242+
return NotificationBannerUtilities.isNotchFeaturedIPhone()
243+
&& UIApplication.shared.statusBarOrientation.isPortrait
244+
&& (parentViewController?.navigationController?.isNavigationBarHidden ?? true) ? 40.0 : 10.0
245+
}
246+
233247
/**
234248
Dismisses the NotificationBanner and shows the next one if there is one to show on the queue
235249
*/
@@ -315,7 +329,8 @@ public class BaseNotificationBanner: UIView {
315329
bannerPositionFrame = BannerPositionFrame(bannerPosition: bannerPosition,
316330
bannerWidth: appWindow.frame.width,
317331
bannerHeight: bannerHeight,
318-
maxY: maximumYPosition())
332+
maxY: maximumYPosition(),
333+
edgeInsets: bannerEdgeInsets)
319334
}
320335

321336
NotificationCenter.default.removeObserver(self,
@@ -408,22 +423,32 @@ public class BaseNotificationBanner: UIView {
408423
}
409424
}
410425

426+
/**
427+
Update banner height, it's necessary after banner labels font update
428+
*/
429+
internal func updateBannerHeight() {
430+
onOrientationChanged()
431+
}
432+
411433
/**
412434
Changes the frame of the notification banner when the orientation of the device changes
413435
*/
414436
@objc private dynamic func onOrientationChanged() {
415437
updateSpacerViewHeight()
416438

417-
let newY = (bannerPosition == .top) ? (frame.origin.y) : (appWindow.frame.height - bannerHeight)
439+
let edgeInsets = bannerEdgeInsets ?? .zero
440+
441+
let newY = (bannerPosition == .top) ? (frame.origin.y) : (appWindow.frame.height - bannerHeight + edgeInsets.top - edgeInsets.bottom)
418442
frame = CGRect(x: frame.origin.x,
419443
y: newY,
420-
width: appWindow.frame.width,
444+
width: appWindow.frame.width - edgeInsets.left - edgeInsets.right,
421445
height: bannerHeight)
422446

423447
bannerPositionFrame = BannerPositionFrame(bannerPosition: bannerPosition,
424448
bannerWidth: appWindow.frame.width,
425449
bannerHeight: bannerHeight,
426-
maxY: maximumYPosition())
450+
maxY: maximumYPosition(),
451+
edgeInsets: bannerEdgeInsets)
427452
}
428453

429454
/**
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
3+
The MIT License (MIT)
4+
Copyright (c) 2018 Denis Kozhukhov
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
7+
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
8+
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11+
12+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
13+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
14+
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
15+
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
17+
*/
18+
19+
import UIKit
20+
import SnapKit
21+
22+
public class FloatGrowingNotificationBanner: GrowingNotificationBanner {
23+
24+
public init(title: String? = nil,
25+
subtitle: String? = nil,
26+
titleFont: UIFont? = nil,
27+
titleColor: UIColor? = nil,
28+
titleTextAlign: NSTextAlignment? = nil,
29+
subtitleFont: UIFont? = nil,
30+
subtitleColor: UIColor? = nil,
31+
subtitleTextAlign: NSTextAlignment? = nil,
32+
leftView: UIView? = nil,
33+
rightView: UIView? = nil,
34+
style: BannerStyle = .info,
35+
colors: BannerColorsProtocol? = nil,
36+
iconPosition: IconPosition = .center) {
37+
38+
super.init(title: title, subtitle: subtitle, leftView: leftView, rightView: rightView, style: style, colors: colors, iconPosition: iconPosition)
39+
40+
if let titleFont = titleFont {
41+
self.titleFont = titleFont
42+
titleLabel!.font = titleFont
43+
}
44+
45+
if let titleColor = titleColor {
46+
titleLabel!.textColor = titleColor
47+
}
48+
49+
if let titleTextAlign = titleTextAlign {
50+
titleLabel!.textAlignment = titleTextAlign
51+
}
52+
53+
if let subtitleFont = subtitleFont {
54+
self.subtitleFont = subtitleFont
55+
subtitleLabel!.font = subtitleFont
56+
}
57+
58+
if let subtitleColor = subtitleColor {
59+
subtitleLabel!.textColor = subtitleColor
60+
}
61+
62+
if let subtitleTextAlign = subtitleTextAlign {
63+
subtitleLabel!.textAlignment = subtitleTextAlign
64+
}
65+
}
66+
67+
/**
68+
Convenience function to display banner with non .zero default edge insets
69+
*/
70+
public func show(queuePosition: QueuePosition = .back,
71+
bannerPosition: BannerPosition = .top,
72+
queue: NotificationBannerQueue = NotificationBannerQueue.default,
73+
on viewController: UIViewController? = nil,
74+
edgeInsets: UIEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8),
75+
cornerRadius: CGFloat? = nil,
76+
shadowColor: UIColor = .black,
77+
shadowOpacity: CGFloat = 1,
78+
shadowBlurRadius: CGFloat = 0,
79+
shadowCornerRadius: CGFloat = 0,
80+
shadowOffset: UIOffset = .zero,
81+
shadowEdgeInsets: UIEdgeInsets? = nil) {
82+
83+
self.bannerEdgeInsets = edgeInsets
84+
85+
if let cornerRadius = cornerRadius {
86+
contentView.layer.cornerRadius = cornerRadius
87+
}
88+
89+
show(queuePosition: queuePosition,
90+
bannerPosition: bannerPosition,
91+
queue: queue,
92+
on: viewController)
93+
94+
applyShadow(withColor: shadowColor,
95+
opacity: shadowOpacity,
96+
blurRadius: shadowBlurRadius,
97+
cornerRadius: shadowCornerRadius,
98+
offset: shadowOffset,
99+
edgeInsets: shadowEdgeInsets)
100+
}
101+
102+
required public init?(coder aDecoder: NSCoder) {
103+
fatalError("init(coder:) has not been implemented")
104+
}
105+
106+
}
107+
108+
private extension FloatGrowingNotificationBanner {
109+
110+
/**
111+
Add shadow for notification with specified parameters.
112+
*/
113+
private func applyShadow(withColor color: UIColor = .black,
114+
opacity: CGFloat = 1,
115+
blurRadius: CGFloat = 0,
116+
cornerRadius: CGFloat = 0,
117+
offset: UIOffset = .zero,
118+
edgeInsets: UIEdgeInsets? = nil) {
119+
120+
contentView.layer.shadowColor = color.cgColor
121+
contentView.layer.shadowOpacity = Float(opacity)
122+
contentView.layer.shadowRadius = blurRadius
123+
contentView.layer.shadowOffset = CGSize(width: offset.horizontal, height: offset.vertical)
124+
125+
if let edgeInsets = edgeInsets {
126+
var shadowRect = CGRect(origin: .zero, size: bannerPositionFrame.startFrame.size)
127+
shadowRect.size.height -= (spacerViewHeight() - spacerViewDefaultOffset) // to proper handle spacer height affects
128+
shadowRect.origin.x += edgeInsets.left
129+
shadowRect.origin.y += edgeInsets.top
130+
shadowRect.size.width -= (edgeInsets.left + edgeInsets.right)
131+
shadowRect.size.height -= (edgeInsets.top + edgeInsets.bottom)
132+
contentView.layer.shadowPath = UIBezierPath(roundedRect: shadowRect, cornerRadius: cornerRadius).cgPath
133+
}
134+
}
135+
136+
}

0 commit comments

Comments
 (0)