@bm-fe/react-native-ui-components 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Bitmart Card.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Bitmart Card.imageset/Property 1=Bitmart Card.svg +3 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Contents.json +6 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Credit Debit Card.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Credit Debit Card.imageset/Property 1=Credit Debit Card.svg +3 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Crypto Prepaid Card.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Crypto Prepaid Card.imageset/Property 1=Crypto Prepaid Card.svg +3 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Mobile Recharge.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Mobile Recharge.imageset/Mobile Recharge.svg +3 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/P2P Trading.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/P2P Trading.imageset/Property 1=P2P Trading.svg +3 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/SEPA Deposit.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/SEPA Deposit.imageset/SEPA Deposit.svg +3 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Third-Party Payment.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Third-Party Payment.imageset/Property 1=Third-Party Payment.svg +3 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/Contents.json +6 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Contents.json +6 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/checkmark.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/checkmark.imageset/checkmark.pdf +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/close_icon.imageset/Contents.json +22 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/close_icon.imageset/close_icon@2x.png +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/close_icon.imageset/close_icon@3x.png +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/cross.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/cross.imageset/cross.pdf +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress.imageset/progress.pdf +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress_circular.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress_circular.imageset/progress_circular.pdf +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_dark.imageset/Contents.json +22 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_dark.imageset/refresh_footer_dark@2x.png +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_dark.imageset/refresh_footer_dark@3x.png +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_light.imageset/Contents.json +22 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_light.imageset/refresh_footer_light@2x.png +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_light.imageset/refresh_footer_light@3x.png +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/search_icon.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/search_icon.imageset/Group 13994.svg +7 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_dark_chose.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_dark_chose.imageset/Frame.svg +3 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_arrow.imageset/Contents.json +22 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_arrow.imageset/sheet_list_arrow@2x.png +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_arrow.imageset/sheet_list_arrow@3x.png +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_cell_checkbox.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_cell_checkbox.imageset/Frame (1).svg +3 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_chose.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_chose.imageset/Frame.png +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/slider_bubble.imageset/Contents.json +22 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/slider_bubble.imageset/slider_bubble@2x.png +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/slider_bubble.imageset/slider_bubble@3x.png +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/spot_second_floor_refresh_arrow.imageset/Contents.json +21 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/spot_second_floor_refresh_arrow.imageset/spot_second_floor_refresh_arrow.svg +8 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Font/Alexandria-Medium.ttf +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Font/Alexandria-Regular.ttf +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Font/Alexandria-SemiBold.ttf +0 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Classes/BMFont/BMFont.swift +82 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Classes/BMFont/UIFontExtensions.swift +120 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/AlertView/BMComponentAlertController.swift +574 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Buttons/BMComponentButton+Examples.swift +77 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Buttons/BMComponentButton.swift +373 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Buttons/BMComponentButtonConfiguration.swift +181 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Popup/BMComponentPopupController.swift +312 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/SegmentView/BMComponentSegmentedTitleCell.swift +294 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/SegmentView/BMComponentSegmentedTitleDataSource.swift +49 -0
- package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/SegmentView/BMComponentSegmentedView.swift +292 -0
- package/ios/Modules/BMUIComponents/LICENSE +19 -0
- package/ios/Modules/BMUIComponents/README.md +29 -0
- package/package.json +3 -1
- package/react-native-ui-components.podspec +33 -4
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
//
|
|
2
|
+
// BMComponentAlertController.swift
|
|
3
|
+
// BMComponents
|
|
4
|
+
//
|
|
5
|
+
// Created by james on 2025/12/31.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import UIKit
|
|
9
|
+
import BMTheme
|
|
10
|
+
import BMCore
|
|
11
|
+
import SnapKit
|
|
12
|
+
|
|
13
|
+
// MARK: - Button Style 按钮风格
|
|
14
|
+
public enum BMComponentAlertButtonStyle {
|
|
15
|
+
/// 主要按钮:渐变色(绿色到亮绿色)
|
|
16
|
+
case primaryGradient
|
|
17
|
+
/// 次要按钮:深绿色背景
|
|
18
|
+
case secondary
|
|
19
|
+
/// 文本按钮:纯文本,无背景
|
|
20
|
+
case text
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// MARK: - Button Layout 按钮布局方向
|
|
24
|
+
public enum BMComponentAlertButtonLayout {
|
|
25
|
+
/// 水平排列(左右)
|
|
26
|
+
case horizontal
|
|
27
|
+
/// 垂直排列(上下)
|
|
28
|
+
case vertical
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// MARK: - Button Action 按钮动作
|
|
32
|
+
public class BMComponentAlertAction {
|
|
33
|
+
public let title: String
|
|
34
|
+
public let style: BMComponentAlertButtonStyle
|
|
35
|
+
public var handler: ((BMComponentAlertAction) -> Void)?
|
|
36
|
+
|
|
37
|
+
public init(title: String, style: BMComponentAlertButtonStyle, handler: ((BMComponentAlertAction) -> Void)? = nil) {
|
|
38
|
+
self.title = title
|
|
39
|
+
self.style = style
|
|
40
|
+
self.handler = handler
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// MARK: - BMComponentAlertController 主类
|
|
45
|
+
open class BMComponentAlertController: UIViewController {
|
|
46
|
+
|
|
47
|
+
// MARK: - Constants
|
|
48
|
+
private struct Constants {
|
|
49
|
+
static let maxContainerWidth = UIScreen.main.bounds.size.width - 38 * 2
|
|
50
|
+
static let maxContainerHeight = UIScreen.main.bounds.size.height * 0.6
|
|
51
|
+
static let buttonHeight: CGFloat = 44
|
|
52
|
+
static let buttonSpacing: CGFloat = 12
|
|
53
|
+
static let horizontalPadding: CGFloat = 20
|
|
54
|
+
static let verticalPadding: CGFloat = 24
|
|
55
|
+
static let titleTopPadding: CGFloat = 20
|
|
56
|
+
static let titleMessageSpacing: CGFloat = 12
|
|
57
|
+
static let messageButtonSpacing: CGFloat = 20
|
|
58
|
+
static let imageSize: CGFloat = 100
|
|
59
|
+
static let imageTopPadding: CGFloat = 24
|
|
60
|
+
static let imageTitleSpacing: CGFloat = 16
|
|
61
|
+
static let cornerRadius: CGFloat = 16
|
|
62
|
+
static let closeButtonSize: CGFloat = 24
|
|
63
|
+
static let progressLabelHeight: CGFloat = 20
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// MARK: - Properties
|
|
67
|
+
public var enableDimmingViewTap = true
|
|
68
|
+
public var needShowCloseBtn = true {
|
|
69
|
+
didSet {
|
|
70
|
+
closeBtn.isHidden = !needShowCloseBtn
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// 按钮布局方向,默认为自动
|
|
75
|
+
public var buttonLayout: BMComponentAlertButtonLayout = .horizontal {
|
|
76
|
+
didSet {
|
|
77
|
+
if isViewLoaded {
|
|
78
|
+
setupButtons()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private let titleText: String?
|
|
84
|
+
private let messageText: String?
|
|
85
|
+
private let image: UIImage?
|
|
86
|
+
private let progressText: String?
|
|
87
|
+
private var actions: [BMComponentAlertAction] = []
|
|
88
|
+
|
|
89
|
+
// MARK: - UI Components
|
|
90
|
+
private lazy var bgButton: UIButton = {
|
|
91
|
+
let button = UIButton(type: .custom)
|
|
92
|
+
button.addTarget(self, action: #selector(bgButtonClick), for: .touchUpInside)
|
|
93
|
+
return button
|
|
94
|
+
}()
|
|
95
|
+
|
|
96
|
+
private lazy var containerView: UIView = {
|
|
97
|
+
let view = UIView()
|
|
98
|
+
view.cexTheme.backgroundColor = .drawerBgColor
|
|
99
|
+
view.layer.cornerRadius = Constants.cornerRadius
|
|
100
|
+
view.clipsToBounds = true
|
|
101
|
+
view.layer.cexTheme.borderColor = .borderColor
|
|
102
|
+
view.layer.borderWidth = 1
|
|
103
|
+
return view
|
|
104
|
+
}()
|
|
105
|
+
|
|
106
|
+
private lazy var closeBtn: UIButton = {
|
|
107
|
+
let btn = UIButton()
|
|
108
|
+
btn.setImage(UIImage(named: "fun_close")?.withTintColor(BMCexThemeColor.primaryColor.currentUIColor()), for: .normal)
|
|
109
|
+
btn.addTarget(self, action: #selector(closeAction), for: .touchUpInside)
|
|
110
|
+
return btn
|
|
111
|
+
}()
|
|
112
|
+
|
|
113
|
+
private lazy var imageView: UIImageView = {
|
|
114
|
+
let imageView = UIImageView()
|
|
115
|
+
imageView.contentMode = .scaleAspectFit
|
|
116
|
+
imageView.clipsToBounds = true
|
|
117
|
+
return imageView
|
|
118
|
+
}()
|
|
119
|
+
|
|
120
|
+
private lazy var titleLabel: UILabel = {
|
|
121
|
+
let label = UILabel()
|
|
122
|
+
label.cexTheme.textColor = .primaryColor
|
|
123
|
+
label.font = UIFont.S1Font
|
|
124
|
+
label.numberOfLines = 2
|
|
125
|
+
label.textAlignment = .center
|
|
126
|
+
return label
|
|
127
|
+
}()
|
|
128
|
+
|
|
129
|
+
private lazy var messageLabel: UILabel = {
|
|
130
|
+
let label = UILabel()
|
|
131
|
+
label.cexTheme.textColor = .primaryColor
|
|
132
|
+
label.font = UIFont.B2Font
|
|
133
|
+
label.numberOfLines = 0
|
|
134
|
+
label.textAlignment = .center
|
|
135
|
+
return label
|
|
136
|
+
}()
|
|
137
|
+
|
|
138
|
+
private lazy var progressLabel: UILabel = {
|
|
139
|
+
let label = UILabel()
|
|
140
|
+
label.cexTheme.textColor = .primaryColor
|
|
141
|
+
label.font = UIFont.B2Font
|
|
142
|
+
label.textAlignment = .center
|
|
143
|
+
return label
|
|
144
|
+
}()
|
|
145
|
+
|
|
146
|
+
private lazy var buttonStack: UIStackView = {
|
|
147
|
+
let stack = UIStackView()
|
|
148
|
+
stack.axis = .vertical
|
|
149
|
+
stack.spacing = Constants.buttonSpacing
|
|
150
|
+
stack.distribution = .fillEqually
|
|
151
|
+
return stack
|
|
152
|
+
}()
|
|
153
|
+
|
|
154
|
+
// 存储渐变按钮和对应的渐变层
|
|
155
|
+
private var gradientButtons: [UIButton: CAGradientLayer] = [:]
|
|
156
|
+
|
|
157
|
+
// MARK: - Initialization
|
|
158
|
+
public init(title: String? = nil,
|
|
159
|
+
message: String? = nil,
|
|
160
|
+
image: UIImage? = nil,
|
|
161
|
+
progressText: String? = nil) {
|
|
162
|
+
self.titleText = title
|
|
163
|
+
self.messageText = message
|
|
164
|
+
self.image = image
|
|
165
|
+
self.progressText = progressText
|
|
166
|
+
super.init(nibName: nil, bundle: nil)
|
|
167
|
+
self.modalPresentationStyle = .custom
|
|
168
|
+
// self.transitioningDelegate = self
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
required public init?(coder: NSCoder) {
|
|
172
|
+
fatalError("init(coder:) has not been implemented")
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// MARK: - Lifecycle
|
|
176
|
+
open override func viewDidLoad() {
|
|
177
|
+
super.viewDidLoad()
|
|
178
|
+
bgButton.isEnabled = enableDimmingViewTap
|
|
179
|
+
closeBtn.isHidden = !needShowCloseBtn
|
|
180
|
+
configUI()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// MARK: - UI Configuration
|
|
184
|
+
private func configUI() {
|
|
185
|
+
// 背景
|
|
186
|
+
view.backgroundColor = UIColor.black.withAlphaComponent(0.7)
|
|
187
|
+
view.addSubview(bgButton)
|
|
188
|
+
view.addSubview(containerView)
|
|
189
|
+
// 关闭按钮
|
|
190
|
+
containerView.addSubview(closeBtn)
|
|
191
|
+
closeBtn.snp.makeConstraints { make in
|
|
192
|
+
make.top.equalToSuperview().offset(20)
|
|
193
|
+
make.trailing.equalToSuperview().offset(-20)
|
|
194
|
+
make.width.height.equalTo(Constants.closeButtonSize)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 图片
|
|
198
|
+
if let image = image {
|
|
199
|
+
containerView.addSubview(imageView)
|
|
200
|
+
imageView.image = image
|
|
201
|
+
imageView.snp.makeConstraints { make in
|
|
202
|
+
make.top.equalToSuperview().offset(Constants.imageTopPadding)
|
|
203
|
+
make.centerX.equalToSuperview()
|
|
204
|
+
make.width.height.equalTo(Constants.imageSize)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 标题
|
|
209
|
+
if let title = titleText {
|
|
210
|
+
containerView.addSubview(titleLabel)
|
|
211
|
+
titleLabel.text = title
|
|
212
|
+
titleLabel.snp.makeConstraints { make in
|
|
213
|
+
if image != nil {
|
|
214
|
+
make.top.equalTo(imageView.snp.bottom).offset(Constants.imageTitleSpacing)
|
|
215
|
+
} else {
|
|
216
|
+
make.top.equalToSuperview().offset(Constants.titleTopPadding)
|
|
217
|
+
}
|
|
218
|
+
make.leading.equalToSuperview().offset(Constants.horizontalPadding)
|
|
219
|
+
make.trailing.equalToSuperview().offset(-Constants.horizontalPadding)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// 描述
|
|
224
|
+
if let message = messageText {
|
|
225
|
+
containerView.addSubview(messageLabel)
|
|
226
|
+
messageLabel.text = message
|
|
227
|
+
messageLabel.snp.makeConstraints { make in
|
|
228
|
+
if titleText != nil {
|
|
229
|
+
make.top.equalTo(titleLabel.snp.bottom).offset(Constants.titleMessageSpacing)
|
|
230
|
+
} else if image != nil {
|
|
231
|
+
make.top.equalTo(imageView.snp.bottom).offset(Constants.imageTitleSpacing)
|
|
232
|
+
} else {
|
|
233
|
+
make.top.equalToSuperview().offset(Constants.titleTopPadding)
|
|
234
|
+
}
|
|
235
|
+
make.leading.equalToSuperview().offset(Constants.horizontalPadding)
|
|
236
|
+
make.trailing.equalToSuperview().offset(-Constants.horizontalPadding)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// 进度指示器
|
|
241
|
+
if let progress = progressText {
|
|
242
|
+
containerView.addSubview(progressLabel)
|
|
243
|
+
progressLabel.text = progress
|
|
244
|
+
progressLabel.snp.makeConstraints { make in
|
|
245
|
+
if messageText != nil {
|
|
246
|
+
make.top.equalTo(messageLabel.snp.bottom).offset(8)
|
|
247
|
+
} else if titleText != nil {
|
|
248
|
+
make.top.equalTo(titleLabel.snp.bottom).offset(Constants.titleMessageSpacing)
|
|
249
|
+
} else {
|
|
250
|
+
make.top.equalToSuperview().offset(Constants.titleTopPadding)
|
|
251
|
+
}
|
|
252
|
+
make.leading.equalToSuperview().offset(Constants.horizontalPadding)
|
|
253
|
+
make.trailing.equalToSuperview().offset(-Constants.horizontalPadding)
|
|
254
|
+
make.height.equalTo(Constants.progressLabelHeight)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 按钮
|
|
259
|
+
containerView.addSubview(buttonStack)
|
|
260
|
+
setupButtons()
|
|
261
|
+
|
|
262
|
+
// 布局约束
|
|
263
|
+
bgButton.snp.makeConstraints { make in
|
|
264
|
+
make.edges.equalToSuperview()
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
containerView.snp.makeConstraints { make in
|
|
268
|
+
make.center.equalToSuperview()
|
|
269
|
+
make.width.equalTo(Constants.maxContainerWidth)
|
|
270
|
+
make.width.lessThanOrEqualTo(Constants.maxContainerWidth)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
buttonStack.snp.makeConstraints { make in
|
|
274
|
+
let topView: UIView
|
|
275
|
+
if progressText != nil {
|
|
276
|
+
topView = progressLabel
|
|
277
|
+
} else if messageText != nil {
|
|
278
|
+
topView = messageLabel
|
|
279
|
+
} else if titleText != nil {
|
|
280
|
+
topView = titleLabel
|
|
281
|
+
} else if image != nil {
|
|
282
|
+
topView = imageView
|
|
283
|
+
} else {
|
|
284
|
+
topView = containerView
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
make.top.equalTo(topView.snp.bottom).offset(Constants.messageButtonSpacing)
|
|
288
|
+
make.leading.equalToSuperview().offset(Constants.horizontalPadding)
|
|
289
|
+
make.trailing.equalToSuperview().offset(-Constants.horizontalPadding)
|
|
290
|
+
make.bottom.equalToSuperview().offset(-Constants.verticalPadding)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// MARK: - Button Setup
|
|
295
|
+
private func setupButtons() {
|
|
296
|
+
// 清除现有按钮
|
|
297
|
+
buttonStack.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
|
298
|
+
gradientButtons.removeAll()
|
|
299
|
+
guard !actions.isEmpty else { return }
|
|
300
|
+
// 确定布局方向
|
|
301
|
+
let layoutAxis: NSLayoutConstraint.Axis
|
|
302
|
+
switch buttonLayout {
|
|
303
|
+
case .horizontal:
|
|
304
|
+
layoutAxis = .horizontal
|
|
305
|
+
case .vertical:
|
|
306
|
+
layoutAxis = .vertical
|
|
307
|
+
}
|
|
308
|
+
// 创建按钮容器
|
|
309
|
+
if layoutAxis == .horizontal {
|
|
310
|
+
// 水平布局:创建水平 StackView
|
|
311
|
+
let buttonContainer = UIStackView()
|
|
312
|
+
buttonContainer.axis = .horizontal
|
|
313
|
+
buttonContainer.spacing = Constants.buttonSpacing
|
|
314
|
+
buttonContainer.distribution = .fillEqually
|
|
315
|
+
actions.forEach { action in
|
|
316
|
+
let button = createButton(for: action, needsHeightConstraint: false)
|
|
317
|
+
buttonContainer.addArrangedSubview(button)
|
|
318
|
+
}
|
|
319
|
+
buttonStack.addArrangedSubview(buttonContainer)
|
|
320
|
+
buttonContainer.snp.makeConstraints { make in
|
|
321
|
+
make.height.equalTo(Constants.buttonHeight)
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
// 垂直布局:直接添加到 buttonStack
|
|
325
|
+
actions.forEach { action in
|
|
326
|
+
let button = createButton(for: action, needsHeightConstraint: true)
|
|
327
|
+
buttonStack.addArrangedSubview(button)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// 更新布局
|
|
331
|
+
view.setNeedsLayout()
|
|
332
|
+
view.layoutIfNeeded()
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private func createButton(for action: BMComponentAlertAction, needsHeightConstraint: Bool) -> UIButton {
|
|
336
|
+
let button = UIButton(type: .custom)
|
|
337
|
+
button.setTitle(action.title, for: .normal)
|
|
338
|
+
button.layer.cornerRadius = 8
|
|
339
|
+
button.clipsToBounds = true
|
|
340
|
+
|
|
341
|
+
switch action.style {
|
|
342
|
+
case .primaryGradient:
|
|
343
|
+
// 渐变色主要按钮
|
|
344
|
+
button.cexTheme.backgroundColor = .brandColor
|
|
345
|
+
button.cexTheme.setTitleColor(.btnTextColor, for: .normal)
|
|
346
|
+
button.titleLabel?.font = UIFont.S1Font
|
|
347
|
+
case .secondary:
|
|
348
|
+
// 深绿色次要按钮
|
|
349
|
+
button.cexTheme.backgroundColor = .brandBgColor
|
|
350
|
+
button.cexTheme.setTitleColor(.brandColor, for: .normal)
|
|
351
|
+
button.titleLabel?.font = UIFont.S1Font
|
|
352
|
+
case .text:
|
|
353
|
+
// 文本按钮
|
|
354
|
+
button.backgroundColor = .clear
|
|
355
|
+
button.cexTheme.setTitleColor(.primaryColor, for: .normal)
|
|
356
|
+
button.titleLabel?.font = UIFont.S4Font
|
|
357
|
+
}
|
|
358
|
+
// 根据布局方向决定是否需要高度约束
|
|
359
|
+
// 水平布局时,容器统一管理高度;垂直布局时,每个按钮需要单独设置高度
|
|
360
|
+
if needsHeightConstraint {
|
|
361
|
+
button.snp.makeConstraints { make in
|
|
362
|
+
make.height.equalTo(Constants.buttonHeight)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
button.onTap { [weak self] in
|
|
366
|
+
self?.dismiss(animated: true) {
|
|
367
|
+
action.handler?(action)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return button
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
open override func viewDidLayoutSubviews() {
|
|
377
|
+
super.viewDidLayoutSubviews()
|
|
378
|
+
// 更新所有渐变按钮的渐变层frame
|
|
379
|
+
gradientButtons.forEach { button, gradientLayer in
|
|
380
|
+
gradientLayer.frame = button.bounds
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// MARK: - Actions
|
|
385
|
+
@objc private func bgButtonClick() {
|
|
386
|
+
if enableDimmingViewTap {
|
|
387
|
+
dismiss(animated: true, completion: nil)
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
@objc private func closeAction() {
|
|
392
|
+
dismiss(animated: true, completion: nil)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// MARK: - Public Methods
|
|
396
|
+
public func addAction(_ action: BMComponentAlertAction) {
|
|
397
|
+
actions.append(action)
|
|
398
|
+
// 如果 view 已经加载,立即更新按钮
|
|
399
|
+
if isViewLoaded {
|
|
400
|
+
setupButtons()
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// MARK: - UIViewControllerTransitioningDelegate
|
|
406
|
+
//extension BMComponentAlertController: UIViewControllerTransitioningDelegate {
|
|
407
|
+
// public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
|
408
|
+
// return AlertAnimatedTransitioning()
|
|
409
|
+
// }
|
|
410
|
+
//
|
|
411
|
+
// public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
|
412
|
+
// return AlertAnimatedTransitioning()
|
|
413
|
+
// }
|
|
414
|
+
//}
|
|
415
|
+
|
|
416
|
+
// MARK: - Convenience Methods 便捷方法
|
|
417
|
+
public extension BMComponentAlertController {
|
|
418
|
+
/// 便捷方法:创建并显示一个简单的弹窗
|
|
419
|
+
/// - Parameters:
|
|
420
|
+
/// - title: 标题
|
|
421
|
+
/// - message: 描述信息
|
|
422
|
+
/// - image: 图片(可选)
|
|
423
|
+
/// - progressText: 进度文本(可选)
|
|
424
|
+
/// - primaryActionTitle: 主要按钮标题
|
|
425
|
+
/// - primaryActionHandler: 主要按钮回调
|
|
426
|
+
/// - secondaryActionTitle: 次要按钮标题(可选)
|
|
427
|
+
/// - secondaryActionHandler: 次要按钮回调(可选)
|
|
428
|
+
/// - textActionTitle: 文本按钮标题(可选)
|
|
429
|
+
/// - textActionHandler: 文本按钮回调(可选)
|
|
430
|
+
/// - buttonLayout: 按钮布局方向(默认自动)
|
|
431
|
+
/// - showCloseButton: 是否显示关闭按钮
|
|
432
|
+
/// - enableDimmingTap: 是否允许点击背景关闭
|
|
433
|
+
/// - from: 显示的视图控制器
|
|
434
|
+
static func show(
|
|
435
|
+
title: String? = nil,
|
|
436
|
+
message: String? = nil,
|
|
437
|
+
image: UIImage? = nil,
|
|
438
|
+
progressText: String? = nil,
|
|
439
|
+
primaryActionTitle: String,
|
|
440
|
+
primaryActionHandler: ((BMComponentAlertAction) -> Void)? = nil,
|
|
441
|
+
secondaryActionTitle: String? = nil,
|
|
442
|
+
secondaryActionHandler: ((BMComponentAlertAction) -> Void)? = nil,
|
|
443
|
+
textActionTitle: String? = nil,
|
|
444
|
+
textActionHandler: ((BMComponentAlertAction) -> Void)? = nil,
|
|
445
|
+
buttonLayout: BMComponentAlertButtonLayout = .horizontal,
|
|
446
|
+
showCloseButton: Bool = true,
|
|
447
|
+
enableDimmingTap: Bool = true,
|
|
448
|
+
from viewController: UIViewController
|
|
449
|
+
) {
|
|
450
|
+
let alert = BMComponentAlertController(
|
|
451
|
+
title: title,
|
|
452
|
+
message: message,
|
|
453
|
+
image: image,
|
|
454
|
+
progressText: progressText
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
alert.needShowCloseBtn = showCloseButton
|
|
458
|
+
alert.enableDimmingViewTap = enableDimmingTap
|
|
459
|
+
alert.buttonLayout = buttonLayout
|
|
460
|
+
|
|
461
|
+
// 添加主要按钮
|
|
462
|
+
let primaryAction = BMComponentAlertAction(
|
|
463
|
+
title: primaryActionTitle,
|
|
464
|
+
style: .primaryGradient,
|
|
465
|
+
handler: primaryActionHandler
|
|
466
|
+
)
|
|
467
|
+
alert.addAction(primaryAction)
|
|
468
|
+
|
|
469
|
+
// 添加次要按钮(如果提供)
|
|
470
|
+
if let secondaryTitle = secondaryActionTitle {
|
|
471
|
+
let secondaryAction = BMComponentAlertAction(
|
|
472
|
+
title: secondaryTitle,
|
|
473
|
+
style: .secondary,
|
|
474
|
+
handler: secondaryActionHandler
|
|
475
|
+
)
|
|
476
|
+
alert.addAction(secondaryAction)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// 添加文本按钮(如果提供)
|
|
480
|
+
if let textTitle = textActionTitle {
|
|
481
|
+
let textAction = BMComponentAlertAction(
|
|
482
|
+
title: textTitle,
|
|
483
|
+
style: .text,
|
|
484
|
+
handler: textActionHandler
|
|
485
|
+
)
|
|
486
|
+
alert.addAction(textAction)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
viewController.present(alert, animated: true)
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/*
|
|
494
|
+
使用示例:
|
|
495
|
+
|
|
496
|
+
// 示例1: 单按钮弹窗(渐变色主要按钮)
|
|
497
|
+
BMComponentAlertController.show(
|
|
498
|
+
title: "Title",
|
|
499
|
+
message: "This is a text description.",
|
|
500
|
+
primaryActionTitle: "Button",
|
|
501
|
+
primaryActionHandler: { action in
|
|
502
|
+
print("Primary button tapped")
|
|
503
|
+
},
|
|
504
|
+
from: self
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
// 示例2: 双按钮并排布局(自动,默认行为)
|
|
508
|
+
BMComponentAlertController.show(
|
|
509
|
+
title: "Title",
|
|
510
|
+
message: "This is a text description.",
|
|
511
|
+
primaryActionTitle: "Confirm",
|
|
512
|
+
primaryActionHandler: { action in
|
|
513
|
+
print("Confirm tapped")
|
|
514
|
+
},
|
|
515
|
+
secondaryActionTitle: "Cancel",
|
|
516
|
+
secondaryActionHandler: { action in
|
|
517
|
+
print("Cancel tapped")
|
|
518
|
+
},
|
|
519
|
+
from: self
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
// 示例2b: 双按钮垂直排列(手动指定)
|
|
523
|
+
BMComponentAlertController.show(
|
|
524
|
+
title: "Title",
|
|
525
|
+
message: "This is a text description.",
|
|
526
|
+
primaryActionTitle: "Confirm",
|
|
527
|
+
primaryActionHandler: { action in
|
|
528
|
+
print("Confirm tapped")
|
|
529
|
+
},
|
|
530
|
+
secondaryActionTitle: "Cancel",
|
|
531
|
+
secondaryActionHandler: { action in
|
|
532
|
+
print("Cancel tapped")
|
|
533
|
+
},
|
|
534
|
+
buttonLayout: .vertical, // 强制垂直排列
|
|
535
|
+
from: self
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
// 示例3: 多按钮堆叠布局(包含文本按钮)
|
|
539
|
+
let alert = BMComponentAlertController(
|
|
540
|
+
title: "Title",
|
|
541
|
+
message: "This is a text description of a dialog Lorem ipsum.",
|
|
542
|
+
progressText: "1/2"
|
|
543
|
+
)
|
|
544
|
+
alert.needShowCloseBtn = true
|
|
545
|
+
alert.buttonLayout = .vertical // 明确指定垂直布局
|
|
546
|
+
alert.addAction(BMComponentAlertAction(title: "Button", style: .secondary) { action in
|
|
547
|
+
print("Secondary button tapped")
|
|
548
|
+
})
|
|
549
|
+
alert.addAction(BMComponentAlertAction(title: "Button", style: .primaryGradient) { action in
|
|
550
|
+
print("Primary gradient button tapped")
|
|
551
|
+
})
|
|
552
|
+
alert.addAction(BMComponentAlertAction(title: "Button Text", style: .text) { action in
|
|
553
|
+
print("Text button tapped")
|
|
554
|
+
})
|
|
555
|
+
present(alert, animated: true)
|
|
556
|
+
|
|
557
|
+
// 示例4: 多个按钮水平排列
|
|
558
|
+
let alert2 = BMComponentAlertController(title: "Title", message: "Message")
|
|
559
|
+
alert2.buttonLayout = .horizontal // 强制水平排列
|
|
560
|
+
alert2.addAction(BMComponentAlertAction(title: "Button 1", style: .secondary))
|
|
561
|
+
alert2.addAction(BMComponentAlertAction(title: "Button 2", style: .primaryGradient))
|
|
562
|
+
alert2.addAction(BMComponentAlertAction(title: "Button 3", style: .text))
|
|
563
|
+
present(alert2, animated: true)
|
|
564
|
+
|
|
565
|
+
// 示例5: 带图片的弹窗
|
|
566
|
+
BMComponentAlertController.show(
|
|
567
|
+
title: "Title",
|
|
568
|
+
message: "This is a text description.",
|
|
569
|
+
image: UIImage(named: "example_image"),
|
|
570
|
+
primaryActionTitle: "Button",
|
|
571
|
+
primaryActionHandler: { _ in },
|
|
572
|
+
from: self
|
|
573
|
+
)
|
|
574
|
+
*/
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//
|
|
2
|
+
// BMComponentButton+Examples.swift
|
|
3
|
+
// BMComponents
|
|
4
|
+
//
|
|
5
|
+
// Created by james on 2025/12/31.
|
|
6
|
+
// 使用示例和便捷方法
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import UIKit
|
|
10
|
+
|
|
11
|
+
// MARK: - Convenience Methods
|
|
12
|
+
|
|
13
|
+
public extension BMComponentButton {
|
|
14
|
+
|
|
15
|
+
/// 创建主色按钮
|
|
16
|
+
static func primary(title: String? = nil, icon: UIImage? = nil,size: BMComponentButtonSize = .Medium,iconPosition: BMComponentButtonIconPosition = .leading) -> BMComponentButton {
|
|
17
|
+
let config = BMComponentButtonConfiguration.primary(size: size, iconPosition: iconPosition)
|
|
18
|
+
return BMComponentButton(title: title,icon: icon,configuration: config)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// 创建次要按钮
|
|
22
|
+
static func secondary(title: String? = nil,icon: UIImage? = nil,size: BMComponentButtonSize = .Medium,iconPosition: BMComponentButtonIconPosition = .leading) -> BMComponentButton {
|
|
23
|
+
let config = BMComponentButtonConfiguration.secondary(size: size, iconPosition: iconPosition)
|
|
24
|
+
return BMComponentButton(title: title,icon: icon,configuration: config)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// 创建买入按钮(绿色)
|
|
28
|
+
static func buyButton(title: String? = nil,icon: UIImage? = nil,size: BMComponentButtonSize = .Medium,iconPosition: BMComponentButtonIconPosition = .leading) -> BMComponentButton {
|
|
29
|
+
let config = BMComponentButtonConfiguration.buy(size: size, iconPosition: iconPosition)
|
|
30
|
+
return BMComponentButton(title: title,icon: icon,configuration: config)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// 创建卖出按钮(红色)
|
|
34
|
+
static func sellButton(title: String? = nil,icon: UIImage? = nil,size: BMComponentButtonSize = .Medium,iconPosition: BMComponentButtonIconPosition = .leading) -> BMComponentButton {
|
|
35
|
+
let config = BMComponentButtonConfiguration.sell(size: size, iconPosition: iconPosition)
|
|
36
|
+
return BMComponentButton(title: title,icon: icon,configuration: config)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// 创建灰色按钮
|
|
40
|
+
static func whiteButton(title: String? = nil,icon: UIImage? = nil,size: BMComponentButtonSize = .Medium,iconPosition: BMComponentButtonIconPosition = .leading) -> BMComponentButton {
|
|
41
|
+
let config = BMComponentButtonConfiguration.white(size: size, iconPosition: iconPosition)
|
|
42
|
+
return BMComponentButton(title: title,icon: icon,configuration: config)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// MARK: - Preset Configurations
|
|
49
|
+
public extension BMComponentButtonConfiguration {
|
|
50
|
+
|
|
51
|
+
/// 主色按钮(标准样式)
|
|
52
|
+
static func primary(size: BMComponentButtonSize = .Medium, iconPosition: BMComponentButtonIconPosition = .leading) -> BMComponentButtonConfiguration {
|
|
53
|
+
return BMComponentButtonConfiguration(type: .primary,size: size, iconPosition: iconPosition)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// 次要按钮(标准样式)
|
|
57
|
+
static func secondary(size: BMComponentButtonSize = .Medium, iconPosition: BMComponentButtonIconPosition = .leading) -> BMComponentButtonConfiguration {
|
|
58
|
+
return BMComponentButtonConfiguration(type: .secondary, size: size, iconPosition: iconPosition)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// 买入按钮(标准样式)
|
|
62
|
+
static func buy(size: BMComponentButtonSize = .Medium, iconPosition: BMComponentButtonIconPosition = .leading) -> BMComponentButtonConfiguration {
|
|
63
|
+
return BMComponentButtonConfiguration(type: .green, size: size, iconPosition: iconPosition)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// 卖出按钮(标准样式)
|
|
67
|
+
static func sell(size: BMComponentButtonSize = .Medium, iconPosition: BMComponentButtonIconPosition = .leading) -> BMComponentButtonConfiguration {
|
|
68
|
+
return BMComponentButtonConfiguration(type: .red,size: size, iconPosition: iconPosition)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// 灰色按钮(标准样式)
|
|
72
|
+
static func white(size: BMComponentButtonSize = .Medium, iconPosition: BMComponentButtonIconPosition = .leading) -> BMComponentButtonConfiguration {
|
|
73
|
+
return BMComponentButtonConfiguration(type: .white,size: size, iconPosition: iconPosition)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|