您的位置:首页 > 移动开发 > Swift

Swift从相册选择图片,图文混排并且可以保存、上传数据

2017-10-16 13:19 645 查看
博主最近突发奇想想做一个自己的日记本App,在过程中遇到了一些坑,摸索了很久才做出一个简单的日记本功能。

先来看看一下效果吧:



先来说说这次用到的一些东西吧:

1、UIImagePickerController:用来获取Photos里面的照片

2、UITexeView:用来做图文混排

3、NSKeyedArchiver和NSKeyedUnarchiver:保存和读取数据

具体的一些细节在过程中讲解。

从照片选择图片

首先,我们要实现从用户的Photos(照片)这个应用中读取用户的照片。最简单的方法就是用UIImagePickerController,要使用它,需要类遵循UIImagePickerControllerDelegate和UINavigationControllerDelegate这两个协议。

在做图片选择功能时,博主将其封装为三个函数(其中两个是协议中的函数)

第一个函数如下:

/// 选择图片
@objc func pickImage() {
self.imagePickerController = UIImagePickerController()
//设置代理
self.imagePickerController.delegate = self
//允许用户对选择的图片或影片进行编辑
self.imagePickerController.allowsEditing = true
//设置image picker的用户界面
self.imagePickerController.sourceType = .photoLibrary
//设置图片选择控制器导航栏的背景颜色
self.imagePickerController.navigationBar.barTintColor = UIColor.orange
//设置图片选择控制器导航栏的标题颜色
self.imagePickerController.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
//设置图片选择控制器导航栏中按钮的文字颜色
self.imagePickerController.navigationBar.tintColor = UIColor.white
//显示图片选择控制器
self.present(self.imagePickerController, animated: true, completion: nil)
}


这个函数中的东西都很简单,需要说的就只有一个那就是设置imagePickerController的sourceType,它的类型是UIImagePickerControllerSourceType的枚举,其中三个变量分别为photoLibrary(照片库)、camera(相机)、savedPhotosAlbum(保存的相册)。设置sourceType就是设置图片的来源,常用的为前两个。

然后就是两个协议中的方法了,一个是你选择了对应的图片

/// 图片选择完成
///
/// - Parameters:
///   - picker: 图片选择控制器
///   - info: 图片信息
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
//        print("media type: \(String(describing: info["UIImagePickerControllerMediaType"]))")
//        print("crop rect: \(String(describing: info["UIImagePickerControllerCropRect"]))")
//        print("reference url: \(String(describing: info["UIImagePickerControllerReferenceURL"]))")
//获取选择到的图片
let image = info["UIImagePickerControllerEditedImage"] as? UIImage
//将图片加入到textView中
addImage(image: image!)
//关闭当前界面
self.dismiss(animated: true, completion: nil)
}


首先用获取到的info属性来获取你选中的相片,info是一个[String:Any]类型的字典,字典中有一些系统定义的key,这儿简单列举一下:

didFinishPickingMediaWithInfo参数的键值对:

UIImagePickerControllerMediaType: 用户选择的媒体类型,如kUTTypeImage or kUTTypeMovie

UIImagePickerControllerOriginalImage: 用户选择的未经剪裁的原始图片素材

UIImagePickerControllerEditedImage: 用户选择的经过编辑之后的图片素材

UIImagePickerControllerCropRect: 用户对原始图片进行剪裁的区域

UIImagePickerControllerMediaURL: 用户选择的影片素材在文件系统中的位置

UIImagePickerControllerReferenceURL: 用户选择的原始图片在文件系统中的位置。即使用户对图片或影片进行了编辑,仍然会返回原始素材的位置

UIImagePickerControllerMediaMetadata: 新拍摄的图片的meta信息。只有当source type为相机时才会返回该信息

UIImagePickerControllerLivePhoto: 用户选择的活拍摄的live photo。仅支持iOS 9.1及之后的版本

我们这里使用的是编辑过后的照片,然后在调用dismiss方法关闭当前的界面。

另一个就是在选择照片的过程中取消选择,这个方法很简单,直接调用dismiss关闭界面就可以了。代码如下:

/// 取消选择图片
///
/// - Parameter picker: 图片选择控制器
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
}


