Ios 使用通用协议筛选子视图数组

Ios 使用通用协议筛选子视图数组,ios,swift,class,generics,protocols,Ios,Swift,Class,Generics,Protocols,在我们的应用程序中,我们有一个画布。画布可以包含标签、图像、文本等。我们有一个协议CanvasItem,实现这些项目之间的公共属性: protocol CanvasItemProtocol{ var scale : CGFloat { get set } } class CanvasItem : CanvasItemProtocol { public var scale : CGFloat = 1.0 } 然后,每个类模型(标签、文本等)都符合CanvasItem,添加了特定于类的

在我们的应用程序中,我们有一个画布。画布可以包含标签、图像、文本等。我们有一个协议
CanvasItem
,实现这些项目之间的公共属性:

protocol CanvasItemProtocol{
  var scale : CGFloat { get set }
}

class CanvasItem : CanvasItemProtocol {
   public var scale : CGFloat = 1.0
}
然后,每个类模型(标签、文本等)都符合
CanvasItem
,添加了特定于类的属性:

public class StickerItem: CanvasItem {
  public var stickerName: String
}

public class ShapeItem: CanvasItem {
  public var shapeColor: UIColor
}
为了展示这些特性,我们首先创建了一个基本的泛型(我认为)
UIView
类,它只能用
CanvasItem
初始化:

class ViewItem <T: CanvasItemProtocol>: UIView {
let canvasItem: T
init (t: T) {
    self.canvasItem = t
    super .init(frame: .zero)
  }
}
class视图项:UIView{
让我们来拉票项目:T
初始(t:t){
self.canvasItem=t
super.init(帧:.0)
}
}
然后,我们为每个模型创建特定的
UIView
类:

class CanvasShapeView: ViewClass<ShapeItem> { }

class CanvasStickerView: ViewClass<StickerItem> { }
class CanvasShapeView:ViewClass{}
类视图:视图类{}
然后我试着做以下几点:

let superview = UIView()
let shapeView = CanvasStickerView(StickerItem())
let stickerView = CanvasShapeView(ShapeItem())

superview.addSubview(shapeView)
superview.addSubview(stickerView)

for canvasItemView in superview.subviews.compactMap({$0 as? ViewItem<CanvasItem>}) {
   print(canvasItemView.canvasItem.scale) // **access only the common properties**
}
let superview=UIView()
让shapeView=CanvasStickView(StickItem())
让粘贴视图=CanvasShapeView(ShapeItem())
superview.addSubview(shapeView)
superview.addSubview(粘贴视图)
用于superview.subviews.compactMap({$0 as?ViewItem})中的canvasItemView{
打印(canvasItemView.canvasItem.scale)//**仅访问公共属性**
}
它编译,但返回零结果(铸造不工作)。。。 我试图仅访问
画布项目
公共属性

主要链接:

有什么建议吗?我们坚持了好几天了


非常感谢您的帮助。

协议和泛型以及子类化不能很好地协同工作

实际上,您既不需要协议也不需要视图包装器类。子类化
UIView
就足够了

class CanvasItem : UIView
{
    public var scale : CGFloat = 1.0
}

class StickerItem: CanvasItem
{
    public var stickerName : String = ""
}

class ShapeItem: CanvasItem
{
    public var color: UIColor = .red
}

let shapeItem = ShapeItem()
let stickerItem = StickerItem()
let superview = UIView()
superview.addSubview(shapeItem)
superview.addSubview(stickerItem)

for case let canvasItemView as CanvasItem in superview.subviews {
    print(canvasItemView.scale)
}

CanvasShapeView
canvasstackerview
共享类的模板,但它们是完全不同的类

这就是为什么我要检查类是否符合相同的模板-搜索
canvasItem
作为类的一部分,然后尝试获取所需的值:

let test = superview.subviews
    .compactMap({ Mirror(reflecting: $0).children.first(where: { $0.label == "canvasItem" }) })
    .compactMap({ $0.value as? CanvasItemProtocol })
for value in test {
    print(value.scale)
}

为了做到这一点,我不得不使用反射——我想这不是最好的解决方案,但它做到了。

嘿,瓦迪安,谢谢你的回答。对于特定的实现,是否仍然可以使用子类
CanvasShapeView
CanvasStickeView
,并且仍然可以使用此机制?基本上,不可能将基类
UIView
强制转换为泛型类型包装器。具有关联类型的协议可能是一个解决方案。这是一个梦想,不必在每个子类中重复代码。。。如果这不是太多的要求(问我这几天都在努力),你能添加一个简单的愚蠢的样本吗?这将是一个很好的帮助,但基类的目标是实现其中的所有公共属性并在子类中重写它们。对于考虑不同子类类型的常用方法,一个具有关联类型或甚至是协议扩展的协议是可取的。忘记提及一个关键的事情。我很抱歉。我们使用
CanvasItem
作为数据库的模型,因此它也继承了不会编译的
Codable
。是否仍有机会使用此机制并符合Codable?很抱歉没有添加它,我在实现时只注意到了一个。很好的方法,但不够干净,不适合生产应用程序哈哈。谢谢你!是的,我会厌倦在生产中使用这个。。。好吧,如果其他一切都失败了,至少你现在有了“B计划”