290 lines
10 KiB
Swift
290 lines
10 KiB
Swift
//
|
||
// CreateBubbleSetupView.swift
|
||
// QuickLocation
|
||
//
|
||
|
||
import UIKit
|
||
import RxSwift
|
||
import RxCocoa
|
||
import SwiftDate
|
||
|
||
/// 设置页:时长 picker + 同页底部通知 sheet
|
||
final class CreateBubbleSetupView: UIView {
|
||
|
||
var disposeBag = DisposeBag()
|
||
let selectedHour = BehaviorRelay<Int>(value: 1)
|
||
var messageText: String = ""
|
||
|
||
var onConfirmCreate: (() -> Void)?
|
||
var onSheetVisibilityChanged: ((Bool) -> Void)?
|
||
|
||
private let hours = Array(1...6)
|
||
|
||
let heroView = BubbleHeroView()
|
||
let confirmBtn = UIButton(type: .custom)
|
||
let sheetConfirmBtn = UIButton(type: .custom)
|
||
|
||
private let subtitleLab = UILabel()
|
||
private let titleLab = UILabel()
|
||
private let pickerView = UIPickerView()
|
||
private let hourLab = UILabel()
|
||
|
||
private let sheetOverlay = UIView()
|
||
private let sheetPanel = UIView()
|
||
private let pinIcon = UIImageView()
|
||
private let sheetTitleLab = UILabel()
|
||
private let messageView = UIView()
|
||
private let messageLab = UILabel()
|
||
private let previewAvatar = UIImageView()
|
||
private let tipStack = UIStackView()
|
||
|
||
override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
backgroundColor = .white
|
||
setupUI()
|
||
setupPicker()
|
||
setupSheet()
|
||
pickerView.selectRow(0, inComponent: 0, animated: false)
|
||
selectedHour.accept(hours[0])
|
||
}
|
||
|
||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||
|
||
func showNotifySheet(message: String) {
|
||
messageText = message
|
||
messageLab.text = message
|
||
sheetOverlay.isHidden = false
|
||
sheetOverlay.alpha = 0
|
||
sheetPanel.transform = CGAffineTransform(translationX: 0, y: 40)
|
||
onSheetVisibilityChanged?(true)
|
||
UIView.animate(withDuration: 0.28, delay: 0, options: .curveEaseOut) {
|
||
self.sheetOverlay.alpha = 1
|
||
self.sheetPanel.transform = .identity
|
||
}
|
||
}
|
||
|
||
func dismissNotifySheet() {
|
||
onSheetVisibilityChanged?(false)
|
||
UIView.animate(withDuration: 0.22, delay: 0, options: .curveEaseIn) {
|
||
self.sheetOverlay.alpha = 0
|
||
self.sheetPanel.transform = CGAffineTransform(translationX: 0, y: 40)
|
||
} completion: { _ in
|
||
self.sheetOverlay.isHidden = true
|
||
}
|
||
}
|
||
|
||
var isSheetVisible: Bool { !sheetOverlay.isHidden && sheetOverlay.alpha > 0.01 }
|
||
|
||
private func setupUI() {
|
||
addSubview(heroView)
|
||
addSubview(titleLab)
|
||
addSubview(subtitleLab)
|
||
addSubview(pickerView)
|
||
addSubview(hourLab)
|
||
addSubview(confirmBtn)
|
||
|
||
heroView.layoutChain
|
||
.top(33)
|
||
.leading(30)
|
||
.trailing(30)
|
||
.heightToWidth(1)
|
||
|
||
titleLab.text = "您会在气泡待多久?"
|
||
titleLab.font = FontManager.boboBold(30)
|
||
titleLab.textColor = UIColor(hexStr: "#293445")
|
||
titleLab.textAlignment = .center
|
||
titleLab.layoutChain.topToBottomOfView(heroView, offset: 25).centerX()
|
||
|
||
subtitleLab.text = "设置气泡时间"
|
||
subtitleLab.font = .systemFont(ofSize: 18, weight: .bold)
|
||
subtitleLab.textColor = UIColor(hexStr: "#9CA3AF")
|
||
subtitleLab.layoutChain.topToBottomOfView(titleLab, offset: 4).centerX()
|
||
|
||
pickerView.layoutChain
|
||
.topToBottomOfView(subtitleLab, offset: 12)
|
||
.edgesHorzontal(15)
|
||
.height(180)
|
||
|
||
hourLab.text = "小时"
|
||
hourLab.font = .systemFont(ofSize: 16, weight: .medium)
|
||
hourLab.textColor = UIColor(hexStr: "#16B3FF")
|
||
hourLab.layoutChain.centerX(self, offset: 100).centerY(pickerView)
|
||
|
||
confirmBtn.setTitle("确定", for: .normal)
|
||
confirmBtn.setTitleColor(.white, for: .normal)
|
||
confirmBtn.titleLabel?.font = FontManager.boboBold(18)
|
||
confirmBtn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
|
||
confirmBtn.cornerRadius = 20
|
||
confirmBtn.layoutChain.left(30).right(30).bottom(34).height(56)
|
||
}
|
||
|
||
private func setupPicker() {
|
||
pickerView.delegate = self
|
||
pickerView.dataSource = self
|
||
pickerView.backgroundColor = .clear
|
||
}
|
||
|
||
override func layoutSubviews() {
|
||
super.layoutSubviews()
|
||
clearPickerSelectionBackground()
|
||
}
|
||
|
||
private func clearPickerSelectionBackground() {
|
||
pickerView.subviews.forEach { sub in
|
||
sub.backgroundColor = .clear
|
||
if sub.frame.height <= 1 {
|
||
sub.isHidden = true
|
||
}
|
||
}
|
||
}
|
||
|
||
private func setupSheet() {
|
||
sheetOverlay.backgroundColor = UIColor.black.withAlphaComponent(0.45)
|
||
sheetOverlay.isHidden = true
|
||
addSubview(sheetOverlay)
|
||
sheetOverlay.layoutChain.edges()
|
||
|
||
sheetPanel.backgroundColor = .white
|
||
sheetPanel.layer.cornerRadius = 24
|
||
sheetPanel.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||
sheetOverlay.addSubview(sheetPanel)
|
||
sheetPanel.layoutChain.edgesHorzontal().bottom()
|
||
|
||
pinIcon.image = UIImage(named: "Bubble/pin")
|
||
pinIcon.contentMode = .scaleAspectFit
|
||
sheetPanel.addSubview(pinIcon)
|
||
pinIcon.layoutChain.top(-22).centerX().width(44).height(44)
|
||
|
||
sheetTitleLab.text = "通知您的圈子"
|
||
sheetTitleLab.font = .systemFont(ofSize: 18, weight: .bold)
|
||
sheetTitleLab.textColor = UIColor(hexStr: "#293445")
|
||
sheetTitleLab.textAlignment = .center
|
||
sheetPanel.addSubview(sheetTitleLab)
|
||
sheetTitleLab.layoutChain.top(28).centerX()
|
||
|
||
messageView.backgroundColor = UIColor(hexStr: "#EBF6F9")
|
||
messageView.layer.cornerRadius = 12
|
||
sheetPanel.addSubview(messageView)
|
||
messageView.addSubview(messageLab)
|
||
messageLab.font = .systemFont(ofSize: 15, weight: .medium)
|
||
messageLab.textColor = UIColor(hexStr: "#293445")
|
||
messageLab.numberOfLines = 0
|
||
messageLab.layoutChain.edgesHorzontal(16).edgesVertical(14)
|
||
|
||
previewAvatar.contentMode = .scaleAspectFill
|
||
previewAvatar.clipsToBounds = true
|
||
previewAvatar.layer.cornerRadius = 18
|
||
previewAvatar.layer.borderWidth = 2
|
||
previewAvatar.layer.borderColor = UIColor.white.cgColor
|
||
let avatar = AppContextManager.shared.avaterIcon
|
||
previewAvatar.image = avatar.size.width > 0 ? avatar : UIImage(named: "Common/default_avatar")
|
||
sheetPanel.addSubview(previewAvatar)
|
||
|
||
messageView.layoutChain
|
||
.topToBottomOfView(sheetTitleLab, offset: 20)
|
||
.left(24)
|
||
.right(72)
|
||
previewAvatar.layoutChain
|
||
.right(24)
|
||
.bottomToView(messageView)
|
||
.width(40)
|
||
.height(40)
|
||
|
||
tipStack.axis = .vertical
|
||
tipStack.spacing = 12
|
||
sheetPanel.addSubview(tipStack)
|
||
tipStack.layoutChain
|
||
.topToBottomOfView(messageView, offset: 16)
|
||
.edgesHorzontal(24)
|
||
|
||
[
|
||
"您的圈子成员只会看到您在气泡中。",
|
||
"当您在气泡中,将不会共享您的确切位置。"
|
||
].forEach { tipStack.addArrangedSubview(makeTipRow($0)) }
|
||
|
||
sheetConfirmBtn.setTitle("确定", for: .normal)
|
||
sheetConfirmBtn.setTitleColor(.white, for: .normal)
|
||
sheetConfirmBtn.titleLabel?.font = FontManager.boboBold(18)
|
||
sheetConfirmBtn.backgroundColor = UIColor(hexStr: "#16B3FF")
|
||
sheetConfirmBtn.layer.cornerRadius = 16
|
||
sheetPanel.addSubview(sheetConfirmBtn)
|
||
sheetConfirmBtn.layoutChain
|
||
.topToBottomOfView(tipStack, offset: 20)
|
||
.edgesHorzontal(20)
|
||
.height(56)
|
||
.bottom(24 + kSafeBottomMargin)
|
||
|
||
let tap = UITapGestureRecognizer(target: self, action: #selector(tapMask))
|
||
sheetOverlay.addGestureRecognizer(tap)
|
||
}
|
||
|
||
private func makeTipRow(_ text: String) -> UIView {
|
||
let row = UIView()
|
||
let star = UIImageView(image: UIImage(systemName: "sparkle"))
|
||
star.tintColor = UIColor(hexStr: "#16B3FF")
|
||
star.contentMode = .scaleAspectFit
|
||
let lab = UILabel()
|
||
lab.text = text
|
||
lab.font = .systemFont(ofSize: 14, weight: .medium)
|
||
lab.textColor = UIColor(hexStr: "#767676")
|
||
lab.numberOfLines = 0
|
||
row.addSubview(star)
|
||
row.addSubview(lab)
|
||
star.layoutChain.left().top(2).width(14).height(14)
|
||
lab.layoutChain.leftToRightOfView(star, offset: 8).right().top().bottom()
|
||
return row
|
||
}
|
||
|
||
@objc private func tapMask(_ gesture: UITapGestureRecognizer) {
|
||
let point = gesture.location(in: sheetPanel)
|
||
if sheetPanel.bounds.contains(point) { return }
|
||
dismissNotifySheet()
|
||
}
|
||
|
||
func buildMessage(for hours: Int) -> String {
|
||
let endDate = Calendar.current.date(byAdding: .hour, value: hours, to: Date()) ?? Date()
|
||
let timeStr = endDate.toFormat("HH:mm")
|
||
return "\(timeStr)之前,我会一直在这个区域。如果需要我帮忙,请给我发消息。"
|
||
}
|
||
}
|
||
|
||
extension CreateBubbleSetupView: UIPickerViewDataSource, UIPickerViewDelegate {
|
||
func numberOfComponents(in pickerView: UIPickerView) -> Int { 1 }
|
||
|
||
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
|
||
hours.count
|
||
}
|
||
|
||
func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { 44 }
|
||
|
||
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
|
||
let container = view as? UIView ?? UIView()
|
||
container.subviews.forEach { $0.removeFromSuperview() }
|
||
|
||
let label = UILabel()
|
||
label.textAlignment = .center
|
||
label.font = .systemFont(ofSize: 20, weight: .medium)
|
||
let selected = row == pickerView.selectedRow(inComponent: component)
|
||
label.textColor = selected ? UIColor(hexStr: "#16B3FF") : UIColor(hexStr: "#999999")
|
||
label.text = "\(hours[row])"
|
||
container.addSubview(label)
|
||
label.layoutChain.center().width(60).height(36)
|
||
|
||
if selected {
|
||
let capsule = UIView()
|
||
capsule.backgroundColor = UIColor(hexStr: "#E3F6FF")
|
||
capsule.layer.cornerRadius = 14
|
||
container.insertSubview(capsule, at: 0)
|
||
capsule.layoutChain.center().width(130).height(36)
|
||
}
|
||
|
||
return container
|
||
}
|
||
|
||
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
|
||
selectedHour.accept(hours[row])
|
||
pickerView.reloadAllComponents()
|
||
clearPickerSelectionBackground()
|
||
}
|
||
}
|