到这里,就实现了一个简单的图片选择器。

UITextView图文混排

其次,就是将获取到的图片在UITextView中显示。

@objc func addImage(image: UIImage) {
//创建文本附件
let attachment = NSTextAttachment()
//设置附件的图片
attachment.image = image
//图片的缩放比例
let scale = (textView.frame.width - 2 * 5 ) / image.size.width
//设置附件的大小
attachment.bounds = CGRect(x: 0, y: 0, width: scale * image.size.width, height: scale * image.size.height)
//获取textView中的富文本
let attribute = NSMutableAttributedString(attributedString: textView.attributedText)
//在富文本中加入图片附件
attribute.append(NSAttributedString(attachment: attachment))
//设置textView中的富文本
textView.attributedText = attribute
}


这里要说一下的就是,你获取到的图片的大小往往比屏幕大很多,所以需要将图片缩放一下,当初这里百度了几十分钟才找到,原来只需要设置一下attachment的bounds就可以了,只能说百度太坑。

NSAttributedString(图文混排)保存与读取

最后就是将textView中的富文本保存和读取了,这个困惑了博主很久,因为第一时间想的是找教程,而不是看Swift的API,所以浪费了很多时间,在保存数据时耽误了很久,读取数据并显示时,耽误了更久。

先看看保存数据的代码吧:

@objc func nextPage() {

var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
url?.appendPathComponent("text.txt")

let attribute = textView.attributedText

let data = NSKeyedArchiver.archivedData(withRootObject: attribute as Any)
do {
try data.write(to: url!)
} catch {
print(error)
}

navigationController?.pushViewController(MyViewController(), animated: true)
}


我这里将其放在了跳转页面的过程中,因为只需要功能,所以没考虑效率、布局和MVC等。最开始博主以为可以将富文本转换成AnyObject来保存数据,可是读取出来,只有其中的文本内容,图片消失不见了,debug时才知道NSTextAttachment(附件)虽然在富文本当中,但它与文字是分开的,在直接转换成AnyObject时是不会保存图片的。然后百度了很久,看了很多大牛的操作,目前只需要做一个简单的图文混排保存,并不想太复杂,所以只能自己去看API,然后,恍然大悟,因为我看到了这个



NSAttributedString遵循了NSSecureCoding协议,这个协议是继承自NSCoding协议的。所以我们就可以直接用NSKeyedArchiver来编码数据并保存了(当时那个心情啊,直接放了几个小时三天三夜,室友都在旁边磨西瓜刀了)

NSKeyedArchiver怎么用?看这篇文章

本以为事情到这里就结束了,然而,编程就是这么有趣,又是几个小时的时间。算了,都是泪,先看代码:

private func getContent() {
var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
url?.appendPathComponent("text.txt")
if let data = try? Data(contentsOf: url!) {
let attribute = NSMutableAttributedString(attributedString: NSKeyedUnarchiver.unarchiveObject(with: data) as! NSAttributedString)
//枚举出富文本中所有的内容
attribute.enumerateAttributes(in: NSRange(location: 0, length: attribute.length), options: [], using: { (data, range, _) in
//找出富文本中的附件
if let attachment = data[.attachment] as? NSTextAttachment {
//取出附件中的图片
let image = (attachment.image)!
//缩放
let scale = (textView.frame.width - 2 * 5 ) / image.size.width
//设置大小
attachment.bounds = CGRect(x: 0, y: 0, width: image.size.width * scale, height: image.size.height * scale)
//替换富文本中的附件
attribute.replaceCharacters(in: range, with: NSAttributedString(attachment: attachment))
}
})
textView.attributedText = attribute
}
}


最开始本以为将数据解码,就能正常显示了,但是,图片,又变成原大小了,想想也是有原因的,在开始时我们设置的只是attachment(附件)的大小,而并没有修改图片的尺寸,所以在保存时,保存的当然就是原图,读取出来之后并没有设置其大小的代码,当然它就会以原图显示。所以,继续百度,其中有注释的内容就是百度了很久才百度到的。

原处是CSDN的小伙伴写的博文

最后附上所有代码:

AppDelegate.swift

window?.rootViewController = UINavigationController(rootViewController: ViewController())


