【问题标题】:Swift set content compression resistanceSwift 设置内容压缩阻力
【发布时间】:2015-01-28 09:41:36
【问题描述】:

再次使用 Swift,我错过了什么吗?

我有这行:

self.itemDescription?.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: UILayoutConstraintAxis.Vertical);

但是 Xcode 给了我一个错误:

Undefined symbols for architecture i386: "_UILayoutPriorityRequired"

Swift 的另一个怪癖?

【问题讨论】:

  • 我可以重现这个,我同意它看起来不对。我现在可能只是提出一个错误报告并使用 1000;大概有些东西没有链接到定义符号所在的任何模块(或者它们实际上没有在任何地方定义......)
  • 已投票结束。 Apple 尚未解决此问题 = /
  • 别这样。这个问题用谷歌搜索得更好。在 OSX swift 应用程序中,NSLayoutPriorityRequired 和其他布局常量也存在同样的问题。

标签: ios swift autolayout


【解决方案1】:

解决方案:将UILayoutPriorityRequired替换为1000

self.itemDescription?.setContentCompressionResistancePriority(1000, forAxis: UILayoutConstraintAxis.Vertical);

这不是错误。这是对如何将 Objective-C 库导入 Swift 的误解。应该理解 Swift 是如何将代码(甚至来自 Apple UIKIt 库)从 Objective-C 导入到 Swift 中的。

UILayoutPriority 是一个浮点数。在 Objective-C 中,已经为我们预定义了几个值。预定义的值似乎是一个枚举。我们可能期望在 Swift 中也可以使用相同的枚举。

文档似乎暗示了一个枚举:

SWIFT(文档)

typealias UILayoutPriority = Float

OBJECTIVE-C(文档)

enum {
   UILayoutPriorityRequired = 1000,
   UILayoutPriorityDefaultHigh = 750,
   UILayoutPriorityDefaultLow = 250,
   UILayoutPriorityFittingSizeLevel = 50,
};
typedef float UILayoutPriority;

但在 Xcode 中,如果您要求查看这些枚举值之一的定义(例如 UILayoutPriorityRequired),您会看到它们实际上在头文件中定义为常量浮点数。

OBJECTIVE-C(头文件)

typedef float UILayoutPriority;
static const UILayoutPriority UILayoutPriorityRequired NS_AVAILABLE_IOS(6_0) = 1000; // A required constraint.  Do not exceed this.
static const UILayoutPriority UILayoutPriorityDefaultHigh NS_AVAILABLE_IOS(6_0) = 750; // This is the priority level with which a button resists compressing its content.
static const UILayoutPriority UILayoutPriorityDefaultLow NS_AVAILABLE_IOS(6_0) = 250; // This is the priority level at which a button hugs its contents horizontally.

因此,尽管我们可能希望将预定义的布局优先级视为枚举值(如文档所示),但布局优先级并未真正定义为枚举;它们被定义为常量浮点数。

对于任何了解 C 编程语言的人来说,一个不匹配的提示是 C 枚举可能只包含 int 值。以下是合法的,将编译:

enum myEnum {
    JGCEnum_one = 1, // this is O.K.
    JGCEnum_two,
    JGCEnum_three
} JGCEnum;

但我们不能将浮点数定义为 C 枚举的值。以下将无法编译:

enum myEnum {
    JGCEnum_one = 1.5, // compile error
    JGCEnum_two,
    JGCEnum_three
} JGCEnum;

Objective-C 枚举与 C 枚举相同(Swift 枚举不同)。重要的是要知道我们是在处理实际的整数还是浮点数。因为整数可以使用 NS_ENUM 宏定义,然后可以作为 Swift 枚举导入 Swift。

iBook 说

Swift 将任何标有 NS_ENUM 宏的 C 样式枚举作为 Swift 枚举导入。这意味着枚举值名称的前缀在导入 Swift 时会被截断,无论它们是在系统框架中定义还是在自定义代码中定义。

