使用Swift Generics识别子类可以使用自定义类,但不适用于UITapGestureRecognizer [英] Identifying a subclass with Swift Generics works with custom class but not with UITapGestureRecognizer

查看:120
本文介绍了使用Swift Generics识别子类可以使用自定义类,但不适用于UITapGestureRecognizer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在swift中做一些事情,但我无法弄清楚如何实现它,也就是删除给定Class类型的手势识别器,这里是我的代码(和示例),我使用了swift 2.0 Xcode 7测试版5:

我有3个继承自UITapGestureRecognizer的类

  class GestureONE:UIGestureRecognizer {/*...*/} 
class GestureTWO:UIGestureRecognizer {/*...*/}
class GestureTHREE:UIGestureRecognizer {/*...*/}

将它们添加到视图中

  var gesture1 = GestureONE()
var gesture11 = GestureONE()
var gesture2 = GestureTWO()
var gesture22 = GestureTWO()
var gesture222 = GestureTWO()
var gesture3 = GestureTHREE()

var myView = UIView()
myView.addGestureRecognizer(gesture1)
myView.addGestureRecognizer(gesture11)
myView.addGestureRecognizer(gesture2)
myView.addGestureRecognizer(gesture22)
myView.addGestureRecognizer(gesture222)
myView.addGestureRecognizer(gesture3)

object:

  print(myView.gestureRecognizers!)
// playground playground[< __ lldb_expr_224.TapONE:0x7fab52c20b40 ; baseClass = UITapGestureRecognizer;状态=可能; view =< UIView 0x7fab52d259c0>> ;,,< __ldb_expr_224.TapONE:0x7fab52d21250; baseClass = UITapGestureRecognizer;状态=可能; view =< UIView 0x7fab52d259c0>> ;,,< __ldb_expr_224.TapTWO:0x7fab52d24a60; baseClass = UITapGestureRecognizer;状态=可能; view =< UIView 0x7fab52d259c0>> ;,,< __ldb_expr_224.TapTWO:0x7fab52c21130; baseClass = UITapGestureRecognizer;状态=可能; view =< UIView 0x7fab52d259c0>> ;,,< __ldb_expr_224.TapTWO:0x7fab52e13260; baseClass = UITapGestureRecognizer;状态=可能; view =< UIView 0x7fab52d259c0>> ;,,< __lldb_expr_224.TapTHREE:0x7fab52c21410; baseClass = UITapGestureRecognizer;状态=可能; view =< UIView 0x7fab52d259c0>>]