修改App根视图为导航栏控制器

ViewController.swift

import UIKit

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

var textView: UITextView!
var imagePickerController: UIImagePickerController!

override func viewDidLoad() {
super.viewDidLoad()

title = "write"

let next = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(nextPage))
navigationItem.rightBarButtonItem = next

let add = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(pickImage))
navigationItem.leftBarButtonItem = add

textView = UITextView(frame: self.view.frame)
textView.font = UIFont.systemFont(ofSize: 36)
textView.becomeFirstResponder()
self.view.addSubview(textView)
}

@objc func addImage(image: UIImage) {
let attachment = NSTextAttachment()
attachment.image = image
let scale = (textView.frame.width - 2 * 5 ) / image.size.width
attachment.bounds = CGRect(x: 0, y: 0, width: scale * image.size.width, height: scale * image.size.height)

let attribute = NSMutableAttributedString(attributedString: textView.attributedText)
attribute.append(NSAttributedString(attachment: attachment))

textView.attributedText = attribute
}

@objc func nextPage() { var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first url?.appendPathComponent("text.txt") let attribute = textView.attributedText let data = NSKeyedArchiver.archivedData(withRootObject: attribute as Any) do { try data.write(to: url!) } catch { print(error) } navigationController?.pushViewController(MyViewController(), animated: true) }

/// 选择图片 @objc func pickImage() { self.imagePickerController = UIImagePickerController() //设置代理 self.imagePickerController.delegate = self //允许用户对选择的图片或影片进行编辑 self.imagePickerController.allowsEditing = true //设置image picker的用户界面 self.imagePickerController.sourceType = .photoLibrary //设置图片选择控制器导航栏的背景颜色 self.imagePickerController.navigationBar.barTintColor = UIColor.orange //设置图片选择控制器导航栏的标题颜色 self.imagePickerController.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white] //设置图片选择控制器导航栏中按钮的文字颜色 self.imagePickerController.navigationBar.tintColor = UIColor.white //显示图片选择控制器 self.present(self.imagePickerController, animated: true, completion: nil) }

/// 图片选择完成
///
/// - Parameters:
/// - picker: 图片选择控制器
/// - info: 图片信息
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
// print("media type: \(String(describing: info["UIImagePickerControllerMediaType"]))")
// print("crop rect: \(String(describing: info["UIImagePickerControllerCropRect"]))")
// print("reference url: \(String(describing: info["UIImagePickerControllerReferenceURL"]))")

let image = info["UIImagePickerControllerEditedImage"] as? UIImage

addImage(image: image!)

self.dismiss(animated: true, completion: nil)
}

/// 取消选择图片 /// /// - Parameter picker: 图片选择控制器 func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { self.dismiss(animated: true, completion: nil) }

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

}


MyViewController.swift

import UIKit

class MyViewController: UIViewController {

var textView: UITextView!

override func viewDidLoad() {
super.viewDidLoad()

title = "read"

textView = UITextView(frame: self.view.frame)
textView.isEditable = false
self.view.addSubview(textView)

getContent()

}

private func getContent() { var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first url?.appendPathComponent("text.txt") if let data = try? Data(contentsOf: url!) { let attribute = NSMutableAttributedString(attributedString: NSKeyedUnarchiver.unarchiveObject(with: data) as! NSAttributedString) //枚举出富文本中所有的内容 attribute.enumerateAttributes(in: NSRange(location: 0, length: attribute.length), options: [], using: { (data, range, _) in //找出富文本中的附件 if let attachment = data[.attachment] as? NSTextAttachment { //取出附件中的图片 let image = (attachment.image)! //缩放 let scale = (textView.frame.width - 2 * 5 ) / image.size.width //设置大小 attachment.bounds = CGRect(x: 0, y: 0, width: image.size.width * scale, height: image.size.height * scale) //替换富文本中的附件 attribute.replaceCharacters(in: range, with: NSAttributedString(attachment: attachment)) } }) textView.attributedText = attribute } }

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}


老实说,这个效果,只能满足基本需求。博主下一篇博文会介绍更漂亮一点的图文混排;再下一篇会升级图片选择,可以一次选择多张照片,敬请期待!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