使用多个通用

时间:2018-01-05 00:37:35

标签: swift generics operator-overloading protocols

我试图定义一个帮助方法来为UIViews设置AutoLayout约束。理想情况下,它应该是UIView类中定义的函数anchor,它以这种方式运行:

let A = UIView()
let B = UIView()
B.anchor(left: A.rightAnchor + 24, top: A.bottomAnchor, width: A.widthAnchor + 100, height: 200)

其中A.rightAnchor + 24表示第二个视图的leftAnchor应该被约束到第一个视图的rightAnchor,常量为24。

以下是我为实现此行为而尝试做的事情:

为了简化,我们假设anchor(_ left:)只有一个参数可以是:

  • NSLayoutXAxisAnchor
  • NSLayoutXAxisAnchor + CGFloat

所有这些输入类型必须实现的协议

protocol LayoutProtocol {
    // LayoutType can be NSLayoutXAxisAnchor, NSLayoutYAxisAnchor or NSLayoutDimension
    associatedtype LayoutType

    var anchor: NSLayoutAnchor<LayoutType> { get }
    var constant: CGFloat { get }
}

现在让NSLayoutXAxisAnchorNSLayoutYAxisAnchor符合该协议:

extension NSLayoutXAxisAnchor: LayoutProtocol {
    typealias LayoutType = NSLayoutXAxisAnchor

    var anchor: NSLayoutAnchor<NSLayoutXAxisAnchor> { return self }
    var constant: CGFloat { return 0 }
}

extension NSLayoutYAxisAnchor: LayoutProtocol {
    typealias LayoutType = NSLayoutYAxisAnchor

    var anchor: NSLayoutAnchor<NSLayoutYAxisAnchor> { return self }
    var constant: CGFloat { return 0 }
}

NSLayoutAnchor<AnchorType>CGFloat

之间的Sum运算符

让我们定义一个符合该协议的结构:

struct LayoutOperation<T>: LayoutProtocol {
    typealias LayoutType = T

    var anchor: NSLayoutAnchor<T>
    var constant: CGFloat
}

现在,sum运算符可以返回该结构:

func +<T>(left: NSLayoutAnchor<T>, right: CGFloat) -> LayoutOperation<T> {
    return LayoutOperation<T>(anchor: left, constant: right)
}

只有一个参数的锚功能

最后,我们可以扩展UIView类:

extension UIView {

    func anchor<T: LayoutProtocol>(_ left: T? = nil) {
        translatesAutoresizingMaskIntoConstraints = false
        var constraints = [NSLayoutConstraint]()

        if let left = left {
            constraints.append(leftAnchor.constraint(equalTo: left.anchor as! NSLayoutAnchor<NSLayoutXAxisAnchor>, constant: left.constant))
        }

        constraints.forEach { $0.isActive = true }
    }

}

一切正常,我可以单独传递一个NSLayoutXAxisAnchor,或者在右侧传递IntDouble

现在让我们专注于概括结构以及向anchor()添加其他参数。

  • LayoutProtocol是通用的
  • LayoutOperation<T>也是一般性的
  • sum运算符也可以处理每个NSLayoutAnchor专业化

不应该有问题,所以:

extension UIView {

    func anchor<T: LayoutProtocol, U: LayoutProtocol>(left: T? = nil, top: U? = nil) {
        translatesAutoresizingMaskIntoConstraints = false
        var constraints = [NSLayoutConstraint]()

        if let left = left {
            constraints.append(leftAnchor.constraint(equalTo: left.anchor as! NSLayoutAnchor<NSLayoutXAxisAnchor>, constant: left.constant))
        }
        if let top = top {
            constraints.append(topAnchor.constraint(equalTo: top.anchor as! NSLayoutAnchor<NSLayoutYAxisAnchor>, constant: top.constant))
        }

        constraints.forEach { $0.isActive = true }
    }

}

TU是不同的泛型,因为水平和垂直锚点具有不同的特殊类型(总共3种泛型类型足以处理各种布局锚点。)

测试用例:

B.anchor(left: A.rightAnchor + 24, top: A.bottomAnchor)
B.anchor(left: A.rightAnchor + 24, top: A.bottomAnchor + 12)
B.anchor(left: A.rightAnchor, top: A.bottomAnchor)
B.anchor(left: A.leftAnchor, top: A.topAnchor + 120.5)

所有工作都按预期正常。

但如果我只传递一个参数,无论是左边还是顶部:

B.anchor(left: A.rightAnchor + 24)
B.anchor(top: A.bottomAnchor)

Swift不再编译,出现以下错误

  

Cannot convert call result type 'LayoutOperation<_>' to expected type '_?'

我还尝试添加where来指定相关类型,但没有运气:

extension UIView {

    func anchor<T: LayoutProtocol, U: LayoutProtocol>(left: T? = nil, top: U? = nil)
      where T.LayoutType == NSLayoutXAxisAnchor,
            U.LayoutType == NSLayoutYAxisAnchor {
        translatesAutoresizingMaskIntoConstraints = false
        var constraints = [NSLayoutConstraint]()

        if let left = left {
            constraints.append(leftAnchor.constraint(equalTo: left.anchor, constant: left.constant))
        }
        if let top = top {
            constraints.append(topAnchor.constraint(equalTo: top.anchor, constant: top.constant))
        }

        constraints.forEach { $0.isActive = true }
    }

}

仍然是同样的错误。

有关如何解决此问题的任何建议?我对Swift来说相对较新,所以这肯定是处理像这样(酷)功能的最糟糕方式。除此之外,先谢谢!

0 个答案:

没有答案