jsdw_ios/QuickLocation/Section/Home/SOS/SOSPracticeView.swift

440 lines
15 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// SOSPracticeView.swift
// QuickLocation
//
// Created by on 2026/6/18.
//
import UIKit
import RxSwift
import RxCocoa
import RxGesture
import Lottie
import SwiftyUserDefaults
class SOSPracticeView: UIView {
var disposeBag = DisposeBag()
var countDownFinish: Bool = false
private func setupRx() {
exclamationLottieView.rx.tapGesture.subscribe(onNext: { _ in
UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut], animations: {
self.countDownView.alpha = 1
}, completion: { _ in
self.countDownLottieView.play { completed in
guard completed else { return }
if let path = Bundle.main.path(forResource: "red-exclamation", ofType: "json") {
self.countDownLottieView.animation = LottieAnimation.filepath(path)
self.countDownLottieView.loopMode = .loop
self.countDownLottieView.play()
self.countDownTipsLab.text = "已将你的SOS和位置发送到你的圈子和紧急联系人。"
self.countDownFinish = true
}
}
})
}).disposed(by: disposeBag)
sliderIcon.rx.panGesture()
.subscribe(onNext: { [weak self] gesture in
guard let self = self else { return }
let translation = gesture.translation(in: self.sliderView)
let maxX = self.sliderView.bounds.width - self.sliderIcon.frame.width
switch gesture.state {
case .began, .changed:
var newX = self.sliderIcon.frame.minX + translation.x
newX = max(0, min(maxX, newX))
self.sliderIcon.frame.origin.x = newX
gesture.setTranslation(.zero, in: self.sliderView)
case .ended:
if self.sliderIcon.frame.minX >= maxX - 5 {
if countDownFinish {
UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) {
self.finishView.alpha = 1
}
}
else {
self.countDownView.alpha = 0
self.countDownLottieView.stop()
}
}
UIView.animate(withDuration: 0.3) {
self.sliderIcon.frame.origin.x = 3
}
default:
break
}
})
.disposed(by: disposeBag)
}
private func setupUI() {
addSubview(titleView)
addSubview(exclamationLottieView)
addSubview(tipsLab)
addSubview(bottomTipsLab)
addSubview(scrollView)
addSubview(pageControl)
addSubview(countDownView)
addSubview(finishView)
titleView.layoutChain
.top(15)
.centerX()
exclamationLottieView.layoutChain
.topToBottomOfView(titleView, offset: 13)
.edgesHorzontal(28)
.heightToWidth(1)
tipsLab.layoutChain
.topToBottomOfView(exclamationLottieView, offset: 60)
.centerX()
bottomTipsLab.layoutChain
.centerX()
.bottom(kSafeBottomMargin + 40)
scrollView.layoutChain
.top(15)
.edges(excludingEdge: .top)
pageControl.layoutChain
.centerX()
.bottom(kSafeBottomMargin + 80)
countDownView.layoutChain
.edges()
finishView.layoutChain.edges()
}
lazy var titleView: UIView = {
let view = UIView()
view.backgroundColor = .clear
view.addSubview(titleLab)
titleLab.layoutChain
.edgesVertical(9)
.edgesHorzontal(38)
return view
}()
lazy var titleLab: UILabel = {
let label = UILabel()
label.text = "开始练习"
label.font = .systemFont(ofSize: 16, weight: .medium)
label.textColor = .white
label.textAlignment = .center
return label
}()
lazy var exclamationLottieView: LottieAnimationView = {
let view = LottieAnimationView(name: "yellow-exclamation")
view.loopMode = .loop
return view
}()
lazy var tipsLab: UILabel = {
let label = UILabel()
label.text = "当您感到紧张或不安全时,请按住此按钮。"
label.font = .systemFont(ofSize: 16, weight: .medium)
label.textColor = UIColor(hexStr: "#333333")
return label
}()
lazy var bottomTipsLab: UILabel = {
let label = UILabel()
label.textColor = UIColor(hexStr: "#333333")
let text = "轻点感叹号发送SOS"
let attr = NSMutableAttributedString(string: text)
attr.addAttribute(.font, value: UIFont.systemFont(ofSize: 20, weight: .semibold), range: NSRange(location: 0, length: text.count))
attr.addAttribute(.foregroundColor, value: UIColor(hexStr: "#FF383C"), range: NSRange(location: "轻点感叹号发送".count, length: "SOS".count))
label.attributedText = attr
return label
}()
lazy var scrollView: UIScrollView = {
let view = UIScrollView()
view.backgroundColor = .white
view.isPagingEnabled = true
view.showsHorizontalScrollIndicator = false
view.bounces = false
view.delegate = self
view.addSubview(scrollContentView)
scrollContentView.layoutChain.edges().heightToView(view)
let view1 = UIView()
view1.backgroundColor = .clear
let img1 = UIImageView(image: UIImage(named: "SOS/1"))
let nextBtn = UIButton(type: .custom)
nextBtn.setTitle("开始设置", for: .normal)
nextBtn.setTitleColor(.white, for: .normal)
nextBtn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
nextBtn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
nextBtn.cornerRadius = 25
nextBtn.rx.tap.subscribe(onNext: { _ in
view.setContentOffset(CGPointMake(kScreenWidth, 0), animated: true)
self.pageControl.currentPage = 1
}).disposed(by: disposeBag)
view1.addSubview(img1)
view1.addSubview(nextBtn)
scrollContentView.addSubview(view1)
view1.layoutChain.edges(excludingEdge: .right).width(kScreenWidth)
img1.layoutChain
.top()
.edgesHorzontal(39)
.heightToWidth(471/297)
nextBtn.layoutChain
.height(50)
.edgesHorzontal(30)
.bottom(kSafeBottomMargin + 20)
let view2 = UIView()
view2.backgroundColor = .clear
let img2 = UIImageView(image: UIImage(named: "SOS/2"))
view2.addSubview(img2)
scrollContentView.addSubview(view2)
view2.layoutChain.top().bottom().leftToRightOfView(view1).widthToView(view1)
img2.layoutChain
.top()
.edgesHorzontal(39)
.heightToWidth(471/297)
let view3 = UIView()
view3.backgroundColor = .clear
let img3 = UIImageView(image: UIImage(named: "SOS/3"))
let doneBtn = UIButton(type: .custom)
doneBtn.setTitle("练习SOS触发", for: .normal)
doneBtn.setTitleColor(.white, for: .normal)
doneBtn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
doneBtn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
doneBtn.cornerRadius = 25
doneBtn.rx.tap.subscribe(onNext: { _ in
self.scrollView.isHidden = true
self.pageControl.isHidden = true
}).disposed(by: disposeBag)
view3.addSubview(img3)
view3.addSubview(doneBtn)
scrollContentView.addSubview(view3)
view3.layoutChain.top().bottom().right().leftToRightOfView(view2).widthToView(view1)
img3.layoutChain
.top()
.edgesHorzontal(39)
.heightToWidth(471/297)
doneBtn.layoutChain
.height(50)
.edgesHorzontal(30)
.bottom(kSafeBottomMargin + 20)
return view
}()
lazy var scrollContentView: UIView = {
let view = UIView()
view.backgroundColor = .clear
return view
}()
lazy var pageControl: UIPageControl = {
let pc = UIPageControl()
pc.numberOfPages = 3
pc.currentPageIndicatorTintColor = UIColor(hexStr: "#57C7FF")
pc.pageIndicatorTintColor = UIColor(hexStr: "#D9D9D9", alpha: 1)
return pc
}()
//
lazy var countDownView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.alpha = 0
view.addSubview(countDownTitleView)
countDownTitleView.layoutChain
.top(15)
.centerX()
view.addSubview(countDownLottieView)
countDownLottieView.layoutChain
.topToBottomOfView(countDownTitleView, offset: 13)
.edgesHorzontal(27)
.heightToWidth(1)
let titleLab = UILabel()
titleLab.text = "滑动取消"
titleLab.font = .systemFont(ofSize: 26, weight: .semibold)
view.addSubview(titleLab)
titleLab.layoutChain
.topToBottomOfView(countDownLottieView, offset: 15)
.centerX()
view.addSubview(countDownTipsLab)
countDownTipsLab.layoutChain
.topToBottomOfView(titleLab, offset: 8)
.edgesHorzontal(38)
view.addSubview(sliderView)
sliderView.layoutChain
.edgesHorzontal(30)
.bottom(kSafeBottomMargin + 30)
.height(50)
return view
}()
lazy var countDownTitleView: UIView = {
let view = UIView()
view.backgroundColor = .clear
view.addSubview(countDownTitleLab)
countDownTitleLab.layoutChain
.edgesVertical(9)
.edgesHorzontal(38)
return view
}()
lazy var countDownTitleLab: UILabel = {
let label = UILabel()
label.text = "练习模式"
label.font = .systemFont(ofSize: 16, weight: .medium)
label.textColor = .white
label.textAlignment = .center
return label
}()
lazy var countDownLottieView: LottieAnimationView = {
let view = LottieAnimationView(name: "10-second-timer")
view.loopMode = .playOnce
return view
}()
lazy var countDownTipsLab: UILabel = {
let tipsLab = UILabel()
tipsLab.text = "10秒后会将你的SOS和位置发送到你的圈子和紧急联系人。"
tipsLab.font = .systemFont(ofSize: 16, weight: .medium)
tipsLab.textAlignment = .center
tipsLab.numberOfLines = 0
return tipsLab
}()
lazy var sliderView: UIView = {
let view = UIView()
view.backgroundColor = .clear
view.cornerRadius = 25
let imgView = UIImageView(image: UIImage(named: "Common/button_bg_2"))
imgView.contentMode = .scaleAspectFill
view.addSubview(imgView)
imgView.layoutChain.edges()
let label = UILabel()
label.text = "滑动以取消SOS"
label.textColor = .white
label.font = .systemFont(ofSize: 16, weight: .medium)
view.addSubview(label)
label.layoutChain.centerX().centerY()
view.addSubview(sliderIcon)
return view
}()
lazy var sliderIcon: UIImageView = {
let view = UIImageView(frame: CGRectMake(3, 2, 46, 46))
view.image = UIImage(named: "SOS/slider")
view.isUserInteractionEnabled = true
return view
}()
lazy var finishView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.alpha = 0
let imgView = UIImageView(image: UIImage(named: "SOS/finish"))
imgView.contentMode = .scaleAspectFill
view.addSubview(imgView)
imgView.layoutChain
.top(50)
.edgesHorzontal(70)
.heightToWidth(356/235)
let doneBtn = UIButton(type: .custom)
doneBtn.setTitle("收到了", for: .normal)
doneBtn.setTitleColor(.white, for: .normal)
doneBtn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
doneBtn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
doneBtn.cornerRadius = 25
doneBtn.rx.tap.subscribe(onNext: { _ in
var sosIsPracticeList = Defaults[\.sosIsPracticeList]
sosIsPracticeList.append(AppContextManager.shared.userId)
Defaults[\.sosIsPracticeList] = sosIsPracticeList
UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut], animations: {
self.alpha = 0
}, completion: { _ in
self.isHidden = true
})
}).disposed(by: disposeBag)
view.addSubview(doneBtn)
doneBtn.layoutChain
.height(50)
.edgesHorzontal(30)
.bottom(kSafeBottomMargin + 20)
return view
}()
override init(frame: CGRect) {
super.init(frame: .zero)
self.isHidden = true
backgroundColor = .white
setupUI()
setupRx()
exclamationLottieView.play()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
self.layoutIfNeeded()
titleView.setGradientLayer(frame: titleView.bounds,
startPoint: CGPoint(x: 0, y: 0.5),
endPoint: CGPoint(x: 1, y: 0.5),
colors: [UIColor(hexStr: "#5CBBFF", alpha: 0), UIColor(hexStr: "#5CBBFF"), UIColor(hexStr: "#5CBBFF", alpha: 0)],
locations: [0, 0.5, 1])
countDownView.layoutIfNeeded()
countDownTitleView.setGradientLayer(frame: countDownTitleView.bounds,
startPoint: CGPoint(x: 0, y: 0.5),
endPoint: CGPoint(x: 1, y: 0.5),
colors: [UIColor(hexStr: "#5CBBFF", alpha: 0), UIColor(hexStr: "#5CBBFF"), UIColor(hexStr: "#5CBBFF", alpha: 0)],
locations: [0, 0.5, 1])
}
}
// MARK: - UIScrollViewDelegate (page control)
extension SOSPracticeView: UIScrollViewDelegate {
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
guard scrollView == self.scrollView else { return }
let page = Int(scrollView.contentOffset.x / scrollView.bounds.width)
pageControl.currentPage = page
}
}