Skip to content

Commit 27f94ed

Browse files
committed
Add interactive background dim capability
1 parent b35a32f commit 27f94ed

11 files changed

+191
-62
lines changed
Binary file not shown.

Demo/Demo/Base.lproj/Main.storyboard

+2-2
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,12 @@
203203
<nil key="highlightedColor"/>
204204
</label>
205205
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" apportionsSegmentWidthsByContent="YES" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="nng-9a-v3B">
206-
<rect key="frame" x="18" y="29" width="287" height="29"/>
206+
<rect key="frame" x="18" y="29" width="288" height="29"/>
207207
<segments>
208208
<segment title="Automatic"/>
209209
<segment title="Forever"/>
210210
<segment title="1 Second"/>
211-
<segment title="2 Seconds"/>
211+
<segment title="5 Seconds"/>
212212
</segments>
213213
</segmentedControl>
214214
</subviews>

Demo/Demo/ExploreViewController.swift

+12-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class ExploreViewController: UITableViewController, UITextFieldDelegate {
2525
view = try! MessageView.viewFromNib()
2626
}
2727

28-
view.configureContent(title: titleText.text, body: bodyText.text, iconImage: nil, iconText: nil, buttonImage: nil, buttonTitle: "Hide", buttonHandler: { SwiftMessages.hide() })
28+
view.configureContent(title: titleText.text, body: bodyText.text, iconImage: nil, iconText: nil, buttonImage: nil, buttonTitle: "Hide", buttonTapHandler: { _ in SwiftMessages.hide() })
2929