摘自:Apple Inc. “Using Swift with Cocoa and Objective-C.” iBooks

这意味着如果 UILayoutPriority 被定义为使用 NS_ENUM 宏的整数,它会作为 Swift 枚举导入到 Swift 中。 UILayoutConstraintAxis 就是这种情况。

SWIFT(文档)

enum UILayoutConstraintAxis : Int {
    case Horizontal
    case Vertical
}

OBJECTIVE-C(文档)

enum {
   UILayoutConstraintAxisHorizontal = 0,
   UILayoutConstraintAxisVertical = 1
};
typedef NSInteger UILayoutConstraintAxis;

查看 Objective-C 头文件可以确认文档所说的内容。

OBJECTIVE-C(头文件)

//
// UIView Constraint-based Layout Support
//

typedef NS_ENUM(NSInteger, UILayoutConstraintAxis) {
    UILayoutConstraintAxisHorizontal = 0,
    UILayoutConstraintAxisVertical = 1
};

因此,至少有两种方法可以知道您习惯于在 Objective-C 中使用的预定义值是否在 Swift 中可用:

  1. 查看文档
  2. 检查 Objective-C 中的头文件(通过右键单击该值然后选择“跳转到定义”找到)

还有另一种方法可以查看您习惯使用的 typedef 是常量还是枚举。在代码中,测试看看常量的地址是否存在。常量有一个内存地址,而枚举没有。请参阅下面的代码。

// this line will compile and run just fine. 
// UILayoutPriorityDefaultHigh is a constant and has a memory address
// the value will be true if the device is running iOS 6.0 or later
// and false otherwise
BOOL predefinedValueIsAvailable = (NULL != &UILayoutPriorityDefaultHigh);

// this line will not compile
// UILayoutConstraintAxisHorizontal is an enum (NOT a constant) 
// and does not have a memory address
predefinedValueIsAvailable = (NULL != &UILayoutConstraintAxisHorizontal);

参考文献

【讨论】:

  • 虽然这是一个很好的答案,描述了导致问题的原因,但它实际上并没有说明解决方案。
  • 这解释了为什么UILayoutPriority.Required 不起作用,因为 UILayoutPriority 不是 Swift 枚举。但是,OP 使用的是UILayoutPriorityRequired,它在 Swift“标题”中定义为let UILayoutPriorityRequired: UILayoutPriority。我认为尝试使用在 Swift 标头中定义的常量会导致链接器错误一个错误,而不仅仅是一个误解。
  • 嗨@mpalmer,没有像Swift“标题”这样的东西。当一个 Objective-C 框架被导入到 Swift 中时,比如 UIKit,这个导入过程使得框架可以作为一个 Swift API 使用。 Swift API与 Objective-C API 相同。因此,“试图使用 Swift 标头中定义的常量”这一说法指的是不存在的东西,因此产生了误解。
  • @JasonCross 绝对正确。但是,这仍然是一个错误。在遇到同样的问题后,我向 Apple 提交了一份错误报告,并在 iOS 9 SDK 中修复了该问题。现在使用UILayoutPriorityRequired 成功链接,正如预期的那样。
【解决方案2】:

要记住的一点是,尽管 UILayoutPriorityFloat 的别名,但您使用的是 Swift,并且可以使用扩展来为这些值创建语义常量:

extension UILayoutPriority {
    static var Low: Float { return 250.0 }
    static var High: Float { return 750.0 }
    static var Required: Float { return 1000.0 }
}

那么你的代码可以读作:

self.itemDescription?.setContentCompressionResistancePriority(UILayoutPriority.Required, forAxis: .Vertical);

我自己在我的一个项目中完成了这项工作,并认为它可能有用。

【讨论】:

  • 你可以删除;
猜你喜欢
  • 2014-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-05
  • 1970-01-01
  • 2015-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多