@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.
Files changed (67) hide show
  1. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Bitmart Card.imageset/Contents.json +21 -0
  2. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Bitmart Card.imageset/Property 1=Bitmart Card.svg +3 -0
  3. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Contents.json +6 -0
  4. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Credit Debit Card.imageset/Contents.json +21 -0
  5. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Credit Debit Card.imageset/Property 1=Credit Debit Card.svg +3 -0
  6. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Crypto Prepaid Card.imageset/Contents.json +21 -0
  7. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Crypto Prepaid Card.imageset/Property 1=Crypto Prepaid Card.svg +3 -0
  8. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Mobile Recharge.imageset/Contents.json +21 -0
  9. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Mobile Recharge.imageset/Mobile Recharge.svg +3 -0
  10. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/P2P Trading.imageset/Contents.json +21 -0
  11. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/P2P Trading.imageset/Property 1=P2P Trading.svg +3 -0
  12. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/SEPA Deposit.imageset/Contents.json +21 -0
  13. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/SEPA Deposit.imageset/SEPA Deposit.svg +3 -0
  14. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Third-Party Payment.imageset/Contents.json +21 -0
  15. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Third-Party Payment.imageset/Property 1=Third-Party Payment.svg +3 -0
  16. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/Contents.json +6 -0
  17. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Contents.json +6 -0
  18. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/checkmark.imageset/Contents.json +21 -0
  19. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/checkmark.imageset/checkmark.pdf +0 -0
  20. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/close_icon.imageset/Contents.json +22 -0
  21. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/close_icon.imageset/close_icon@2x.png +0 -0
  22. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/close_icon.imageset/close_icon@3x.png +0 -0
  23. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/cross.imageset/Contents.json +21 -0
  24. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/cross.imageset/cross.pdf +0 -0
  25. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress.imageset/Contents.json +21 -0
  26. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress.imageset/progress.pdf +0 -0
  27. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress_circular.imageset/Contents.json +21 -0
  28. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress_circular.imageset/progress_circular.pdf +0 -0
  29. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_dark.imageset/Contents.json +22 -0
  30. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_dark.imageset/refresh_footer_dark@2x.png +0 -0
  31. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_dark.imageset/refresh_footer_dark@3x.png +0 -0
  32. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_light.imageset/Contents.json +22 -0
  33. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_light.imageset/refresh_footer_light@2x.png +0 -0
  34. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_light.imageset/refresh_footer_light@3x.png +0 -0
  35. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/search_icon.imageset/Contents.json +21 -0
  36. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/search_icon.imageset/Group 13994.svg +7 -0
  37. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_dark_chose.imageset/Contents.json +21 -0
  38. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_dark_chose.imageset/Frame.svg +3 -0
  39. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_arrow.imageset/Contents.json +22 -0
  40. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_arrow.imageset/sheet_list_arrow@2x.png +0 -0
  41. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_arrow.imageset/sheet_list_arrow@3x.png +0 -0
  42. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_cell_checkbox.imageset/Contents.json +21 -0
  43. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_cell_checkbox.imageset/Frame (1).svg +3 -0
  44. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_chose.imageset/Contents.json +21 -0
  45. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_chose.imageset/Frame.png +0 -0
  46. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/slider_bubble.imageset/Contents.json +22 -0
  47. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/slider_bubble.imageset/slider_bubble@2x.png +0 -0
  48. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/slider_bubble.imageset/slider_bubble@3x.png +0 -0
  49. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/spot_second_floor_refresh_arrow.imageset/Contents.json +21 -0
  50. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/spot_second_floor_refresh_arrow.imageset/spot_second_floor_refresh_arrow.svg +8 -0
  51. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Font/Alexandria-Medium.ttf +0 -0
  52. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Font/Alexandria-Regular.ttf +0 -0
  53. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Font/Alexandria-SemiBold.ttf +0 -0
  54. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/BMFont/BMFont.swift +82 -0
  55. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/BMFont/UIFontExtensions.swift +120 -0
  56. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/AlertView/BMComponentAlertController.swift +574 -0
  57. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Buttons/BMComponentButton+Examples.swift +77 -0
  58. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Buttons/BMComponentButton.swift +373 -0
  59. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Buttons/BMComponentButtonConfiguration.swift +181 -0
  60. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Popup/BMComponentPopupController.swift +312 -0
  61. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/SegmentView/BMComponentSegmentedTitleCell.swift +294 -0
  62. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/SegmentView/BMComponentSegmentedTitleDataSource.swift +49 -0
  63. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/SegmentView/BMComponentSegmentedView.swift +292 -0
  64. package/ios/Modules/BMUIComponents/LICENSE +19 -0
  65. package/ios/Modules/BMUIComponents/README.md +29 -0
  66. package/package.json +3 -1
  67. 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
+