3030
switch theme.selectedSegmentIndex {
3131
case 0:
@@ -82,13 +82,22 @@ class ExploreViewController: UITableViewController, UITextFieldDelegate {
8282
break
8383
}
8484

85-
switch duration {
85+
switch duration.selectedSegmentIndex {
8686
case 1:
8787
config.duration = .Forever
8888
case 2:
8989
config.duration = .Seconds(seconds: 1)
9090
case 3:
91-
config.duration = .Seconds(seconds: 2)
91+
config.duration = .Seconds(seconds: 5)
92+
default:
93+
break
94+
}
95+
96+
switch dimMode.selectedSegmentIndex {
97+
case 1:
98+
config.dimMode = .Automatic(interactive: false)
99+
case 2:
100+
config.dimMode = .Automatic(interactive: true)
92101
default:
93102
break
94103
}

Demo/Demo/TacoDialogView.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ class TacoDialogView: MessageView {
3838

3939
@IBOutlet weak var tacoSlider: UISlider!
4040

41-
@IBAction func tacoSliderSlided(slider: UISlider) {
41+
@IBAction func tacoSliderSlid(slider: UISlider) {
4242
count = Int(slider.value)
4343
}
44+
45+
@IBAction func tacoSliderFinished(slider: UISlider) {
46+
slider.setValue(Float(count), animated: true)
47+
}
4448
}

Demo/Demo/TacoDialogView.xib

+3-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@
7676
<rect key="frame" x="-2" y="142" width="516" height="31"/>
7777
<color key="minimumTrackTintColor" red="0.94509803921568625" green="0.57647058823529407" blue="0.078431372549019607" alpha="1" colorSpace="calibratedRGB"/>
7878
<connections>
79-
<action selector="tacoSliderSlided:" destination="JI3-gM-XBO" eventType="valueChanged" id="ABc-u9-Vpu"/>
79+
<action selector="tacoSliderFinished:" destination="JI3-gM-XBO" eventType="touchUpInside" id="ekV-4L-MLR"/>
80+
<action selector="tacoSliderFinished:" destination="JI3-gM-XBO" eventType="touchUpOutside" id="x7e-Y6-N1o"/>
81+
<action selector="tacoSliderSlid:" destination="JI3-gM-XBO" eventType="valueChanged" id="mwN-f7-zaf"/>
8082
</connections>
8183
</slider>
8284
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="bottom" translatesAutoresizingMaskIntoConstraints="NO" id="x8I-RG-nBX">

Demo/Demo/ViewController.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class ViewController: UITableViewController {
1414
var items: [Item] = [
1515
.TitleBody(title: "MESSAGE VIEW", body: "SwiftMessages provides a standard message view along with a number of layouts, themes and presentation options.", function: ViewController.demoBasics),
1616
.TitleBody(title: "ANY VIEW", body: "Any view, no matter how cute, can be displayed as a message.", function: ViewController.demoAnyView),
17-
.TitleBody(title: "CUSTOMIZE", body: "Easily customize by copying one of the SwiftMessages nib files into your project as a starting point. Then order a taco.", function: ViewController.demoCustomNib),
17+
.TitleBody(title: "CUSTOMIZE", body: "Easily customize by copying one of the SwiftMessages nib files into your project as a starting point. Then order some tacos.", function: ViewController.demoCustomNib),
1818
.Explore,
1919
]
2020

@@ -122,6 +122,7 @@ class ViewController: UITableViewController {
122122
config.presentationContext = .Window(windowLevel: UIWindowLevelStatusBar)
123123
config.duration = .Forever
124124
config.presentationStyle = .Bottom
125+
config.dimMode = .Automatic(interactive: true)
125126
SwiftMessages.show(config: config, view: view)
126127
}
127128

Design/SwiftMessagesDesign.sketch

-112 KB
Binary file not shown.

SwiftMessages/MessageView.swift

+74-48
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,30 @@ public class MessageView: DropShadowView, Identifiable, MarginAdjustable {
1919
@IBOutlet public var iconContainer: UIView?
2020
@IBOutlet public var iconImageView: UIImageView?
2121
@IBOutlet public var iconLabel: UILabel?
22-
@IBOutlet public var button: UIButton?
2322
@IBOutlet public var contentView: UIView?
2423
@IBOutlet public var backgroundView: UIView!
2524

25+
@IBOutlet public var button: UIButton? {
26+
didSet {
27+
if let old = oldValue {
28+
old.removeTarget(self, action: #selector(MessageView.buttonTapped(_:)), forControlEvents: .TouchUpInside)
29+
}
30+
if let button = button {
31+
button.addTarget(self, action: #selector(MessageView.buttonTapped(_:)), forControlEvents: .TouchUpInside)
32+
}
33+
}
34+
}
35+
36+
/*
37+
MARK: - Button tap handler
38+
*/
39+
40+
public var buttonTapHandler: ((button: UIButton) -> Void)?
41+
42+
func buttonTapped(button: UIButton) {
43+
buttonTapHandler?(button: button)
44+
}
45+
2646
/*
2747
MARK: - Creating message views
2848
*/
@@ -41,23 +61,60 @@ public class MessageView: DropShadowView, Identifiable, MarginAdjustable {
4161
public static func viewFromNib<T: MessageView>(layout layout: Layout, bundle: NSBundle) -> T {
4262
return try! UIView.viewFromNib(named: layout.rawValue, bundle: bundle)
4363
}
44-
64+
4565
/*
46-
MARK: - Configuring the theme
66+
MARK: - Initialization
4767
*/
68+
69+
public required init?(coder aDecoder: NSCoder) {
70+
super.init(coder: aDecoder)
71+
backgroundView = self
72+
}
73+
74+
public override init(frame: CGRect) {
75+
super.init(frame: frame)
76+
backgroundView = self
77+
}
78+
79+
public override func awakeFromNib() {
80+
layoutMargins = UIEdgeInsetsZero
81+
}
82+
83+
/*
84+
MARK: - Identifiable
85+
*/
86+
87+
public var id: String {
88+
return "MessageView:title=\(titleLabel?.text), body=\(bodyLabel?.text)"
89+
}
90+
91+
/*
92+
MARK: - MarginAdjustable
93+
*/
94+
95+
@IBInspectable public var bounceAnimationOffset: CGFloat = 5.0
96+
97+
@IBInspectable public var statusBarOffset: CGFloat = 20.0
98+
}
4899

100+
/*
101+
MARK: - Configuring the theme
102+
*/
103+
104+
extension MessageView {
105+
49106
public func configureErrorTheme() {
50107
let backgroundColor = UIColor(red: 249.0/255.0, green: 66.0/255.0, blue: 47.0/255.0, alpha: 1.0)
51108
let foregroundColor = UIColor.whiteColor()
52109
configureTheme(backgroundColor: backgroundColor, foregroundColor: foregroundColor, iconImage: Icon.Error.image)
53110
}
54-
111+
55112
public func configureWarningTheme() {
56113
let backgroundColor = UIColor(red: 238.0/255.0, green: 189.0/255.0, blue: 34.0/255.0, alpha: 1.0)
57114
let foregroundColor = UIColor.whiteColor()
58115
configureTheme(backgroundColor: backgroundColor, foregroundColor: foregroundColor, iconImage: Icon.Warning.image)
59116
}
60-
117+
61118
public func configureInfoTheme() {
62119
let backgroundColor = UIColor(red: 225.0/255.0, green: 225.0/255.0, blue: 225.0/255.0, alpha: 1.0)
63120
let foregroundColor = UIColor.darkTextColor()
@@ -78,15 +135,18 @@ public class MessageView: DropShadowView, Identifiable, MarginAdjustable {
78135
button?.contentEdgeInsets = UIEdgeInsetsMake(7.0, 7.0, 7.0, 7.0)
79136
button?.layer.cornerRadius = 5.0
80137
}
81-
138+
82139
public override func configureDropShadow() {
83140
configureDropShadow(backgroundView ?? self)
84141
}
85-
86-
/*
87-
MARK: - Configuring the content
88-
*/
142+
}
143+
144+
/*
145+
MARK: - Configuring the content
146+
*/
89147

148+
extension MessageView {
149+
90150
public func configureContent(body body: String) {
91151
bodyLabel?.text = body
92152
}
@@ -101,54 +161,20 @@ public class MessageView: DropShadowView, Identifiable, MarginAdjustable {
101161
iconImageView?.image = iconImage
102162
iconLabel?.text = nil
103163
}
104-
164+
105165
public func configureContent(title title: String, body: String, iconText: String) {
106166
configureContent(title: title, body: body)
107167
iconImageView?.image = nil
108168
iconLabel?.text = iconText
109169
}
110-
111-
public func configureContent(title title: String?, body: String?, iconImage: UIImage?, iconText: String?, buttonImage: UIImage?, buttonTitle: String?, buttonHandler: (() -> Void)?) {
170+
171+
public func configureContent(title title: String?, body: String?, iconImage: UIImage?, iconText: String?, buttonImage: UIImage?, buttonTitle: String?, buttonTapHandler: ((button: UIButton) -> Void)?) {
112172
titleLabel?.text = title
113173
bodyLabel?.text = body
114174
iconImageView?.image = iconImage
115175
iconLabel?.text = iconText
116176
button?.setImage(buttonImage, forState: .Normal)
117177
button?.setTitle(buttonTitle, forState: .Normal)
118-
// TODO set button tap handler
119-
}
120-
121-
/*
122-
MARK: - Initialization
123-
*/
124-
125-
public required init?(coder aDecoder: NSCoder) {
126-
super.init(coder: aDecoder)
127-
backgroundView = self
128-
}
129-
130-
public override init(frame: CGRect) {
131-
super.init(frame: frame)
132-
backgroundView = self
133-
}
134-
135-
public override func awakeFromNib() {
136-
layoutMargins = UIEdgeInsetsZero
178+
self.buttonTapHandler = buttonTapHandler
137179
}
138-
139-
/*
140-
MARK: - Identifiable
141-
*/
142-
143-
public var id: String {
144-
return "MessageView:title=\(titleLabel?.text), body=\(bodyLabel?.text)"
145-
}
146-
147-
/*
148-
MARK: - MarginAdjustable
149-
*/
150-
151-
@IBInspectable public var bounceAnimationOffset: CGFloat = 5.0
152-
153-
@IBInspectable public var statusBarOffset: CGFloat = 20.0
154180
}

SwiftMessages/PassthroughView.swift

+19-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,26 @@
99
import UIKit
1010

1111
class PassthroughView: UIView {
12+
13+
var tapRecognizer: UITapGestureRecognizer?
14+
15+
var tappedHander: (() -> Void)? {
16+
didSet {
17+
if let tap = tapRecognizer {
18+
removeGestureRecognizer(tap)
19+
}
20+
if tappedHander == nil { return }
21+
let tap = UITapGestureRecognizer(target: self, action: #selector(PassthroughView.tapped))
22+
addGestureRecognizer(tap)
23+
}
24+
}
25+
26+
@objc func tapped() {
27+
tappedHander?()
28+
}
29+
1230
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
1331
let view = super.hitTest(point, withEvent: event)
14-
return view == self ? nil : view
32+
return view == self && tappedHander == nil ? nil : view
1533
}
1634
}

SwiftMessages/Presenter.swift

+48-1
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,23 @@ class Weak<T: AnyObject> {
1313
init() {}
1414
}
1515

16+
protocol PresenterDelegate: class {
17+
func hide(presenter presenter: Presenter)
18+
}
19+
1620
class Presenter {
1721

1822
let config: SwiftMessages.Config
1923
let view: UIView
24+
weak var delgate: PresenterDelegate?
2025
let maskingView = PassthroughView()
2126
let presentationContext = Weak<UIViewController>()
2227
var translationConstraint: NSLayoutConstraint! = nil
2328

24-
init(config: SwiftMessages.Config, view: UIView) {
29+
init(config: SwiftMessages.Config, view: UIView, delegate: PresenterDelegate) {
2530
self.config = config
2631
self.view = view
32+
self.delgate = delegate
2733
maskingView.clipsToBounds = true
2834
}
2935

@@ -154,6 +160,7 @@ class Presenter {
154160
}
155161

156162
func showAnimation(completion completion: (completed: Bool) -> Void) {
163+
157164
switch config.presentationStyle {
158165
case .Top, .Bottom:
159166
UIView.animateWithDuration(0.4, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: [.BeginFromCurrentState, .CurveLinear, .AllowUserInteraction], animations: {
@@ -167,6 +174,31 @@ class Presenter {
167174
completion(completed: completed)
168175
})
169176
}
177+
178+
func dim(color: UIColor) {
179+
self.maskingView.backgroundColor = UIColor.clearColor()
180+
UIView.animateWithDuration(0.2, animations: {
181+
self.maskingView.backgroundColor = color
182+
})
183+
}
184+
185+
func setupInteractive() {
186+
maskingView.tappedHander = { [weak self] in
187+
guard let strongSelf = self else { return }
188+
self?.delgate?.hide(presenter: strongSelf)
189+
}
190+
}
191+
192+
switch config.dimMode {
193+
case .None:
194+
break
195+
case .Automatic(let interactive):
196+
dim(UIColor(white: 0, alpha: 0.3))
197+
if interactive { setupInteractive() }
198+
case .Color(let color, let interactive):
199+
dim(color)
200+
if interactive { setupInteractive() }
201+
}
170202
}
171203

172204
func hide(completion completion: (completed: Bool) -> Void) {
@@ -184,5 +216,20 @@ class Presenter {
184216
completion(completed: completed)
185217
})
186218
}
219+
220+
func undim() {
221+
UIView.animateWithDuration(0.2, animations: {
222+
self.maskingView.backgroundColor = UIColor.clearColor()
223+
})
224+
}
225+
226+
switch config.dimMode {
227+
case .None:
228+
break
229+
case .Automatic:
230+
undim()
231+
case .Color:
232+
undim()
233+
}
187234
}
188235
}

0 commit comments

Comments
 (0)