可以在Obj-C中使用Swift的枚举吗?

177

我正在尝试将我的一些Obj-C类转换为Swift。而一些其他的Obj-C类仍在使用转换后类中的枚举类型。我搜索过预发布文档,但没有找到相关信息,或者可能是我错过了。是否有办法在Obj-C类中使用Swift枚举类型?或者有关这个问题的文档链接?

这是我在旧的Obj-C代码和新的Swift代码中声明枚举类型的方式:

我的旧Obj-C代码:

typedef NS_ENUM(NSInteger, SomeEnum)
{
    SomeEnumA,
    SomeEnumB,
    SomeEnumC
};

@interface SomeClass : NSObject

...

@end

我的新 Swift 代码:

enum SomeEnum: NSInteger
{
    case A
    case B
    case C
};

class SomeClass: NSObject
{
    ...
}

更新:从回答中得知,在 Swift 1.2 之前的旧版本中无法完成此操作。但是根据这篇官方Swift博客。在与XCode 6.3一起发布的Swift 1.2中,您可以通过在enum前面添加@objc来在Objective-C中使用Swift Enum。


没有真正需要更改现有的代码。关于Swift和Objective-C之间的交互,请观看WWDC视频。 - gnasher729
我只是想检查一下,如果将来我的项目中有一个 Swift 类,它是否仍然能够正常工作,但我无法确定应该添加哪个类来进行测试。因此,我转换了旧的类。无论如何,感谢您的帮助。 - myLifeasdog
9个回答

279
从Swift 1.2版本(Xcode 6.3)开始,你可以这样做。只需在枚举声明前加上@objc即可。
@objc enum Bear: Int {
    case Black, Grizzly, Polar
}

无耻地从Swift Blog中摘取

注意:这不适用于字符串枚举或带有关联值的枚举。您的枚举将需要绑定到Int类型


在Objective-C中,它看起来像这样:
Bear type = BearBlack;
switch (type) {
    case BearBlack:
    case BearGrizzly:
    case BearPolar:
       [self runLikeHell];
}

11
非常感谢您指出这一点……需要注意的是,在 Objective-C 中,枚举值将被称为 BearBlackBearGrizzlyBearPolar - nburk
1
这有道理,不是吗?特别是当你看看它是如何从 obj-c 转换为 swift 的时候。@nburk - Daniel Galasko
2
是的,这个可以用。但至少在我的情况下,必须为枚举添加一个“public”属性,以便在项目的Objective-C端访问,像这样:“@objc public enum Bear: Int”。 - Pirkka Esko
2
@AJit 为什么你想要这样做呢?只需将枚举添加到自己的头文件中,然后在桥接头文件中导入即可,否则它就只能在 Swift 中使用。 - Daniel Galasko
1
这个解决方案在Swift中不适用于字符串类型的枚举。@objc只适用于整数类型的枚举。 - Yash Bedi
显示剩余6条评论

33

为了对所选答案进行扩展...

可以使用NS_ENUM()在Swift和Objective-C之间共享Swift风格的枚举。

只需要在Objective-C上下文中使用NS_ENUM()定义它们,就可以使用Swift点符号进行访问。

来自“ 使用Swift与Cocoa和Objective-C”

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

Objective-C

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
   UITableViewCellStyleDefault,
   UITableViewCellStyleValue1,
   UITableViewCellStyleValue2,
   UITableViewCellStyleSubtitle
};

迅捷

let cellStyle: UITableViewCellStyle = .Default

我在UITableViewCellStyle处得到了“此处不允许函数定义”的错误,我做错了什么?当然,我的命名不同于UITableViewCellStyle。 - Cristi Băluță
1
正如Galasko先生在下面的回答中所指出的那样,Swift 1.2允许在Swift中定义枚举并在Obj-c中使用。这种定义方式,即NS_ENUM,在Obj-c中仍然有效,但是从Swift版本1.2开始,您可以选择任一选项。 - SirNod
我发现在Swift中ObjC枚举存在问题:它们不可失败。在类似于if let a = MyEnum(rawValue: 12345)的代码片段中,如果12345不是该枚举的一部分,则结果不是可选项,而是一些无效的枚举。 - bio
苹果文档:将相关的Objective-C常量分组 - bshirley

29
根据《使用 Swift 与 Cocoa 和 Objective-C》指南:

一个 Swift 类或协议必须标记 @objc 属性才能在 Objective-C 中访问和使用。[...]

只要与 Objective-C 兼容,您就可以访问标记了 @objc 属性的类或协议中的任何内容。这不包括仅适用于 Swift 的特性,例如以下列出的特性:

泛型元组 / 在 Swift 中定义的枚举 / 在 Swift 中定义的结构体 / 在 Swift 中定义的顶级函数 / 在 Swift 中定义的全局变量 / 在 Swift 中定义的类型别名 / Swift 风格的可变参数 / 嵌套类型 / 柯里化函数

所以,不能在 Objective-C 类中使用 Swift 枚举。

2
有什么解决办法吗?我的意思是,如果我创建了一个Swift类,并且我绝对需要一个枚举。那么我该如何使该枚举在Objective-C中也可用? - Raul Lopez
来源:https://developer.apple.com/library/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html - fabb
5
如果你知道你将要与Objective-C进行互操作,那么你应该在Objective-C中声明枚举并让两种语言共享它。 - Gregory Higley
4
“嗯,所以我们制作了这座桥梁来帮助你过渡到Swift,但是如果你想使用任何酷炫的东西,比如枚举,结构体,泛型...那么它就没用了。就是这样。” - Kevin R
25
自从Xcode 6.3 / Swift 1.2以来,Swift枚举可以使用@objc在Objective-C中使用,如下面 @DanielGalasko 在他的答案中指出的那样,因此此答案已不再有效! - nburk
10
为了澄清上述评论,引用当前Swift 2.1文档上的文字(https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html):“在Swift中定义的枚举 不带有Int原始值类型”。因此,如果您在Swift中声明的enum具有Int原始值类型,如@obj enum MyEnum: Int,则像之前提到的那样,它将在Objective-C文件中正常工作。如果您的enum声明了其他类型的原始值类型,例如@obj enum MyOtherEnum: String,则无法在Objective-C文件中使用它。 - jjramos

