-
-
Notifications
You must be signed in to change notification settings - Fork 21
/
InnerAlertController.swift
259 lines (218 loc) · 8.04 KB
/
InnerAlertController.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//
// InnerAlertController.swift
// Alertift
//
// Created by Suguru Kishimoto on 4/27/17.
// Copyright © 2017 Suguru Kishimoto. All rights reserved.
//
import Foundation
import UIKit
extension Alertift {
public enum ImageTopMargin {
case none
case belowRoundCorner
var margin: CGFloat {
switch self {
case .none: return 0.0
case .belowRoundCorner: return 16.0
}
}
}
}
/// InnerAlertController
/// subclass of **UIAlertController**
class InnerAlertController: UIAlertController {
typealias AdjustInfo = (lineCount: Int, height: CGFloat)
private(set) var originalTitle: String?
private var spaceAdjustedTitle: String = ""
private(set) var originalMessage: String?
private var spaceAdjustedMessage: String = ""
private var imageView: UIImageView? = nil
private var previousImgViewSize: CGSize = .zero
private var imageTopMargin: Alertift.ImageTopMargin = .none
override var title: String? {
didSet {
if title != spaceAdjustedTitle {
originalTitle = title
}
}
}
override var message: String? {
didSet {
if message != spaceAdjustedMessage {
originalMessage = message
}
}
}
public func setImage(_ image: UIImage?, imageTopMargin: Alertift.ImageTopMargin = .none) {
if let _ = image, title == nil {
title = " "
}
self.imageTopMargin = imageTopMargin
guard let imageView = self.imageView else {
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
self.imageView = imageView
return
}
imageView.image = image
imageView.frame.size = image?.size ?? .zero
}
// MARK: - Layout code
override func viewDidLayoutSubviews() {
guard let imageView = imageView, let _ = imageView.image else {
super.viewDidLayoutSubviews()
return
}
adjustImageViewPosition()
super.viewDidLayoutSubviews()
}
private func adjustImageViewPosition() {
guard let imageView = imageView, let image = imageView.image else {
return
}
if imageView.superview == nil {
mainView?.addSubview(imageView)
}
if imageView.frame.width > view.frame.width {
imageView.frame.size.width = view.frame.width
imageView.frame.size.height = imageView.frame.size.width * image.size.height / image.size.width
}
// Adjust title if image size has changed
if previousImgViewSize != imageView.bounds.size {
previousImgViewSize = imageView.bounds.size
adjustLabel(for: imageView)
imageView.center.x = view.bounds.width / 2.0
imageView.frame.origin.y = imageTopMargin.margin
}
}
private func adjustLabel(for imageView: UIImageView) {
if let label = titleLabel {
let lineCount = getLineCount(for: imageView, label: label)
let lines = String(repeating: "\n", count: lineCount)
spaceAdjustedTitle = lines + (originalTitle ?? "")
title = spaceAdjustedTitle
} else if let label = messageLabel {
let lineCount = getLineCount(for: imageView, label: label)
let lines = String(repeating: "\n", count: lineCount)
spaceAdjustedMessage = lines + (originalMessage ?? "")
message = spaceAdjustedMessage
}
}
private func getLineCount(for imageView: UIImageView, label: UILabel?) -> Int {
guard let label = label else {
return 0
}
let _label = UILabel(frame: .zero)
_label.font = label.font
_label.text = ""
_label.numberOfLines = 0
var lineCount = 1
while _label.frame.height < imageView.frame.height {
_label.text = _label.text.map { $0 + "\n" }
_label.sizeToFit()
lineCount += 1
}
return lineCount
}
private lazy var lineHeight: CGFloat = {
return titleLabel?.font.lineHeight ?? messageLabel?.font.lineHeight ?? 1.0
}()
/// textFieldTextDidChangeHandler: ((UITextField, Int) -> Void)
var textFieldTextDidChangeHandler: Alertift.Alert.TextFieldHandler?
public typealias FinallyHandler = (UIAlertAction, Int, [UITextField]?) -> Void
var finallyHandler: FinallyHandler?
var alertBackgroundColor: UIColor?
var titleTextColor: UIColor? = .black
var messageTextColor: UIColor? = .black
var titleTextAlignment: NSTextAlignment = .center
var messageTextAlignment: NSTextAlignment = .center
/// Register UITextFieldTextDidChange notification
///
/// - Parameter textField: textField
func registerTextFieldObserver(_ textField: UITextField) {
NotificationCenter.default.addObserver(
self,
selector: #selector(self.textFieldDidChange(_:)),
name: UITextField.textDidChangeNotification, object: textField
)
}
/// Delegate method for UITextFieldTextDidChange
///
/// - Parameter notification: notification (object is UITextField)
@objc private func textFieldDidChange(_ notification: Notification) {
guard let textField = notification.object as? UITextField else {
return
}
guard let index = textFields?.firstIndex(of: textField) else {
return
}
textFieldTextDidChangeHandler?(textField, index)
}
/// Returns actionHandler
var actionHandler: (UIAlertAction) -> (UIAlertAction, Int) {
return { [weak self] action in (action, self?.actions.firstIndex(of: action) ?? -1) }
}
/// Returns actionWithTextFieldsHandler
var actionWithTextFieldsHandler: (UIAlertAction) -> (UIAlertAction, Int, [UITextField]?) {
return { [weak self] action in (action, self?.actions.firstIndex(of: action) ?? -1, self?.textFields) }
}
/// Returns finallyExecutor
var finallyExecutor: (UIAlertAction) -> Void {
return { [weak self] action in
self?.finallyHandler?(action, self?.actions.firstIndex(of: action) ?? -1, self?.textFields)
}
}
override public func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
adaptBackgroundColor()
updateTitleLabel()
updateMessageLabel()
}
private func adaptBackgroundColor() {
mainView?.backgroundColor = alertBackgroundColor
if preferredStyle == .actionSheet {
if let cancelTitle = actions.filter({ $0.style == .cancel }).first?.title {
if let cancelButton = searchLabel(from: cancelTitle )?.superview?.superview {
cancelButton.backgroundColor = alertBackgroundColor
}
}
}
}
var titleLabel: UILabel? {
return title.flatMap(searchLabel(from:))
}
var messageLabel: UILabel? {
return message.flatMap(searchLabel(from:))
}
private func updateTitleLabel() {
if let titleLabel = titleLabel {
titleLabel.textColor = titleTextColor
titleLabel.textAlignment = titleTextAlignment
}
}
private func updateMessageLabel() {
if let messageLabel = messageLabel {
messageLabel.textColor = messageTextColor
messageLabel.textAlignment = messageTextAlignment
}
}
private var mainView: UIView? {
return view.subviews.first?.subviews.first?.subviews.first
}
private func searchLabel(from text: String) -> UILabel? {
return view.recursiveSubviews
.compactMap { $0 as? UILabel}
.first { $0.text == text }
}
deinit {
NotificationCenter.default.removeObserver(self)
Debug.log()
}
}
extension UIView {
var recursiveSubviews: [UIView] {
return subviews.reduce(subviews, { return $0 + $1.recursiveSubviews })
}
}