|
@@ -0,0 +1,375 @@
|
|
|
|
+//
|
|
|
|
+// TSSOSLigntVC.swift
|
|
|
|
+// Girly
|
|
|
|
+//
|
|
|
|
+// Created by 100Years on 2025/1/14.
|
|
|
|
+//
|
|
|
|
+
|
|
|
|
+class TSSOSLigntVC: TSBaseVC {
|
|
|
|
+
|
|
|
|
+ private var isOpen = false
|
|
|
|
+ private var currentValue:CGFloat = 0.5
|
|
|
|
+
|
|
|
|
+ lazy var shapeView: TSLightShape1View = {
|
|
|
|
+ let shapeView = TSLightShape1View()
|
|
|
|
+ shapeView.imageNamed = "sos_shape"
|
|
|
|
+ shapeView.color = "#FF313D".color
|
|
|
|
+ return shapeView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ let sosFlashlight = SOSFlashlight()
|
|
|
|
+
|
|
|
|
+ lazy var setToolView: TSSOSSetToolView = {
|
|
|
|
+ let setToolView = TSSOSSetToolView()
|
|
|
|
+ setToolView.changedValueComplete = { [weak self] value,isEnd in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+ currentValue = value
|
|
|
|
+
|
|
|
|
+ UIDevice.brightness(brightness: currentValue)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ setToolView.clickDoneComplete = { [weak self] in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+ isOpen = !isOpen
|
|
|
|
+
|
|
|
|
+ if isOpen {
|
|
|
|
+ UIView.animate(withDuration: 0.5, delay: 0, options: []) {
|
|
|
|
+ setToolView.isHidden = true
|
|
|
|
+ }
|
|
|
|
+ UIDevice.keepScreenAwake()
|
|
|
|
+ sosFlashlight.start()
|
|
|
|
+ setToolView.submitBtn.setTitle("Close".localized, for: .normal)
|
|
|
|
+ }else{
|
|
|
|
+ UIDevice.allowScreenSleep()
|
|
|
|
+ sosFlashlight.stop()
|
|
|
|
+ setToolView.submitBtn.setTitle("Open".localized, for: .normal)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return setToolView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ override func createView() {
|
|
|
|
+
|
|
|
|
+ view.insertSubview(shapeView, at: 0)
|
|
|
|
+ shapeView.snp.makeConstraints { make in
|
|
|
|
+ make.top.equalTo(10)
|
|
|
|
+ make.centerX.equalToSuperview()
|
|
|
|
+ make.width.equalTo(k_ScreenWidth)
|
|
|
|
+ make.height.equalToSuperview()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ contentView.layoutIfNeeded()
|
|
|
|
+ self.shapeView.addContent()
|
|
|
|
+
|
|
|
|
+ addNormalNavBarView()
|
|
|
|
+ setPageTitle("")
|
|
|
|
+
|
|
|
|
+ self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showToView)))
|
|
|
|
+
|
|
|
|
+ contentView.addSubview(setToolView)
|
|
|
|
+ setToolView.snp.makeConstraints { make in
|
|
|
|
+ make.leading.trailing.equalTo(0)
|
|
|
|
+ make.bottom.equalTo(0)
|
|
|
|
+ make.height.equalTo(282.0)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @objc func showToView() {
|
|
|
|
+ if setToolView.isHidden == true {
|
|
|
|
+ UIView.animate(withDuration: 0.5, delay: 0, options: []) {
|
|
|
|
+ self.setToolView.isHidden = false
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ self.setToolView.submitBtn.setTitle(self.isOpen ? "Close" : "Open", for: .normal)
|
|
|
|
+ UIDevice.allowScreenSleep()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ deinit {
|
|
|
|
+ UIDevice.allowScreenSleep()
|
|
|
|
+ sosFlashlight.stop()
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+class TSSOSSetToolView: TSBaseView {
|
|
|
|
+
|
|
|
|
+ var clickDoneComplete:(()->Void)?
|
|
|
|
+ var changedValueComplete:((CGFloat,Bool)->Void)?{
|
|
|
|
+ didSet{
|
|
|
|
+ flashSliderView.changedValueComplete = changedValueComplete
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ lazy var bottomView: UIView = {
|
|
|
|
+ let bottomView = UIView()
|
|
|
|
+ bottomView.backgroundColor = .cardBgColor
|
|
|
|
+ return bottomView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ var isShow = true
|
|
|
|
+
|
|
|
|
+ lazy var titleLabel1: UILabel = {
|
|
|
|
+ let titleLabel1 = UILabel.createLabel(text: "Brightness".localized,font: .font(size: 14,weight: .medium),textColor: .white)
|
|
|
|
+ return titleLabel1
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ lazy var flashSliderView: TSFlashSliderView = {
|
|
|
|
+ let flashSliderView = TSFlashSliderView()
|
|
|
|
+ flashSliderView.setSliderValue(minimumValue: 0.0, maximumValue: 1.0, value: 0.5)
|
|
|
|
+ flashSliderView.setSliderImage(leftImage: UIImage(named: "reduceBrightness")!, rightImage: UIImage(named: "brightness")!)
|
|
|
|
+ return flashSliderView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ lazy var submitBtn: UIButton = {
|
|
|
|
+ let submitBtn = createNormalSubmitBtn(title: "Open".localized) { [weak self] in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+ isShow = !isShow
|
|
|
|
+ clickDoneComplete?()
|
|
|
|
+ }
|
|
|
|
+ submitBtn.cornerRadius = 22.0;
|
|
|
|
+ return submitBtn
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ override func creatUI() {
|
|
|
|
+
|
|
|
|
+ contentView.addSubview(bottomView)
|
|
|
|
+ bottomView.snp.makeConstraints { make in
|
|
|
|
+ make.edges.equalToSuperview()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let cell1 = creatCell(subView: titleLabel1)
|
|
|
|
+ bottomView.addSubview(cell1)
|
|
|
|
+ cell1.snp.makeConstraints { make in
|
|
|
|
+ make.leading.equalTo(0)
|
|
|
|
+ make.trailing.equalTo(0)
|
|
|
|
+ make.top.equalTo(20)
|
|
|
|
+ make.height.equalTo(44)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bottomView.addSubview(flashSliderView)
|
|
|
|
+ flashSliderView.snp.makeConstraints { make in
|
|
|
|
+ make.top.equalTo(cell1.snp.bottom)
|
|
|
|
+ make.leading.equalTo(16)
|
|
|
|
+ make.trailing.equalTo(-16)
|
|
|
|
+ make.height.equalTo(48)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bottomView.addSubview(submitBtn)
|
|
|
|
+ submitBtn.snp.makeConstraints { make in
|
|
|
|
+ make.leading.equalTo(50)
|
|
|
|
+ make.trailing.equalTo(-51)
|
|
|
|
+ make.bottom.equalTo(-34)
|
|
|
|
+ make.height.equalTo(44)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ kDelayMainShort {
|
|
|
|
+ self.bottomView.cornersRound(radius: 20, corner: [.topLeft,.topRight])
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func creatCell(subView:UIView) -> UIView{
|
|
|
|
+ let view = UIView()
|
|
|
|
+ view.addSubview(subView)
|
|
|
|
+ subView.snp.makeConstraints { make in
|
|
|
|
+ make.leading.equalTo(16)
|
|
|
|
+ make.trailing.equalTo(0)
|
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
|
+ }
|
|
|
|
+ return view
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class TSLightShape1View: TSBaseView {
|
|
|
|
+
|
|
|
|
+ private var shwoWidth:CGFloat = k_ScreenWidth
|
|
|
|
+ private var less:CGFloat = 5.0
|
|
|
|
+ lazy var bgShapeView: UIView = {
|
|
|
|
+ let shapeView = UIView()
|
|
|
|
+ shapeView.frame = CGRect(x: 0, y: 0, width: shwoWidth-less, height: shwoWidth-less)
|
|
|
|
+ shapeView.backgroundColor = color
|
|
|
|
+ return shapeView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ lazy var blurView: UIView = {
|
|
|
|
+ let blurView = createBlurEffectView(style:.extraLight,backgroundColor: .clear)
|
|
|
|
+ blurView.frame = CGRect(x: 0, y: 0, width: shwoWidth, height: shwoWidth)
|
|
|
|
+ return blurView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ lazy var shapeView: UIView = {
|
|
|
|
+ let shapeView = UIView()
|
|
|
|
+ shapeView.frame = CGRect(x: 0, y: 0, width: shwoWidth, height: shwoWidth)
|
|
|
|
+ shapeView.backgroundColor = color
|
|
|
|
+ return shapeView
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ var imageNamed:String = "bold_roundness_shape" {
|
|
|
|
+ didSet{
|
|
|
|
+ if UIImage(named: imageNamed) != nil {
|
|
|
|
+ setShapeView(shapeView: bgShapeView)
|
|
|
|
+ setShapeView(shapeView: shapeView)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var color:UIColor = .white {
|
|
|
|
+ didSet{
|
|
|
|
+ shapeView.backgroundColor = color
|
|
|
|
+ bgShapeView.backgroundColor = color
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ override func creatUI() {
|
|
|
|
+ self.backgroundColor = .mainBg
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ func addContent() {
|
|
|
|
+ shwoWidth = min(contentView.width, contentView.height)
|
|
|
|
+ contentView.addSubview(bgShapeView)
|
|
|
|
+ contentView.addSubview(blurView)
|
|
|
|
+ contentView.addSubview(shapeView)
|
|
|
|
+
|
|
|
|
+ let bottom:CGFloat = 0.0
|
|
|
|
+ let space:CGFloat = 0.0
|
|
|
|
+ bgShapeView.snp.makeConstraints { make in
|
|
|
|
+ make.leading.equalTo(space+less)
|
|
|
|
+ make.trailing.equalTo(-less-space)
|
|
|
|
+ make.top.equalTo(bottom)
|
|
|
|
+ make.bottom.equalTo(-bottom)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ blurView.snp.makeConstraints { make in
|
|
|
|
+ make.leading.equalTo(space)
|
|
|
|
+ make.trailing.equalTo(-space)
|
|
|
|
+ make.top.equalTo(bottom)
|
|
|
|
+ make.bottom.equalTo(-bottom)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ shapeView.snp.makeConstraints { make in
|
|
|
|
+ make.leading.equalTo(space)
|
|
|
|
+ make.trailing.equalTo(-space)
|
|
|
|
+ make.top.equalTo(bottom)
|
|
|
|
+ make.bottom.equalTo(-bottom)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.setNeedsLayout()
|
|
|
|
+ kDelayMainShort {
|
|
|
|
+ self.setShapeView(shapeView: self.bgShapeView)
|
|
|
|
+ self.setShapeView(shapeView: self.shapeView)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for subview in blurView.subviews {
|
|
|
|
+ if String(describing: type(of: subview)).contains("UIVisualEffectSubview") {
|
|
|
|
+ subview.isHidden = true
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func setShapeView(shapeView:UIView){
|
|
|
|
+ if let image = UIImage(named: imageNamed) {
|
|
|
|
+ shapeView.setImageMask(image: image,scaleFactor: 0.7)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+import AVFoundation
|
|
|
|
+
|
|
|
|
+class SOSFlashlight {
|
|
|
|
+ private var isRunning = false
|
|
|
|
+ private let device = AVCaptureDevice.default(for: .video)
|
|
|
|
+
|
|
|
|
+ /// 开始闪烁
|
|
|
|
+ func start() {
|
|
|
|
+ guard let device = device, device.hasTorch else {
|
|
|
|
+ print("Device does not support torch")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if isRunning { return } // 防止重复调用
|
|
|
|
+ isRunning = true
|
|
|
|
+
|
|
|
|
+ DispatchQueue.global().async { [weak self] in
|
|
|
|
+ while self?.isRunning == true {
|
|
|
|
+ self?.flashSOSPattern()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// 停止闪烁
|
|
|
|
+ func stop() {
|
|
|
|
+ isRunning = false
|
|
|
|
+ turnTorch(on: false)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// 按摩斯码的模式闪烁
|
|
|
|
+ private func flashSOSPattern() {
|
|
|
|
+ // 短闪:0.2 秒,长闪:0.6 秒,间隔:0.2 秒
|
|
|
|
+ let shortDuration: TimeInterval = 0.2
|
|
|
|
+ let longDuration: TimeInterval = 0.6
|
|
|
|
+ let pauseDuration: TimeInterval = 0.2
|
|
|
|
+
|
|
|
|
+ // 短闪三次
|
|
|
|
+ for _ in 0..<3 {
|
|
|
|
+ flash(on: true, for: shortDuration)
|
|
|
|
+ pause(for: pauseDuration)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 长闪三次
|
|
|
|
+ for _ in 0..<3 {
|
|
|
|
+ flash(on: true, for: longDuration)
|
|
|
|
+ pause(for: pauseDuration)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 短闪三次
|
|
|
|
+ for _ in 0..<3 {
|
|
|
|
+ flash(on: true, for: shortDuration)
|
|
|
|
+ pause(for: pauseDuration)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 间隔 1 秒后重复
|
|
|
|
+ pause(for: 1.0)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// 控制闪光灯开关并延迟指定时间
|
|
|
|
+ private func flash(on: Bool, for duration: TimeInterval) {
|
|
|
|
+ turnTorch(on: on)
|
|
|
|
+ pause(for: duration)
|
|
|
|
+ turnTorch(on: false)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// 打开或关闭闪光灯
|
|
|
|
+ private func turnTorch(on: Bool) {
|
|
|
|
+ guard let device = device, device.hasTorch else { return }
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ try device.lockForConfiguration()
|
|
|
|
+ device.torchMode = on ? .on : .off
|
|
|
|
+ if on {
|
|
|
|
+ try device.setTorchModeOn(level: AVCaptureDevice.maxAvailableTorchLevel)
|
|
|
|
+ }
|
|
|
|
+ device.unlockForConfiguration()
|
|
|
|
+ } catch {
|
|
|
|
+ print("Torch could not be used: \(error)")
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// 延迟指定时间
|
|
|
|
+ private func pause(for duration: TimeInterval) {
|
|
|
|
+ Thread.sleep(forTimeInterval: duration)
|
|
|
|
+ }
|
|
|
|
+}
|