16

Swift 4.1,Xcode 9.4.1:

1)Swift枚举必须以@objc为前缀,并且是Int类型:

// in .swift file:
@objc enum CalendarPermission: Int {
    case authorized
    case denied
    case restricted
    case undetermined
}

2)Objective-C 的命名方式是将枚举名和选项名结合起来,例如 CalendarPermissionAuthorized

// in .m file:
// point to something that returns the enum type (`CalendarPermission` here)
CalendarPermission calPermission = ...;

// use the enum values with their adjusted names
switch (calPermission) {
    case CalendarPermissionAuthorized:
    {
        // code here
        break;
    }
    case CalendarPermissionDenied:
    case CalendarPermissionRestricted:
    {
        // code here
        break;
    }
    case CalendarPermissionUndetermined:
    {
        // code here
        break;
    }
}

当然,记得将你的Swift桥接头作为Objective-C文件导入列表中的最后一项:

#import "MyAppViewController.h"
#import "MyApp-Swift.h"

为什么MyApp-Swift应该是最后一个? - Paul T.
@PaulT.:可能与处理顺序有关。尝试将其放在其他位置,您会发现它不起作用。 - leanne
我检查了一下,我的当前项目中几乎所有的文件都在导入部分的末尾,但是在几个文件中它不在末尾,但项目仍然可以正常工作。也许在新的Xcode中它可以工作?我现在无法检查,因为我的当前项目需要很长时间才能编译:),但我稍后会检查它。 - Paul T.

2

如果你希望保留 ObjC 代码不变,你可以在你的项目中添加一个帮助头文件:

Swift2Objc_Helper.h

在头文件中添加如下枚举类型:
typedef NS_ENUM(NSInteger, SomeEnum4ObjC)
{
   SomeEnumA,
   SomeEnumB
};

你的.m文件中可能还有另一个地方需要进行更改:包含隐藏的头文件:

#import "[YourProjectName]-Swift.h"

将 [YourProjectName] 替换为你的项目名称。这个头文件将所有 Swift 中定义的 @objc 类和枚举暴露给 ObjC。

你可能会收到一个关于枚举类型隐式转换的警告信息... 这是正常的。

顺便说一下,你可以使用这个头文件助手来保留一些 ObjC 代码,比如 #define 常量。


1

如果你(像我一样)真的想要利用字符串枚举,你可以为Objective-C制作一个专门的接口。例如:

enum Icon: String {
    case HelpIcon
    case StarIcon
    ...
}

// Make use of string enum when available:
public func addIcon(icon: Icon) {
    ...
}

// Fall back on strings when string enum not available (objective-c):
public func addIcon(iconName:String) {
    addIcon(Icon(rawValue: iconName))
}

当然,这样做不会给你带来自动补全的便利(除非你在Objective-C环境中定义了额外的常量)。


-1
在研究此问题时,我发现只有部分答案,因此我创建了一个完整的示例,展示了一个使用 Objective-C 桥接的 Swift 应用程序,其中包含由 Objective-C 代码使用的 Swift 枚举和由 Swift 代码使用的 Objective-C 枚举。这是一个简单的 Xcode 项目,您可以运行并进行实验。它是使用 Xcode 10.3 和 Swift 5.0 编写的。 示例项目

我没有看到你的项目在Objective C中使用了Swift枚举。此外,Swift枚举的定义enum SwAnimal缺少前导的@obj - oliolioli

-1

如果您正在尝试观察类似于以下内容的枚举:

enum EnumName: String {
    case one = "One"
    case two = "Two"
}

这个解决方法对我很有帮助。

可观察类:

  • 创建 @objc dynamic var observable: String?
  • 像这样创建您的枚举实例:

    private var _enumName: EnumName? {
        didSet {
            observable = _enumName!.rawValue
        }
    }
    

观察者类:

  • 创建 private var _enumName: EnumName?
  • 创建 private let _instance = ObservableClass()
  • 创建

    private var _enumObserver: NSKeyValueObservation = _instance.observe(\.observable, options: .new, changeHandler: { [weak self] (_, value) in
        guard let newValue = value.newValue else { return }
        self?._enumName = EnumName(rawValue: period)!
    })
    

就是这样。现在每次您更改可观察类中的_enumName时,观察者类中的相应实例也将立即更新。

当然,这只是一个过度简化的实现,但它应该让您了解如何观察不兼容KVO的属性。


-2
这可能会有所帮助
问题陈述:我在Swift类中有一个枚举,我正在从其他Swift类访问它,现在我需要从我的一个Objective-C类中访问它。
在从Objective-C类访问之前:
enum NTCType   {
    case RETRYNOW
    case RETRYAFTER
}
 var viewType: NTCType? 

从 Objective-C 类中访问它的更改

@objc  enum NTCType :Int  {
    case RETRYNOW
    case RETRYAFTER
}

并添加一个函数来传递它的值

  @objc  func setNtc(view:NTCType)  {
        self.viewType = view; // assign value to the variable
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接