开发应用:照片管理应用的实现与优化
1. 填充照片网格
在维护了照片列表后,需要将其显示在集合视图中。集合视图通过实现
UICollectionViewDataSource
协议的数据源来填充数据。常见的做法是让视图控制器成为数据源,具体操作步骤如下:
1. 打开
Main.storyboard
,从集合视图按住
Control
键拖动到视图控制器。
2. 松开鼠标后,从菜单中选择
dataSource
。
接下来需要实现数据源协议的两个方法:
-
collectionView:numberOfItemsInSection:
:用于指定要显示的单元格数量。
extension ViewController: UICollectionViewDataSource {
func collectionView(
collectionView: UICollectionView,
numberOfItemsInSection section: Int
) -> Int
{
return self.photos.count
}
}
-
collectionView:cellForItemAtIndexPath::用于为列表中的特定索引自定义每个单元格。
在配置单元格之前,需要创建自定义的单元格子类
PhotoCollectionViewCell
:
import UIKit
class PhotoCollectionViewCell: UICollectionViewCell {
@IBOutlet var imageView: UIImageView!
@IBOutlet var label: UILabel!
}
然后回到
storyboard
进行以下操作:
1. 在左侧的视图层次结构中找到单元格并点击它。
2. 选择
View | Utilities | Show Identify Inspector
,在类字段中输入
PhotoCollectionViewCell
以设置单元格的类。
3. 导航到
View | Utilities | Show Connections Inspector
,将
imageView
和
label
分别连接到单元格中的图像视图和标签。
4. 返回属性检查器,在标识符文本字段中输入
DefaultCell
以设置单元格的重用标识符。
还需要在视图控制器中添加对集合视图的引用:
class ViewController: UIViewController {
@IBOutlet var collectionView: UICollectionView!
// ...
}
最后实现
collectionView:cellForItemAtIndexPath:
方法:
extension ViewController: UICollectionViewDataSource {
// ...
func collectionView(
collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath
) -> UICollectionViewCell
{
let cell = collectionView
.dequeueReusableCellWithReuseIdentifier(
"DefaultCell",
forIndexPath: indexPath
) as! PhotoCollectionViewCell
let photo = self.photos[indexPath.item]
cell.imageView.image = photo.image
cell.label.text = photo.label
return cell
}
}
当添加照片时,集合视图不会自动重新加载数据,需要手动调用
reloadData
或
insertItemsAtIndexPaths
方法。推荐使用
insertItemsAtIndexPaths
方法以实现动画效果:
let saveAction = UIAlertAction(
title: "Save",
style: .Default
) { action in
let label = textField.text ?? ""
let photo = Photo(image: image, label: label)
self.photos.append(photo)
let indexPath = NSIndexPath(
forItem: self.photos.count - 1,
inSection: 0
)
self.collectionView.insertItemsAtIndexPaths([indexPath])
}
2. 遵循 MVC 模式进行重构
之前的代码将大量业务逻辑放在了视图控制器中,不利于模型、视图和控制器层的分离。因此,创建一个
PhotoStore
类来负责存储照片并实现数据源协议。
首先,将照片属性移动到
PhotoStore
类:
import UIKit
class PhotoStore: NSObject {
var photos = [Photo]()
}
然后添加回调属性:
class PhotoStore: NSObject {
var photos = [Photo]()
var cellForPhoto:
(Photo, NSIndexPath) -> UICollectionViewCell
init(
cellForPhoto: (Photo,NSIndexPath) -> UICollectionViewCell
)
{
self.cellForPhoto = cellForPhoto
super.init()
}
}
接着实现数据源方法:
extension PhotoStore: UICollectionViewDataSource {
func collectionView(
collectionView: UICollectionView,
numberOfItemsInSection section: Int
) -> Int
{
return self.photos.count
}
func collectionView(
collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath
) -> UICollectionViewCell
{
let photo = self.photos[indexPath.item]
return self.cellForPhoto(photo, indexPath)
}
}
再添加保存照片的方法:
func saveNewPhotoWithImage(
image: UIImage,
labeled label: String
) -> NSIndexPath
{
let photo = Photo(image: image, label: label)
self.photos.append(photo)
return NSIndexPath(
forItem: self.photos.count - 1,
inSection: 0
)
}
最后更新视图控制器以使用
PhotoStore
:
class ViewController: UIViewController {
var photoStore: PhotoStore!
func createCellForPhoto(
photo: Photo,
indexPath: NSIndexPath
) -> UICollectionViewCell
{
let cell = self.collectionView
.dequeueReusableCellWithReuseIdentifier(
"DefaultCell",
forIndexPath: indexPath
) as! PhotoCollectionViewCell
cell.imageView.image = photo.image
cell.label.text = photo.label
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
self.photoStore = PhotoStore(
cellForPhoto: self.createCellForPhoto
)
self.collectionView.dataSource = self.photoStore
}
func createSaveActionWithTextField(
textField: UITextField,
andImage image: UIImage
) -> UIAlertAction
{
return UIAlertAction(
title: "Save",
style: .Default
) { action in
let label = textField.text ?? ""
let indexPath = self.photoStore.saveNewPhotoWithImage(
image,
labeled: label
)
self.collectionView.insertItemsAtIndexPaths([indexPath])
}
}
}
3. 永久保存照片
目前应用在退出后照片会丢失,需要将照片永久保存。选择将照片保存到文件系统,每个应用都有一个文档目录,可将照片以用户提供的标签命名存储在其中,并使用时间戳创建子目录以避免标签重复。
首先,在
PhotoStore
类中添加获取保存目录的方法:
private extension PhotoStore {
func getSaveDirectory() throws -> NSURL {
let fileManager = NSFileManager.defaultManager()
return try fileManager.URLForDirectory(
.DocumentDirectory,
inDomain: .UserDomainMask,
appropriateForURL: nil,
create: true
)
}
}
然后在
Photo
结构体中添加保存照片到目录的方法:
struct Photo {
// ...
enum Error: String, ErrorType {
case CouldntGetImageData = "Couldn't get data from image"
case CouldntWriteImageData = "Couldn't write image data"
}
func saveToDirectory(directory: NSURL) throws {
let timeStamp = "\(NSDate().timeIntervalSince1970)"
let fullDirectory = directory
.URLByAppendingPathComponent(timeStamp)
try NSFileManager.defaultManager().createDirectoryAtURL(
fullDirectory,
withIntermediateDirectories: true,
attributes: nil
)
let fileName = "\(self.label).jpg"
let filePath = fullDirectory
.URLByAppendingPathComponent(fileName)
if let data = UIImageJPEGRepresentation(self.image, 1) {
if !data.writeToURL(filePath, atomically: true) {
throw Error.CouldntWriteImageData
}
}
else {
throw Error.CouldntGetImageData
}
}
}
更新
saveNewPhotoWithImage:labeled:
方法以使用
saveToDirectory:
方法:
func saveNewPhotoWithImage(
image: UIImage,
labeled label: String
) throws -> NSIndexPath
{
let photo = Photo(image: image, label: label)
try photo.saveToDirectory(self.getSaveDirectory())
self.photos.append(photo)
return NSIndexPath(
forItem: self.photos.count - 1,
inSection: 0
)
}
在视图控制器中添加错误处理方法:
func displayErrorWithTitle(title: String?, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: .Alert
)
alert.addAction(UIAlertAction(
title: "OK",
style: .Default,
handler: nil
))
self.presentViewController(
alert,
animated: true,
completion: nil
)
}
func displayError(error: ErrorType, withTitle: String) {
switch error {
case let error as NSError:
self.displayErrorWithTitle(
title,
message: error.localizedDescription
)
case let error as Photo.Error:
self.displayErrorWithTitle(
title,
message: error.rawValue
)
default:
self.displayErrorWithTitle(
title,
message: "Unknown Error"
)
}
}
func createSaveActionWithTextField(
textField: UITextField,
andImage image: UIImage
) -> UIAlertAction
{
return UIAlertAction(
title: "Save",
style: .Default
) { action in
do {
let indexPath = try self.photoStore
.saveNewPhotoWithImage(
image,
labeled: textField.text ?? ""
)
self.collectionView.insertItemsAtIndexPaths(
[indexPath]
)
}
catch let error {
self.displayError(
error,
withTitle: "Error Saving Photo"
)
}
}
}
4. 从磁盘加载照片
为了在应用启动时显示保存的照片,需要从磁盘加载照片。在
Photo
结构体中添加从文件路径初始化的方法:
init(image: UIImage, label: String) {
self.image = image
self.label = label
}
init?(filePath: NSURL) {
if let image = UIImage(
contentsOfFile: filePath.relativePath!
)
{
let label = filePath.URLByDeletingPathExtension?
.lastPathComponent ?? ""
self.init(image: image, label: label)
}
else {
return nil
}
}
在
PhotoStore
类中添加加载照片的方法:
func loadPhotos() throws {
self.photos.removeAll(keepCapacity: true)
let fileManager = NSFileManager.defaultManager()
let saveDirectory = try self.getSaveDirectory()
let enumerator = fileManager.enumeratorAtPath(
saveDirectory.relativePath!
)
while let file = enumerator?.nextObject() as? String {
let fileType = enumerator!.fileAttributes![NSFileType]
as! String
if fileType == NSFileTypeRegular {
let fullPath = saveDirectory
.URLByAppendingPathComponent(file)
if let photo = Photo(filePath: fullPath) {
self.photos.append(photo)
}
}
}
}
最后在视图控制器的
viewWillAppear:
方法中调用加载照片的方法:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
do {
try self.photoStore.loadPhotos()
self.collectionView.reloadData()
}
catch let error {
self.displayError(
error,
withTitle: "Error Loading Photos"
)
}
}
总结
通过以上步骤,实现了照片管理应用的核心功能,包括填充照片网格、遵循 MVC 模式重构代码以及永久保存和加载照片。这些操作不仅提高了代码的可维护性和可扩展性,还增强了应用的用户体验。
下面是整个流程的 mermaid 流程图:
graph TD
A[开始] --> B[填充照片网格]
B --> C[遵循 MVC 模式重构]
C --> D[永久保存照片]
D --> E[从磁盘加载照片]
E --> F[结束]
同时,下面的表格总结了各个步骤的关键方法和类:
| 步骤 | 关键方法/类 | 作用 |
| ---- | ---- | ---- |
| 填充照片网格 |
collectionView:numberOfItemsInSection:
、
collectionView:cellForItemAtIndexPath:
、
PhotoCollectionViewCell
| 显示照片列表 |
| 遵循 MVC 模式重构 |
PhotoStore
、
createCellForPhoto
| 分离业务逻辑 |
| 永久保存照片 |
getSaveDirectory
、
saveToDirectory
、
saveNewPhotoWithImage:labeled:
| 保存照片到文件系统 |
| 从磁盘加载照片 |
Photo(filePath:)
、
loadPhotos
| 从文件系统加载照片 |
开发应用:照片管理应用的实现与优化
5. 代码优化与注意事项
在实现照片管理应用的过程中,有一些代码优化和注意事项需要关注。
5.1 内存管理
集合视图为了处理大量单元格,会自动重用已滚动出屏幕的单元格以节省内存。在
collectionView:cellForItemAtIndexPath:
方法中,每次调用时都要重置单元格的配置,避免旧配置残留。例如:
extension ViewController: UICollectionViewDataSource {
func collectionView(
collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath
) -> UICollectionViewCell
{
let cell = collectionView
.dequeueReusableCellWithReuseIdentifier(
"DefaultCell",
forIndexPath: indexPath
) as! PhotoCollectionViewCell
// 重置单元格配置
cell.imageView.image = nil
cell.label.text = nil
let photo = self.photos[indexPath.item]
cell.imageView.image = photo.image
cell.label.text = photo.label
return cell
}
}
5.2 错误处理
在保存和加载照片的过程中,可能会出现各种错误,如无法获取图像数据、无法写入图像数据等。在代码中已经实现了错误处理,但可以进一步优化错误信息的显示,例如添加更多的错误类型和详细的错误描述。
5.3 性能优化
在调用
insertItemsAtIndexPaths
方法时,要确保在
collectionView:numberOfItemsInSection:
返回插入后的更新数量之后调用,否则可能会出现显示异常。另外,频繁调用
reloadData
会导致性能下降,优先使用
insertItemsAtIndexPaths
等更细粒度的方法。
6. 未来功能扩展
基于现有的照片管理应用,还可以扩展以下功能:
6.1 照片编辑
可以添加照片编辑功能,如裁剪、旋转、添加滤镜等。可以使用
UIImagePickerController
的编辑功能,或者集成第三方库来实现更复杂的编辑效果。
6.2 照片分类
允许用户对照片进行分类,例如按日期、地点、主题等分类。可以在
Photo
结构体中添加分类属性,并在集合视图中实现分类显示。
6.3 分享功能
添加分享照片的功能,让用户可以将照片分享到社交媒体、短信、邮件等。可以使用
UIActivityViewController
来实现分享功能。
7. 总结与回顾
通过一系列的操作,我们完成了照片管理应用的开发,包括填充照片网格、遵循 MVC 模式重构代码、永久保存和加载照片等核心功能。在开发过程中,我们学习了如何使用集合视图、如何处理数据源协议、如何进行文件操作以及如何处理错误等重要知识。
以下是开发过程中的关键步骤总结表格:
| 阶段 | 操作内容 | 关键代码 |
| ---- | ---- | ---- |
| 填充照片网格 | 配置数据源、自定义单元格、插入新单元格 |
collectionView:numberOfItemsInSection:
、
collectionView:cellForItemAtIndexPath:
、
insertItemsAtIndexPaths
|
| MVC 重构 | 创建
PhotoStore
类、分离业务逻辑 |
PhotoStore
、
cellForPhoto
回调 |
| 永久保存 | 获取保存目录、保存照片到文件系统 |
getSaveDirectory
、
saveToDirectory
|
| 加载照片 | 从文件系统加载照片 |
Photo(filePath:)
、
loadPhotos
|
下面是未来功能扩展的 mermaid 流程图:
graph TD
A[现有应用] --> B[照片编辑]
A --> C[照片分类]
A --> D[分享功能]
B --> E[裁剪]
B --> F[旋转]
B --> G[添加滤镜]
C --> H[按日期分类]
C --> I[按地点分类]
C --> J[按主题分类]
D --> K[分享到社交媒体]
D --> L[分享到短信]
D --> M[分享到邮件]
通过持续的优化和功能扩展,我们可以让照片管理应用更加完善,为用户提供更好的体验。
超级会员免费看

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



