Swift从相册选择图片,图文混排并且可以保存、上传数据
2017-10-16 13:19
645 查看
博主最近突发奇想想做一个自己的日记本App,在过程中遇到了一些坑,摸索了很久才做出一个简单的日记本功能。
先来看看一下效果吧:
先来说说这次用到的一些东西吧:
1、UIImagePickerController:用来获取Photos里面的照片
2、UITexeView:用来做图文混排
3、NSKeyedArchiver和NSKeyedUnarchiver:保存和读取数据
具体的一些细节在过程中讲解。
在做图片选择功能时,博主将其封装为三个函数(其中两个是协议中的函数)
第一个函数如下:
这个函数中的东西都很简单,需要说的就只有一个那就是设置imagePickerController的sourceType,它的类型是UIImagePickerControllerSourceType的枚举,其中三个变量分别为photoLibrary(照片库)、camera(相机)、savedPhotosAlbum(保存的相册)。设置sourceType就是设置图片的来源,常用的为前两个。
然后就是两个协议中的方法了,一个是你选择了对应的图片
首先用获取到的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关闭界面就可以了。代码如下:
到这里,就实现了一个简单的图片选择器。
这里要说一下的就是,你获取到的图片的大小往往比屏幕大很多,所以需要将图片缩放一下,当初这里百度了几十分钟才找到,原来只需要设置一下attachment的bounds就可以了,只能说百度太坑。
先看看保存数据的代码吧:
我这里将其放在了跳转页面的过程中,因为只需要功能,所以没考虑效率、布局和MVC等。最开始博主以为可以将富文本转换成AnyObject来保存数据,可是读取出来,只有其中的文本内容,图片消失不见了,debug时才知道NSTextAttachment(附件)虽然在富文本当中,但它与文字是分开的,在直接转换成AnyObject时是不会保存图片的。然后百度了很久,看了很多大牛的操作,目前只需要做一个简单的图文混排保存,并不想太复杂,所以只能自己去看API,然后,恍然大悟,因为我看到了这个
NSAttributedString遵循了NSSecureCoding协议,这个协议是继承自NSCoding协议的。所以我们就可以直接用NSKeyedArchiver来编码数据并保存了(当时那个心情啊,直接放了几个小时三天三夜,室友都在旁边磨西瓜刀了)
NSKeyedArchiver怎么用?看这篇文章
本以为事情到这里就结束了,然而,编程就是这么有趣,又是几个小时的时间。算了,都是泪,先看代码:
最开始本以为将数据解码,就能正常显示了,但是,图片,又变成原大小了,想想也是有原因的,在开始时我们设置的只是attachment(附件)的大小,而并没有修改图片的尺寸,所以在保存时,保存的当然就是原图,读取出来之后并没有设置其大小的代码,当然它就会以原图显示。所以,继续百度,其中有注释的内容就是百度了很久才百度到的。
原处是CSDN的小伙伴写的博文
修改App根视图为导航栏控制器
ViewController.swift
MyViewController.swift
老实说,这个效果,只能满足基本需求。博主下一篇博文会介绍更漂亮一点的图文混排;再下一篇会升级图片选择,可以一次选择多张照片,敬请期待!
先来看看一下效果吧:
先来说说这次用到的一些东西吧:
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.swiftwindow?.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.
}
}
老实说,这个效果,只能满足基本需求。博主下一篇博文会介绍更漂亮一点的图文混排;再下一篇会升级图片选择,可以一次选择多张照片,敬请期待!
相关文章推荐
- 如何通过表单上传图片数据,PHP后端获取并且保存文件数据
- iOS上传头像, 相册权限,相册权限,拍照上传,相册选择图片,拍照页面语言设置,保存到相册
- 小程序从本地相册选择图片或使用相机拍照wx.chooseImage上传后预览并可以识别图中二维码
- Android中相机拍摄照片,以及相册选择图片压缩上传(压缩后保存进SD中)(可用于修改头像等)
- 长按 WebView 页面图片弹出对话框可以选择保存长按的图片到本地相册
- 个人界面 < 头像 > 图片选择(相册,拍照)--如何调用系统的相册,裁剪并且上传
- 从相册中选择或拍照设置并上传头像图片
- 从UIWebView中调用iOS相册,并选择图片上传到Linux Web服务器
- 上传图片并且生成可以控制大小图片清晰度的方法
- Android仿微信图片上传,可以选择多张图片,缩放预览,拍照上传等
- iOS从相册选择图片并裁剪,可用于上传头像
- 仿QQ发表动态,获取系统相册,选择多张图片上传
- Ionic3学习笔记(十二)拍照上传图片以及从相册选择图片上传
- 从系统相册中选择GIF图片上传到服务器
- 拍照上传图片与从相册中选择图片上传
- 拍照选择-相册图片裁剪后保存在指定的地址
- swift - 简单的图片滤镜+保存view转成图片存入本地相册
- Swift中获取相册图片与保存到相册
- Flash 上传 ByteArray 数据到 php 并保存为图片
- Android仿微信图片上传,可以选择多张图片,缩放预览,拍照上传等