21、探索Objective - C与Swift的交互及相机应用开发

探索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 框架的一些关键概念和操作,为进一步开发更复杂的应用奠定了基础。在实际开发中,还可以根据需求添加更多的功能和优化用户体验。

演示了为无线无人机电池充电设计的感应电力传输(IPT)系统 Dynamic Wireless Charging for (UAV) using Inductive Coupling 模拟了为无人机(UAV)量身定制的无线电力传输(WPT)系统。该模型演示了直流电到高频交流电的转换,通过磁共振在气隙中无线传输能量,以及整流回直流电用于电池充电。 系统拓扑包括: 输入级:使用IGBT/二极管开关连接到全桥逆变器的直流电压源(12V)。 开关控制:脉冲发生器以85 kHz(周期:1/85000秒)的开关频率运行,这是SAE J2954无线充电标准的标准频率。 耦合级:使用互感和线性变压器块来模拟具有特定耦合系数的发射(Tx)和接收(Rx)线圈。 补偿:包括串联RLC分支,用于模拟谐振补偿网络(将线圈调谐到谐振频率)。 输出级:桥式整流器(基于二极管),用于将高频交流电转换回直流电,以供负载使用。 仪器:使用示波器块进行全面的电压和电流测量,用于分析输入/输出波形和效率。 模拟详细信息: 求解器:离散Tustin/向后Euler(通过powergui)。 采样时间:50e-6秒。 4.主要特点 高频逆变:模拟85 kHz下IGBT的开关瞬态。 磁耦合:模拟无人机着陆垫和机载接收器之间的松耦合行为。 Power GUI集成:用于专用电力系统离散仿真的设置。 波形分析:预配置的范围,用于查看逆变器输出电压、初级/次级电流和整流直流电压。 5.安装使用 确保您已安装MATLAB和Simulink。 所需工具箱:必须安装Simscape Electrical(以前称为SimPowerSystems)工具箱才能运行sps_lib块。 打开文件并运行模拟。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值