/ p>

 扩展UIView {
func removeGestureRecognizers< T:UIGestureRecognizer>(type:T.Type){
if让手势= self.gestureRecognizers {
用于手势中的手势{
如果手势是T {
removeGestureRecognizer(手势)
}
}
}


$ / code>

然后我使用它

  myView.gestureRecognizers?.count //打印6 
myView.removeGestureRecognizers(GestureTWO)
myView.gestureRecognizers?.count //打印0

删除所有手势D:



这里是对自定义类的实验

  // **动物测试* // 
class Animal {/ * ... * /}

class Dog:Animal {/ * ... * /}
class Cat:Animal {/*...*/}
class Hipo:Animal {/*...*/}

class动物园{
var animals = [Animal]()
}

var zoo = Zoo()

var dog1 = Dog()$ b $ ()
var cat3 = Cat()
var hipo1 = Hipo()
var hipo2 = Hipo()

zoo.animals.append(dog1)
zoo.animals.append(cat1)
zoo.animals.append(cat2)
zoo.animals.append(cat3)
zoo.animals.append(hipo1)
zoo.animals.append(hipo2)

print(zoo.animals)
// playground playground[Dog,Cat ,猫,猫,Hipo,Hipo]

扩展Zoo {
func removeAnimalType< T:Animal>(type:T.Type){
for(index,animal)在animals.enumerate(){
如果animal是T {
animals.removeAtIndex(index)
}
}
}
}

zoo.animals.count //打印6
zoo.removeAnimalType(Cat)
zoo.animals.count //打印3

它实际上是删除它应该的类:D



我在UIGestureRecognizer中丢失了什么?

 扩展UIView {
()函数没有泛型(无聊) func removeActionsTapGestureRecognizer(){
如果让手势= self.gestureRecognizers {
gestures.map({
如果$ 0是ActionsTapGestureRecognizer {
self.removeGestureRecognizer($ 0)
}
})
}
}
}

这当然有用,但我仍然想要一个真正的解决方案。



感谢您的帮助!

注意:我在这里问的第一个问题

解决方案

TL; DR :



使用 dynamicType 来检查每个手势识别器的运行时类型与类型参数。






好的问题。它看起来像是遇到了Objective-C的动态类型和Swift静态类型之间的区别变得清晰的场景。



在Swift中, SomeType .Type 是一个类型的元类型类型,它基本上允许你指定一个编译时类型参数。但是这可能与运行时的类型不一样。

  class BaseClass {...} 
class SubClass :BaseClass {...}

let object:BaseClass = SubClass()



<在上面的例子中, object 的编译时类是 BaseClass ,但是在运行时,它是子类。您可以使用 dynamicType 来检查运行时类:

  print(object。 dynamicType)
//打印SubClass

那为什么这么重要呢?正如您在 Animal 测试中看到的那样,事情表现得如您所料:您的方法需要一个参数,其类型为元类型类型 Animal 子类,然后你只删除符合该类型的动物。编译器知道 T 可以是 Animal 的任何特定子类。但是如果你指定了一个Objective-C类型( UIGestureRecognizer ),编译器会将它的脚趾浸入Objective-C动态类型的不确定世界,直到事情变得难以预测运行时。

我必须警告你我对这里的细节有点毛茸茸......我不知道编译器/运行时混合Swift和Swing世界时处理泛型Objective-C的。也许有人对这个主题有了更好的认识,可以进行阐述!



作为比较,让我们快速尝试一下变体,可以进一步了解Objective-C的世界:

  class SwiftGesture:UIGestureRecognizer {} 

class GestureONE:SwiftGesture {}
class GestureTWO:SwiftGesture {}
class GestureTHREE:SwiftGesture {}
$ b扩展UIView {
func removeGestureRecognizersOfType< T:SwiftGesture>(类型:T.Type){
guard let gestureRecognizers = self.gestureRecognizers else {return}
用于在gestureRecognizers {
self.removeGestureRecognizer(gesture)
}



myView.removeGestureRecognizers(GestureTWO)

使用上面的代码,只有 GestureTWO 实例将被删除,这是我们想要的, Swift类型。 Swift编译器可以查看这个泛型方法声明,而不必关心Objective-C类型。



幸运的是,如上所述,Swift能够检查对象,使用 dynamicType 。有了这些知识,只需稍作调整即可使您的方法适用于Objective-C类型:

  func removeGestureRecognizersOfType< T: UIGestureRecognizer>(类型:T.Type){
guard let gestureRecognizers = self.gestureRecognizers else {return}
用于在gestureRecognizers中设置手势gesture.dynamicType == type {
self.removeGestureRecognizer (手势)
}
}

for循环绑定到 gesture 其中 runtime 类型等于传入的元类型类型值的仅变量手势识别器,因此我们只成功删除指定类型的手势识别器。 p>

There's something I wanted to do in swift, but I couldn't figure out how to achieve it, that is to remove gesture recognisers given a Class Type, here's my code (and example), i'm using swift 2.0 in Xcode 7 beta 5:

I have 3 classes that inherits from UITapGestureRecognizer

class GestureONE: UIGestureRecognizer { /*...*/ }
class GestureTWO: UIGestureRecognizer { /*...*/ }
class GestureTHREE: UIGestureRecognizer { /*...*/ }

Add them to a view

var gesture1 =     GestureONE()
var gesture11 =    GestureONE()
var gesture2 =     GestureTWO()
var gesture22 =    GestureTWO()
var gesture222 =   GestureTWO()
var gesture3 =     GestureTHREE()

var myView = UIView()
myView.addGestureRecognizer(gesture1)
myView.addGestureRecognizer(gesture11)
myView.addGestureRecognizer(gesture2)
myView.addGestureRecognizer(gesture22)
myView.addGestureRecognizer(gesture222)
myView.addGestureRecognizer(gesture3)

I print the object:

print(myView.gestureRecognizers!)
// playground prints "[<__lldb_expr_224.TapONE: 0x7fab52c20b40; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>, <__lldb_expr_224.TapONE: 0x7fab52d21250; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>, <__lldb_expr_224.TapTWO: 0x7fab52d24a60; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>, <__lldb_expr_224.TapTWO: 0x7fab52c21130; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>, <__lldb_expr_224.TapTWO: 0x7fab52e13260; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>, <__lldb_expr_224.TapTHREE: 0x7fab52c21410; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>]"

Have this extension I made with a generic function

extension UIView {
    func removeGestureRecognizers<T: UIGestureRecognizer>(type: T.Type) {
        if let gestures = self.gestureRecognizers {
            for gesture in gestures {
                if gesture is T {
                    removeGestureRecognizer(gesture)
                }
            }
        }
    }
}

Then I use it

myView.gestureRecognizers?.count // Prints 6
myView.removeGestureRecognizers(GestureTWO)
myView.gestureRecognizers?.count // Prints 0

Is removing all of the gestures D:

And here's an experiment with custom classes

//** TEST WITH ANIMALS*//
class Animal { /*...*/ }

class Dog: Animal { /*...*/ }
class Cat: Animal { /*...*/ }
class Hipo: Animal { /*...*/ }

class Zoo {
    var animals = [Animal]()
}

var zoo = Zoo()

var dog1 = Dog()
var cat1 = Cat()
var cat2 = Cat()
var cat3 = Cat()
var hipo1 = Hipo()
var hipo2 = Hipo()

zoo.animals.append(dog1)
zoo.animals.append(cat1)
zoo.animals.append(cat2)
zoo.animals.append(cat3)
zoo.animals.append(hipo1)
zoo.animals.append(hipo2)

print(zoo.animals)
//playground prints "[Dog, Cat, Cat, Cat, Hipo, Hipo]"

extension Zoo {
    func removeAnimalType<T: Animal>(type: T.Type) {
        for (index, animal) in animals.enumerate() {
            if animal is T {
                animals.removeAtIndex(index)
            }
        }
    }
}

zoo.animals.count // prints 6
zoo.removeAnimalType(Cat)
zoo.animals.count // prints 3

It's actually removing the classes it should :D

What am I missing with the UIGestureRecognizer's? I ended up with a workaround making a function that has no generics (boring) like this:

extension UIView {
    func removeActionsTapGestureRecognizer() {
        if let gestures = self.gestureRecognizers {
            gestures.map({
                if $0 is ActionsTapGestureRecognizer {
                    self.removeGestureRecognizer($0)
                }
            })
        }
    }
}

This works of course, but still I would like to have a real solution

I appreciate your help!!

Note: First question I ask here

解决方案

TL;DR:

Use dynamicType to check the runtime type of each gesture recognizer against your type parameter.


Great question. It looks like you're encountering a scenario where the difference between Objective-C's dynamic typing and Swift's static typing becomes clear.

In Swift, SomeType.Type is the metatype type of a type, which essentially allows you to specify a compile-time type parameter. But this might not be the same as the type at runtime.

class BaseClass { ... }
class SubClass: BaseClass { ... }

let object: BaseClass = SubClass()

In the example above, object's compile-time class is BaseClass, but at runtime, it is SubClass. You can check the runtime class with dynamicType:

print(object.dynamicType)
// prints "SubClass"

So why does that matter? As you saw with your Animal test, things behaved as you expected: your method takes an argument whose type is a metatype type of an Animal subclass, and then you only remove animals that conform to that type. The compiler knows that T can be any particular subclass of Animal. But if you specify an Objective-C type (UIGestureRecognizer), the compiler dips its toes into the uncertain world of Objective-C dynamic typing, and things get a little less predictable until runtime.

I must warn you that I'm a bit wooly on the details here... I don't know the specifics of how the compiler/runtime treats generics when mixing the worlds of Swift & Objective-C. Perhaps somebody with some better knowledge of the subject could chip in and elucidate!

As a comparison, let's just quickly try a variation of your method where the compiler can steer a bit further clear of the Objective-C world:

class SwiftGesture: UIGestureRecognizer {}

class GestureONE: SwiftGesture {}
class GestureTWO: SwiftGesture {}
class GestureTHREE: SwiftGesture {}

extension UIView {
    func removeGestureRecognizersOfType<T: SwiftGesture>(type: T.Type) {
        guard let gestureRecognizers = self.gestureRecognizers else { return }
        for case let gesture as T in gestureRecognizers {
            self.removeGestureRecognizer(gesture)
        }
    }
}

myView.removeGestureRecognizers(GestureTWO)

With the above code, only GestureTWO instances will be removed, which is what we want, if only for Swift types. The Swift compiler can look at this generic method declaration without concerning itself with Objective-C types.

Fortunately, as discussed above, Swift is capable of inspecting the runtime type of an object, using dynamicType. With this knowledge, it only takes a minor tweak to make your method work with Objective-C types:

func removeGestureRecognizersOfType<T: UIGestureRecognizer>(type: T.Type) {
    guard let gestureRecognizers = self.gestureRecognizers else { return }
    for case let gesture in gestureRecognizers where gesture.dynamicType == type {
        self.removeGestureRecognizer(gesture)
    }
}

The for loop binds to the gesture variable only gesture recognizers whose runtime type is equal to the passed in metatype type value, so we successfully remove only the specified type of gesture recognizers.

这篇关于使用Swift Generics识别子类可以使用自定义类,但不适用于UITapGestureRecognizer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