Swift 5.8使用any泛型触发_ErrorCodeProtocol无成员错误求助
问题
在UIKit程序化布局的宠物项目中,为简化布局代码实现了UISnapable协议(让UIView和UILayoutGuide遵循)、泛型Constraints枚举,并编写applyConstraints函数,使用any UISnapable作为可变参数。运行时出现错误:Type _ErrorCodeProtocol has no member alignTopWithTop,无法定位该协议与代码的关联。
相关代码示例
调用代码
// view是视图控制器的view let searchArea = UIView() view.addSubview(searchArea) searchArea.applyConstraints( .alignTopWithTop(of: guide), .alignLeadingWithLeading(of: view), .alignTrailingWithTrailing(of: view), .alignHeight(with: view, mult: 0.1) )
协议与枚举定义
protocol UISnapable { var leadingAnchor: NSLayoutXAxisAnchor { get } var centerYAnchor: NSLayoutYAxisAnchor { get } } extension UIView: UISnapable {} extension UILayoutGuide: UISnapable {} enum Constraints<T: UISnapable> { case alignTopWithTop(of: T, const: CGFloat = 0) case setAspectWidth(ratio: CGFloat) }
函数实现
func applyConstraints(object: inout UIView, constraints: any UISnapable...) { object.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate( constraints.map { switch $0 { case .alignTopWithTop(of: let of, const: let const): return object.topAnchor.constraint( equalTo: of.topAnchor, constant: const ) case .setAspectWidth(ratio: let ratio): return object.heightAnchor.constraint( equalTo: object.widthAnchor, multiplier: ratio ) } } ) }
问题原因
- 类型完全不匹配:
applyConstraints函数参数声明为any UISnapable...,但调用时传入的是Constraints枚举实例,两者无继承/关联关系。Swift类型推断失败后抛出了无关的底层协议错误(_ErrorCodeProtocol是Swift内部错误处理协议,和当前布局逻辑无关)。 - 协议缺少必要Anchor定义:
UISnapable仅定义了leadingAnchor和centerYAnchor,但调用的alignTopWithTop、alignTrailingWithTrailing等约束需要topAnchor、trailingAnchor、heightAnchor等,即使类型问题解决,后续也会编译失败。 - 函数设计不合理:将
applyConstraints设计为独立函数并使用inout参数没必要,作为UIView的扩展方法更符合Swift API设计习惯;泛型Constraints枚举的设计增加了不必要的复杂度。
修正后的代码
1. 完善UISnapable协议
补充布局所需的所有Anchor:
protocol UISnapable { var leadingAnchor: NSLayoutXAxisAnchor { get } var trailingAnchor: NSLayoutXAxisAnchor { get } var topAnchor: NSLayoutYAxisAnchor { get } var bottomAnchor: NSLayoutYAxisAnchor { get } var centerYAnchor: NSLayoutYAxisAnchor { get } var widthAnchor: NSLayoutDimension { get } var heightAnchor: NSLayoutDimension { get } } extension UIView: UISnapable {} extension UILayoutGuide: UISnapable {}
2. 调整Constraints枚举(移除泛型,简化定义)
enum Constraints { case alignTopWithTop(of: any UISnapable, constant: CGFloat = 0) case alignLeadingWithLeading(of: any UISnapable, constant: CGFloat = 0) case alignTrailingWithTrailing(of: any UISnapable, constant: CGFloat = 0) case alignHeight(with: any UISnapable, multiplier: CGFloat = 1) case setAspectWidth(ratio: CGFloat) }
3. 实现UIView扩展方法
extension UIView { func applyConstraints(_ constraints: Constraints...) { self.translatesAutoresizingMaskIntoConstraints = false let layoutConstraints = constraints.map { constraint in switch constraint { case .alignTopWithTop(of: let snapable, constant: let constant): return self.topAnchor.constraint(equalTo: snapable.topAnchor, constant: constant) case .alignLeadingWithLeading(of: let snapable, constant: let constant): return self.leadingAnchor.constraint(equalTo: snapable.leadingAnchor, constant: constant) case .alignTrailingWithTrailing(of: let snapable, constant: let constant): return self.trailingAnchor.constraint(equalTo: snapable.trailingAnchor, constant: constant) case .alignHeight(with: let snapable, multiplier: let multiplier): return self.heightAnchor.constraint(equalTo: snapable.heightAnchor, multiplier: multiplier) case .setAspectWidth(ratio: let ratio): return self.heightAnchor.constraint(equalTo: self.widthAnchor, multiplier: ratio) } } NSLayoutConstraint.activate(layoutConstraints) } }
4. 调用代码(仅需匹配枚举参数名)
let searchArea = UIView() view.addSubview(searchArea) searchArea.applyConstraints( .alignTopWithTop(of: guide), .alignLeadingWithLeading(of: view), .alignTrailingWithTrailing(of: view), .alignHeight(with: view, multiplier: 0.1) )
注:若要保留调用时的
mult参数名,可将Constraints枚举中alignHeight的参数名改为mult。
内容的提问来源于stack exchange,提问作者Alexey_BH
相关产品推荐
相关产品推荐

