Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 在Swift扩展中存储变量的替代方法_Ios_Swift_Delegates_Protocols_Mixpanel - Fatal编程技术网

Ios 在Swift扩展中存储变量的替代方法

Ios 在Swift扩展中存储变量的替代方法,ios,swift,delegates,protocols,mixpanel,Ios,Swift,Delegates,Protocols,Mixpanel,我目前正在开发一个大型应用程序,我们需要能够跟踪从UIView派生的任何自定义类上的特定事件(单击、滑动..)。例如,我们有UITableView的多个子类,它们都需要以不同的组合响应单击和/或滑动事件-这些事件的发生然后将属性发送到外部服务。子类化UIView,并将其用作所有其他自定义子类的父类不是一个选项 更重要的是,这些事件发生时发送的数据会根据显示UI元素的应用程序页面的不同而有所不同。我的第一个想法是创建一个可跟踪的协议。理想情况下,我希望将所有用于设置手势识别器的样板代码放在该协议的

我目前正在开发一个大型应用程序,我们需要能够跟踪从
UIView
派生的任何自定义类上的特定事件(单击、滑动..)。例如,我们有
UITableView
的多个子类,它们都需要以不同的组合响应单击和/或滑动事件-这些事件的发生然后将属性发送到外部服务。子类化
UIView
,并将其用作所有其他自定义子类的父类不是一个选项

更重要的是,这些事件发生时发送的数据会根据显示UI元素的应用程序页面的不同而有所不同。我的第一个想法是创建一个可跟踪的协议。理想情况下,我希望将所有用于设置手势识别器的样板代码放在该协议的扩展中,但不能,因为
#selector
语法需要
@objc
注释,而该注释在协议扩展中不可用

此外,当我尝试扩展
UIView
时,我不再能够访问
Trackable
协议所需的属性,并且无法添加它的默认实现,因为如上所述,扩展不支持变量声明。下面是我想要实现的一个(非常粗略的)想法。这可能吗?是否存在更好的模式?我还研究了委托模式,它不能解决上述任何问题

protocol Trackable: class {
    var propertiesToSend: [String : [String : String]] { get set }
}

extension UIView: Trackable {
    //need alternative way to achieve this, as it is not allowed here
    var propertiesToSend = [:] 

    func subscribe(to event: String, with properties: [String : String]) {
        propertiesToSend[event] = properties
        startListening(for: event)
    }

    func unsubscribe(from event: String) {
        propertiesToSend.removeValue(forKey: event)
    }

    private func startListening(for event: String) {
        switch (event) {
            case "click":
                let clickRecogniser = UITapGestureRecognizer(target: self, action: #selector(track(event:)))
                addGestureRecognizer(clickRecogniser)

            case "drag":
                for direction: UISwipeGestureRecognizerDirection in [.left, .right] {
                    let swipeRecogniser = UISwipeGestureRecognizer(target: self, action: #selector(track(event:)))
                    swipeRecogniser.direction = direction
                    addGestureRecognizer(swipeRecogniser)
                }

            default: return
        }
    }

    @objc
    func track(event: UIEvent) {
        let eventString: String

        switch (event.type) {
            case .touches:
                eventString = "click"
            case .motion:
                eventString = "drag"
            default: return
        }

        if let properties = propertiesToSend[eventString] {
            sendPropertiesToExternalService("Interaction", properties: properties)
        }
    }
}

不要让它变得比需要的更复杂
UIView
及其子类必须派生自
NSObject
。阅读有关
objc\u getAssociatedObject
objc\u getAssociatedObject
的文档。不需要协议或其他抽象

import ObjectiveC

private var key: Void? = nil // the address of key is a unique id.

extension UIView {
    var propertiesToSend: [String: [String: String]] {
        get { return objc_getAssociatedObject(self, &key) as? [String: [String: String]] ?? [:] }
        set { objc_setAssociatedObject(self, &key, newValue, .OBJC_ASSOCIATION_RETAIN) }
    }
}
这可以按如下方式使用

let button = UIButton()

button.propertiesToSend = ["a": ["b": "c"]]
print(button.propertiesToSend["a"]?["b"] ?? "unknown")

我能够在此基础上实现我的目标,非常感谢:)