You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为添加了手势的UIView实现类似Switch的开关功能?

用带手势的UIView实现类似UISwitch的开关功能

你的现有代码存在关键问题:直接访问sender的isOn属性,但普通UIView并没有这个属性,运行时会触发错误——因为手势触发时的sender是UIView实例,不是UISwitch。下面是修正后的完整实现方案:

方案一:自定义UIView(推荐)

通过自定义View封装开关状态、手势和UI逻辑,代码更清晰可复用:

import UIKit

class ToggleView: UIView {
    // 存储开关状态,状态变更时自动更新UI
    var isOn = false {
        didSet {
            updateToggleUI()
            applyTheme()
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupBaseStyle()
        addTapGesture()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupBaseStyle()
        addTapGesture()
    }
    
    // 设置开关基础样式
    private func setupBaseStyle() {
        layer.cornerRadius = bounds.height / 2
        backgroundColor = .systemGray5
        clipsToBounds = true
        // 添加滑块子视图,贴近UISwitch视觉效果
        let slider = UIView(frame: CGRect(x: 2, y: 2, width: bounds.height - 4, height: bounds.height - 4))
        slider.layer.cornerRadius = slider.bounds.height / 2
        slider.backgroundColor = .white
        slider.tag = 100
        addSubview(slider)
    }
    
    // 添加点击手势
    private func addTapGesture() {
        let tap = UITapGestureRecognizer(target: self, action: #selector(toggleState))
        addGestureRecognizer(tap)
    }
    
    // 切换开关状态
    @objc private func toggleState() {
        isOn.toggle()
    }
    
    // 更新开关UI(含动画)
    private func updateToggleUI() {
        backgroundColor = isOn ? .systemGreen : .systemGray5
        guard let slider = viewWithTag(100) else { return }
        
        UIView.animate(withDuration: 0.2) {
            slider.frame.origin.x = self.isOn ? self.bounds.width - slider.bounds.width - 2 : 2
        }
    }
    
    // 应用主题切换逻辑
    private func applyTheme() {
        if isOn {
            print("It's on")
            AppearanceService.shared.theme = Themes.light
        } else {
            print("It's off")
            AppearanceService.shared.theme = Themes.dark
        }
        view.window?.overrideUserInterfaceStyle = AppearanceService.shared.theme.getUserInterfaceStyle()
    }
}

使用时直接在控制器中添加这个自定义View即可:

let toggleView = ToggleView(frame: CGRect(x: 80, y: 100, width: 60, height: 30))
view.addSubview(toggleView)

方案二:给普通UIView扩展状态属性

如果不想自定义View,可以通过关联对象给UIView添加isOn属性,快速实现功能:

import UIKit
import ObjectiveC

// 用关联对象存储开关状态
private var ToggleIsOnKey: UInt8 = 0
extension UIView {
    var isOn: Bool {
        get { objc_getAssociatedObject(self, &ToggleIsOnKey) as? Bool ?? false }
        set { objc_setAssociatedObject(self, &ToggleIsOnKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
    }
}

// 控制器中使用
class YourViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        setupToggleView()
    }
    
    private func setupToggleView() {
        let toggleView = UIView(frame: CGRect(x: 80, y: 100, width: 60, height: 30))
        toggleView.layer.cornerRadius = 15
        toggleView.backgroundColor = .systemGray5
        // 添加点击手势
        let tap = UITapGestureRecognizer(target: self, action: #selector(handleToggle(_:)))
        toggleView.addGestureRecognizer(tap)
        view.addSubview(toggleView)
    }
    
    @objc func handleToggle(_ sender: UIView) {
        // 切换状态
        sender.isOn.toggle()
        
        // 更新UI
        sender.backgroundColor = sender.isOn ? .systemGreen : .systemGray5
        
        // 主题切换逻辑
        if sender.isOn {
            print("It's on")
            AppearanceService.shared.theme = Themes.light
        } else {
            print("It's off")
            AppearanceService.shared.theme = Themes.dark
        }
        view.window?.overrideUserInterfaceStyle = AppearanceService.shared.theme.getUserInterfaceStyle()
    }
}

核心修正点:

  • 必须给UIView添加自己的isOn状态存储(自定义类属性或关联对象)
  • 状态切换时要同步更新UI,给用户视觉反馈
  • 避免直接强制转换sender为未知类型访问不存在的属性

内容的提问来源于stack exchange,提问作者lin lin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 05:06:02