探索Objective - C与Swift的交互及相机应用开发
1. 理解Objective - C文件导入与实现
1.1 文件导入方式
在Objective - C中,存在两种导入文件的方式:
- 使用尖括号
<>
导入框架中的头文件,例如导入基础框架的头文件。
- 使用引号
""
导入同一框架或应用内的头文件。
当一个头文件导入另一个头文件时,如果其他文件包含该头文件,实际上会导入这两个头文件。若只需知道类的存在,而不需要了解其具体细节,可以使用
@class
关键字来声明类的存在,示例如下:
@class Car;
@interface SteeringWheel : NSObject
@property (weak) Car *car;
@end
不过,在实现文件中可能仍需导入该类的头文件,以便与类的某些部分进行交互。
1.2 实现文件的作用
实现文件主要用于实现类型的具体功能。它不会被其他文件导入,而是用于履行接口文件所定义的承诺。通常,头文件和实现文件是成对出现的,例如定义一个
SteeringWheel
类时,会创建
SteeringWheel.h
头文件和
SteeringWheel.m
实现文件。其他需要与
SteeringWheel
类细节交互的代码会导入头文件,编译器在编译时会将所有实现提供给运行程序。
实现文件也是隐藏私有代码的好地方,因为它不能被其他代码导入。人们有时会在实现文件中添加类接口,或者添加匿名类别,示例如下:
@interface SteeringWheel ()
@property NSString *somePrivateProperty;
- (void)somePrivateMethod;
@end
这个类别没有名称,因此被认为是匿名的。其实现应在类的正常实现中定义,这样可以在实现文件顶部定义私有属性和方法。只有打算从外部文件使用的方法才应在头文件中声明,头文件应被视为类的公共接口,并且应尽可能简洁。
1.3 文件组织
Objective - C项目中的文件组织方式与Swift文件类似。创建文件夹将相关文件分组是一种好的做法,通常会将头文件和实现文件对放在一起。此外,还可以使用快捷键
Control/Command + 上箭头
或
Control/Command + 下箭头
快速在头文件和实现文件之间切换。
2. 从Swift调用Objective - C代码
2.1 桥接头文件
要从Swift调用Objective - C代码,关键是让代码对Swift可见。当向Swift项目中添加第一个Objective - C代码时,Xcode会询问是否添加桥接头文件,应选择“是”。Xcode会自动创建一个以项目名称结尾并加上
Bridging - Header.h
的头文件,在这个文件中导入要暴露给Swift的Objective - C头文件即可,无需导入实现文件。
2.2 使用函数
将头文件暴露给Swift后,调用函数非常简单,可以直接调用函数,就像函数没有参数名一样,示例如下:
NSArray *addInviteeToListIfSpotAvailable
(
NSArray *invitees,
NSString *newInvitee
);
在Swift中调用:
addInviteeToListIfSpotAvailable(inviteeList, "Sarah")
Xcode甚至会自动补全代码,从Swift文件的角度来看,无法知道该函数是用Objective - C还是Swift实现的。
2.3 使用类型
使用类型的方式与使用函数类似。在桥接头文件中导入适当的头文件后,就可以像使用Swift类型一样使用Objective - C类型,示例如下:
@interface Contact : NSObject
@property NSString *firstName;
@property NSString *lastName;
- (NSArray *)addToInviteeList:(NSArray *)invitees includeLastName:(BOOL)include;
@end
在Swift中使用:
var contact = Contact()
contact.firstName = "First"
contact.lastName = "Last"
contact.addToInviteeList(inviteeList, includeLastName: false)
从Swift的角度来看,使用Objective - C类的代码与使用Swift类的代码没有区别。不过,Objective - C中定义的所有类都继承自
NSObject
,并且Objective - C枚举不能完美地转换为Swift枚举,而是作为单个常量暴露。
2.4 容器类型的转换
在代码中可以看到,
NSString
和
NSArray
类型似乎可以透明地转换为Swift的
String
和
Array
类。这些类型以及字典在Swift和Objective - C之间的转换几乎是完美的,但Objective - C在定义容器时需要指定元素类型,转换到Swift后容器元素类型为
AnyObject
。如果要将其视为更具体的类型,需要进行类型转换,示例如下:
inviteeList = contact.addToInviteeList(
inviteeList,
includeLastName: false
) as! [String]
该方法转换为Swift后的实际返回值是
[AnyObject]!
。如果确定该方法永远不会返回
nil
并且总是返回字符串数组,那么进行强制类型转换是安全的;否则,应该检查是否为
nil
并进行可选类型转换(
as?
)。
2.5 注解
Objective - C类型转换为Swift时存在一种模式,任何引用类型默认会转换为隐式解包可选类型,因为Objective - C引用类型的性质使得编译器无法自动知道返回值是否可能为
nil
。不过,Objective - C开发者可以添加注解来告知编译器值是否可以为
nil
。
2.5.1 空值注解
可以使用
__nonnull
和
__nullable
关键字来注解变量是否可以为
nil
,示例如下:
- (NSArray * __nonnull)addToInviteeList:
(NSArray * __nullable)invitees;
__nonnull
表示不能为
nil
,在Swift中会转换为非可选类型;
__nullable
表示可以为
nil
,在Swift中会转换为常规可选类型。
2.5.2 容器元素类型注解
Objective - C开发者还可以注解容器类型包含的元素类型,使用尖括号,示例如下:
- (NSArray<NSString *> * __nonnull)addStringToInviteeList:
(NSArray<NSString *> * __nullable)invitees;
这样,该方法在Swift中就像Swift方法一样,接受一个可选的字符串数组并返回一个非可选的字符串数组,无需进行类型转换。
以下是从Swift调用Objective - C代码的流程图:
graph TD;
A[添加Objective - C代码到Swift项目] --> B[Xcode询问添加桥接头文件];
B --> C{选择Yes};
C --> D[Xcode创建桥接头文件];
D --> E[在桥接头文件中导入Objective - C头文件];
E --> F[在Swift中调用Objective - C函数和类型];
3. 开发简单相机应用
3.1 应用概念化
在打开Xcode之前,需要对要开发的应用有一个清晰的概念,包括需要表示的数据类型和用户界面的大致样子。对于一个基本的相机应用,第一个版本需要具备以下关键功能:
- 拍照
- 查看之前拍摄的照片库
- 为照片添加标签
- 删除照片
应用的第一个屏幕将是用户已拍摄照片的图库,屏幕上会有一个按钮用于拍摄新照片,还可以激活编辑模式来删除照片或更改标签。数据可以以扁平列表的形式存储在本地文件系统中,每个图像以用户选择的标签命名,但需要考虑处理具有相同标签的不同图像的问题。
3.2 设置应用项目
完成应用概念化后,开始创建iOS应用项目。在Xcode中,导航到
File | New | Project…
,从
iOS | Application
菜单中选择
Single View Application
,然后点击
Next
。为项目命名为
LearningCamera
,组织名称和标识符可以随意填写。确保从语言下拉菜单中选择
Swift
,从设备下拉菜单中选择
Universal
,再次点击
Next
并创建项目。
Xcode会呈现一个项目开发窗口,默认屏幕允许配置应用的各种属性,如版本号、目标设备等。对于本次开发,所有默认设置都可以使用。Xcode还会创建一些文件和文件夹,我们主要在
LearningCamera
文件夹中工作。其中,
AppDelegate.swift
是应用的入口点,包含一个
AppDelegate
类,该类有一些在应用生命周期不同阶段调用的方法;
ViewController.swift
包含一个
UIViewController
子类,用于管理应用默认视图和业务逻辑之间的交互。
以下是创建相机应用项目的步骤列表:
1. 打开Xcode。
2. 选择
File | New | Project…
。
3. 从
iOS | Application
菜单中选择
Single View Application
。
4. 点击
Next
。
5. 为项目命名为
LearningCamera
。
6. 选择
Swift
作为语言,
Universal
作为设备类型。
7. 点击
Next
并创建项目。
综上所述,虽然Swift是目前苹果开发社区中热门的新语言,但Objective - C短期内不会被完全取代,因为所有现有的苹果API都是用Objective - C编写的。理解和与Objective - C交互对于Swift开发者来说仍然非常有价值。通过学习开发简单的相机应用,可以了解iOS框架的一些关键词汇和概念,为进一步的应用开发打下基础。
3.3 配置用户界面
配置用户界面是开发相机应用的重要环节,以下是一些关键步骤和要点:
-
使用故事板(Storyboard)
:Xcode 提供了可视化的故事板来设计用户界面。在
Main.storyboard
文件中,可以添加各种 UI 元素,如按钮、图像视图等。
-
添加相机按钮
:从对象库中拖出一个按钮到故事板的视图上,将其文本设置为“拍照”。
-
添加图像视图
:用于显示拍摄的照片,设置其约束以适应不同的设备屏幕。
-
建立连接
:通过按住
Control
键并拖动 UI 元素到
ViewController.swift
文件中,可以创建
IBOutlet
和
IBAction
连接。例如,将“拍照”按钮与一个
IBAction
方法连接,当按钮被点击时触发拍照操作。
以下是一个简单的
IBAction
方法示例:
@IBAction func takePhotoButtonTapped(_ sender: UIButton) {
// 这里添加拍照的逻辑
}
3.4 运行应用
配置好用户界面后,就可以运行应用进行测试了:
1. 选择运行设备:在 Xcode 的工具栏中,选择要运行应用的设备,如模拟器或连接的真机。
2. 点击运行按钮:点击 Xcode 工具栏中的三角形运行按钮,Xcode 会编译并运行应用。
3. 测试功能:在应用运行后,测试拍照、查看照片库等功能是否正常工作。
3.5 临时保存照片
拍摄照片后,需要临时保存照片以便后续使用。可以使用
UIImagePickerController
来拍摄照片,并使用
UIImageWriteToSavedPhotosAlbum
方法将照片保存到相册,示例代码如下:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let pickedImage = info[.originalImage] as? UIImage {
UIImageWriteToSavedPhotosAlbum(pickedImage, nil, nil, nil)
}
picker.dismiss(animated: true, completion: nil)
}
3.6 填充照片网格
为了让用户可以查看之前拍摄的照片,需要填充一个照片网格。可以使用
UICollectionView
来实现照片网格,以下是实现步骤:
1.
创建
UICollectionView
:在
ViewController.swift
中创建一个
UICollectionView
实例,并设置其数据源和委托。
2.
实现数据源方法
:实现
UICollectionViewDataSource
协议的方法,如
numberOfItemsInSection
和
cellForItemAt
,以提供照片数据。
3.
设置单元格布局
:使用
UICollectionViewFlowLayout
来设置单元格的大小和间距。
以下是一个简单的
cellForItemAt
方法示例:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as! PhotoCollectionViewCell
// 这里设置单元格中的照片
return cell
}
3.7 遵循模型 - 视图 - 控制器(MVC)进行重构
为了使代码结构更加清晰和易于维护,需要遵循模型 - 视图 - 控制器(MVC)模式进行重构:
-
模型(Model)
:负责处理数据,如照片的存储和管理。可以创建一个
PhotoModel
类来表示照片数据。
-
视图(View)
:负责显示界面,如
UICollectionView
和
UIImageView
。
-
控制器(Controller)
:负责协调模型和视图之间的交互,如
ViewController.swift
。
以下是一个简单的
PhotoModel
类示例:
class PhotoModel {
var image: UIImage
var label: String
init(image: UIImage, label: String) {
self.image = image
self.label = label
}
}
3.8 永久保存照片
除了临时保存照片,还需要将照片永久保存到本地文件系统。可以使用
FileManager
来实现,示例代码如下:
func savePhotoPermanently(_ image: UIImage, withLabel label: String) {
let fileManager = FileManager.default
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let photoURL = documentsDirectory.appendingPathComponent("\(label).jpg")
if let imageData = image.jpegData(compressionQuality: 0.8) {
do {
try imageData.write(to: photoURL)
} catch {
print("保存照片失败: \(error)")
}
}
}
以下是相机应用开发流程的表格总结:
| 步骤 | 操作 |
| ---- | ---- |
| 1 | 应用概念化,确定功能和界面 |
| 2 | 设置应用项目,创建
LearningCamera
项目 |
| 3 | 配置用户界面,使用故事板添加 UI 元素 |
| 4 | 运行应用进行测试 |
| 5 | 临时保存照片到相册 |
| 6 | 填充照片网格,使用
UICollectionView
|
| 7 | 遵循 MVC 模式进行重构 |
| 8 | 永久保存照片到本地文件系统 |
以下是相机应用开发的整体流程图:
graph LR;
A[应用概念化] --> B[设置应用项目];
B --> C[配置用户界面];
C --> D[运行应用];
D --> E[临时保存照片];
E --> F[填充照片网格];
F --> G[遵循 MVC 重构];
G --> H[永久保存照片];
通过以上步骤,我们完成了一个简单相机应用的开发。在开发过程中,我们不仅学习了如何从 Swift 调用 Objective - C 代码,还深入了解了 iOS 框架的一些关键概念和操作,为进一步开发更复杂的应用奠定了基础。在实际开发中,还可以根据需求添加更多的功能和优化用户体验。
超级会员免费看
286

被折叠的 条评论
为什么被折叠?



