在函数中使用 “any generic” 导致 “_ErrorCodeProtocol has no member” 错误。

huangapple go评论74阅读模式
英文:

Swift: "any generic" in function causes "_ErrorCodeProtocol has no member" error

问题

Swift 5.8 使用 'any generic' 导致奇怪的错误 (_ErrorCodeProtocol)

你好!在我的宠物项目中,我想要减少 UIKit 的布局代码(我使用程序化的 UI 布局)。我的想法是为我的宠物项目创建一个用于最常见情况的枚举,例如:

// view 是一个视图控制器的视图
let searchArea = UIView()
view.addSubview(searchArea)
searchArea.applyConstraints(
   .alignTopWithTop(of: guide),
   .alignLeadingWithLeading(of: view),
   .alignTrailingWithTrailing(of: view),
   .alignHeight(with: view, mult: 0.1)
)

我真的很喜欢它,比起下面的方式更好:

let searchArea = UIView()
view.addSubview(searchArea)
object.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
  searchArea.topAnchor.constraint(equalTo: view.topAnchor, constant: 0),
  searchArea.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
  searchArea.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0),
  searchArea.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.1, constant: 0)
])

因为 UIViews 应该与其他 UIView 子视图以及 UIViewController 安全区域 (UILayoutGuide) 对齐,我使用了泛型来实现这一点:

protocol UISnapable {
    var leadingAnchor: NSLayoutXAxisAnchor { get }
    // 这里包括 UIView 和 UILayoutGuide 同时拥有的其他锚点
    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
                )
            }
        }
    )
}

我在泛型 UISnapable 中使用了 "any" 关键字,因为它最近在 Swift 中为这种情况引入了。

但不幸的是,我遇到了错误 "Type _ErrorCodeProtocol has no member alignTopWithTop"。

我做错了什么?我不知道 _ErrorCodeProtocol 如何与我的泛型情况相关联。

我检查了 _ErrorCodeProtocol 的源代码,它指向 Foundation。但我没有在 Foundation 中找到它的声明。而且在 Apple 文档中也没有关于它的信息(https://developer.apple.com/search/?q=_ErrorCodeProtocol&type=Documentation)。

英文:

Swift 5.8 using 'any generic' causes strange error (_ErrorCodeProtocol)

Hello! In my Pet project I want to reduce layout code for UIKit (I'm using programmatic UI layout). The idea is to make enum for most used cases in my pet like:

// view is a view controller&#39;s view
let searchArea = UIView()
view.addSubview(searchArea)
searchArea.applyConstraints(
   .alignTopWithTop(of: guide),
   .alignLeadingWithLeading(of: view),
   .alignTrailingWithTrailing(of: view),
   .alignHeight(with: view, mult: 0.1)
)

I really like it much more than:

let searchArea = UIView()
view.addSubview(searchArea)
object.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
  searchArea.topAnchor.constraint(equalTo: view.topAnchor, constant: 0),
  searchArea.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
  searchArea.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0),
  searchArea.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.1, constant: 0)
])

Because UIViews should be aligned to other UIView childs as well as UIViewController safe Area (UILayoutGuide) I implemented this using generics:

protocol UISnapable {
    var leadingAnchor: NSLayoutXAxisAnchor { get }
    // Here are other anchors that UIView and UILayoutGuide have at the same time
    var centerYAnchor: NSLayoutYAxisAnchor { get }
}


extension UIView: UISnapable {}
extension UILayoutGuide: UISnapable {}

enum Constraints&lt;T: UISnapable&gt; {
    case alignTopWithTop(of: T, const: CGFloat = 0)
    // Other align cases here
    case setAspectWidth(ratio: CGFloat)
}


func applyConstraints(object: inout UIView, constraints: any UISnapable...) {
    object.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate(
        // Transforming input variadic to constraints
        constraints.map {
            switch $0 {
            case .alignTopWithTop(of: let of, const: let const):
                return object.topAnchor.constraint(
                    equalTo: of.topAnchor,
                    constant: const
                )
            // Other align cases here
            case .setAspectWidth(ratio: let ratio):
                return object.heightAnchor.constraint(
                    equalTo: object.widthAnchor,
                    multiplier: ratio
                )
            }
        }
    )
}

I use "any" keyword for generic UISnapable as it's was recently introduced in Swift for such cases.

But unfortunately, I get error "Type _ErrorCodeProtocol has no member alignTopWithTop"

What I am doing wrong? I dunno how _ErrorCodeProtocol is connected to my generic case.

I've checked source of _ErrorCodeProtocol and it lead to Foundation. But I didn't find its declaration somewhere in foundation. Also I didn't find information on it in Apple docs (https://developer.apple.com/search/?q=_ErrorCodeProtocol&amp;type=Documentation)

答案1

得分: 1

你传递的是UISnapable列表,但你的意思是传递Constraints列表。在这种情况下,你将any放在了错误的位置。

我认为你的意思是这样的:

// Constraints不是泛型,但在某些情况下可以容纳任何UISnapable
enum Constraints {
    // 使用`any UISnapable`而不是T
    case alignTopWithTop(of: any UISnapable, const: CGFloat = 0)
    // 在这里添加其他对齐情况
    case setAspectWidth(ratio: CGFloat)
}

然后方法是(请注意,这里的inout是不必要的;UIView是一个引用类型):

func applyConstraints(object: inout UIView, constraints: Constraints...) {
                                                         ^^^^^^^^^^^

至于错误,编译器之所以困惑,是因为你正尝试switch一个非枚举类型(这里的$0any UISnapable)。这个错误并不特别有帮助,并且与潜在问题无关。

英文:

You're passing a list of UISnapable, but you mean to pass a list of Constraints. In this case you're putting any in the wrong place.

What I believe you mean is this:

// Constraints are not generic, but they can hold any UISnapable in some cases
enum Constraints {
    // Use `any UISnapable` rather than T
    case alignTopWithTop(of: any UISnapable, const: CGFloat = 0)
    // Other align cases here
    case setAspectWidth(ratio: CGFloat)
}

And then the method is (note that the inout here is unnecessary; UIView is a reference type):

func applyConstraints(object: inout UIView, constraints: Constraints...) {
                                                         ^^^^^^^^^^^

As for the error, the compiler is just getting confused because you're trying to switch on a non-enum ($0 here is any UISnapable). The error is not particularly helpful, and is not related to the underlying problem.

huangapple
  • 本文由 发表于 2023年5月30日 02:13:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76359530.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定