干货|swift,富文本编辑器

这一篇我们将实现一个富文本编辑器,拥有功能:

  • 1、斜体、下划线混排。
  • 2、图文混排
  • 3、字体大小。
  • 4、选择自定义字体。
  • 5、制作长图片分享到微信朋友圈与微信好友。
  • 6、调用系统邮件发送文本。

先来看一下大体的效果吧,还有一些效果将在后面演示:
(写在最前面,这个Demo存在大量BUG,我只是通过他来演示一些功能,也许在后面我会做一个完整的APP,到时候也会再来写一遍,来说说一些BUG如何处理)。

1.png

富文本编辑器在以前需要使用CoreText来实现,但是不得不说这真的是一个不小的工程,但是在iOS7发布以后,apple发布了TextKit,通过TextKit我们能够轻松实现很多从前难以实现的功能。


1、斜体、下划线混排、字体的增大以及减小
在SB中拖入一个UITextView,此后的所有操作都是对这个TextView中的文字进行操作,先来看几行代码:
<pre><code>
var string = NSMutableAttributedString(attributedString: self.textview.attributedText)
string.addAttribute(NSObliquenessAttributeName, value: 0.5, range: NSMakeRange(0,5))
</code></pre>

我们来解释一下这一段代码:
首先,我们从TextView中获取了string,这个string是NSMutableAttributedString类型的,这个类型继承自String,但是呢,从名字上我们就可以看出来这个类型,我们可以给字符串添加不同的属性,我们回到代码。这段代码的第二句我们通过:

   func addAttribute(name: String, value: AnyObject, range: NSRange)

这个函数给字符串添加了属性,这个API的第1、2个参数就是确定添加的属性类型,在这里我们添加的就是斜体这个属性,斜体这个属性的Value参数填写0~1之间的数值,在这个我们填写的是0.5.来看第二个参数,第二个参数是添加属性的范围,填写的是一个NSRange类型的值,应该不难理解。

以上代码段的功能就是给textview的字符串的第0个字符开始,连续五个字符添加斜体的效果。

是不是十分方便?~~~~~是的。

以上内容只是为了演示UITextView中的attributedText属性,事实上我们不需要这么做(感谢刘大大,告诉我接下来这种做法)。
在TextView中有一个属性叫做typingAttributes,xcode对这个属性的解释是这样的automatically resets when the selection changes,意思就是我们对这个属性进行设置可以改变接下来改变的文字。
也许这样说,不能让人太好的理解,我们在按钮中添加以下代码
<pre><code>
@IBAction func Obliqueness(sender: AnyObject) {
textview.typingAttributes[NSObliquenessAttributeName] = (textview.typingAttributes[NSObliquenessAttributeName] as? NSNumber) == 0 ? 0.5 : 0
}

</code></pre>

当** NSObliquenessAttributeName**的值为0时,点击按钮将将之改变为1,为1时则相反。我们点一下试试,神奇的事情发生啦啦啦啦~~~接下来我们输入的文字都变成了斜体。再次点击则变回正常。

以此类推,我们写出下划线、字体的增大以及减小的代码。
<pre><code>
/**
字体减小

:param: sender
*/
@IBAction func fontincrease(sender: AnyObject) {
    self.fontSize -= 2
    self.textview.typingAttributes[NSFontAttributeName] = UIFont.systemFontOfSize((CGFloat)(self.fontSize))
}
/**
字体增大

:param: sender
*/
@IBAction func fontdecase(sender: AnyObject) {
    self.fontSize += 2
    self.textview.typingAttributes[NSFontAttributeName] = UIFont.systemFontOfSize((CGFloat)(self.fontSize))

}
/**
设置斜体

:param: sender
*/
@IBAction func Obliqueness(sender: AnyObject) {
    textview.typingAttributes[NSObliquenessAttributeName] = (textview.typingAttributes[NSObliquenessAttributeName] as? NSNumber) == 0 ? 0.5 : 0
}
/**
设置下划线

:param: sender
*/
@IBAction func underline(sender: AnyObject) {
    self.textview.typingAttributes[NSUnderlineStyleAttributeName] =  (NSUnderlineStyle.StyleSingle.hashValue ) == 0 ? 1 : NSUnderlineStyle.StyleSingle.hashValue
}
/**

</code></pre>

实现方法都是类似的,十分方便的已经实现了很多功能,这放在以前是不可能的(其实我不知道以前实现究竟有多复杂——!)。


2、插入图片。
前面我们知道TextView存在一个属性叫做attributedText,插入图片需要做的就是在TextView的这个属性中添加图片,上代码。
<pre><code>
//1
var string = NSMutableAttributedString(attributedString: self.textview.attributedText)
//2
var textAttachment = NSTextAttachment()
textAttachment.image = img
//3
var textAttachmentString = NSAttributedString(attachment: textAttachment)
var countString:Int = count(self.textview.text) as Int
string.insertAttributedString(textAttachmentString, atIndex: countString)
//4
textview.attributedText = string
</code></pre>

  • 1、获取当前的attributedString
  • 2、新建一个NSTextAttachment,设置他的图片属性
  • 3、将刚刚创建的NSTextAttachment,添加在原本的attributedString的最后面
  • 4、重定义** textview.attributedText **

以上代码中出现一个变量img,这个变量就是从系统相册获取图片,代码如下:
<pre><code>
@IBAction func photeSelect(sender: AnyObject) {
var sheet:UIActionSheet
if(UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)){
sheet = UIActionSheet(title: nil, delegate: self, cancelButtonTitle: "取消", destructiveButtonTitle: nil, otherButtonTitles: "从相册选择", "拍照")
}else{
sheet = UIActionSheet(title:nil, delegate: self, cancelButtonTitle: "取消", destructiveButtonTitle: nil, otherButtonTitles: "从相册选择")
}
sheet.showInView(self.view)
}
func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: Int) {
var sourceType = UIImagePickerControllerSourceType.PhotoLibrary
if(buttonIndex != 0){
if(buttonIndex==1){ //相册
sourceType = UIImagePickerControllerSourceType.PhotoLibrary
}else{
sourceType = UIImagePickerControllerSourceType.Camera
}
let imagePickerController:UIImagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.allowsEditing = true//true为拍照、选择完进入图片编辑模式
imagePickerController.sourceType = sourceType
self.presentViewController(imagePickerController, animated: true, completion: {
})
}
}
</code></pre>

首先是弹出一个alertView,让你进行选择,选择以后,将调用下面的方法,进入到相册进行选择图片。在这里你需要继承几个协议,不然在选择以后不会触发下面的方法:UIActionSheetDelegate,UIImagePickerControllerDelegate。同时进入相册你需要添加几个库:AssetsLibrary.framework和MobileCoreServices.framework。具体代码解释在这里就不行进解释,今天我们把重点放在富文本的实现上。当你选择图片以后,将促发以下方法,这也是前面添加的协议的功能。
<pre><code>
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]){
var string = NSMutableAttributedString(attributedString: self.textview.attributedText)
var img = info[UIImagePickerControllerEditedImage] as! UIImage
img = self.scaleImage(img)

    var textAttachment       = NSTextAttachment()
    textAttachment.image     = img

    var textAttachmentString = NSAttributedString(attachment: textAttachment)
    var countString:Int      = count(self.textview.text) as Int
    string.insertAttributedString(textAttachmentString, atIndex: countString)

    textview.attributedText  = string
    self.textview.becomeFirstResponder()
    picker.dismissViewControllerAnimated(true, completion: nil)
}

</code></pre>
同时在这个函数中我们给文本插入图片,插入方法在前面已经说过了。

当我们选择图片以后你会发现由于图片太大,所以在界面上只能显示一部分,那么我们就需要压缩图片,压缩方法如下:
<pre><code>
func scaleImage(image:UIImage)->UIImage{
UIGraphicsBeginImageContext(CGSizeMake(self.view.bounds.size.width, image.size.height(self.view.bounds.size.width/image.size.width)))
image.drawInRect(CGRectMake(0, 0, self.view.bounds.size.width, image.size.height
(self.view.bounds.size.width/image.size.width)))
var scaledimage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaled image
}
</code></pre>
这个方法将图片的宽度设置为屏幕的宽度,高度按比例缩放。

好的~~~~~我们终于实现了插入图片,其实在这里有一个很大的问题,就是下面这句话:

<pre><code>
textview.attributedText = string
</code></pre>

我们每次插入图片都将TextView的内容全部改变了一次,这样的做法,先不提在大量文字的情况下可能造成的卡顿问题,同时造成了编排问题,有同学可能已经发现了,当我们用这样的方法插入图片以后,当我们再次改变属性的时候,视图将会瞬间调到最上面,原因就是这句话。至于如何解决~~~~还没想到——!


3、选择字体
改变字体的方法其实和我们设置斜体,下划线等是一样的,方法如下:
<pre><code>
func lovefont(sender:AnyObject){
self.textview.typingAttributes[NSFontAttributeName] = UIFont(name: "1-", size: (CGFloat)(self.fontSize))
}
</code></pre>
那么我们现在的问题就是自定义字体了,毕竟xcode的字体大多不支持中文,同时中文显示的时候不那么优雅,我们要说的就是自定义字体。

加载自定义字体,并不是太过复杂,我在简书看到这篇文章描述加载自定义字体就感觉写的很好,//www.greatytc.com/p/d728570bdf7b 小伙伴们有兴趣的话自行跳转过去看吧,这里就不重复介绍了。

4、制作长图片分享到微信朋友圈与微信好友。

那么我们需要做的第一步就是制作长图片,代码如下:
<pre><code>
func madelongPicture() -> UIImage {

    var image : UIImage!
    UIGraphicsBeginImageContext(self.textview.contentSize)
    var savedContentOffset      = self.textview.contentOffset
    var savedFrame              = self.textview.frame
    self.textview.contentOffset = CGPointZero
    self.textview.frame         = CGRectMake(0, 0, self.textview.contentSize.width, self.textview.contentSize.height)
    self.textview.layer.renderInContext(UIGraphicsGetCurrentContext())
    image                       = UIGraphicsGetImageFromCurrentImageContext()
    self.textview.contentOffset = savedContentOffset
    self.textview.frame         = savedFrame
    UIGraphicsEndPDFContext()
    return image
}

</code></pre>

TextView继承自ScorllView,所以我们只需要给TextView进行截图,就可以制作一张长图片了,方法如上。然后我们需要做的就是调用微信给我们的API,将图片分享到朋友圈。

由于分享的话要申请AppId,所以在这里没有实现这个功能,不过实现的方法并不复杂。

微信分享的文档地址:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&lang=zh_CN

其实里头已经讲的很清楚了,有不明白的亲可以留言哦~


5、调用系统邮件发送文本

调用系统邮件,你可以将刚刚写好的文章或者啥的发送给好朋友,或者交给老师检查~~~~~~~首先我们还是得制作长图片,制作方法上面已经讲过了,就不重复累赘了,这里讲一下如何调用系统邮件。

首先你得继承一个协议:MFMailComposeViewControllerDelegate

然后代码如下:
<pre><code>
@IBAction func email(sender: AnyObject) {
UIApplication.sharedApplication().keyWindow?.endEditing(true)
var configuredMailComposeViewController = MailComposeViewController()
if canSendMail() {
presentViewController(configuredMailComposeViewController, animated: true, completion: nil)
} else {
showSendMailErrorAlert()
}
}

func MailComposeViewController() -> MFMailComposeViewController {
    let mailComposerVC                 = MFMailComposeViewController()
    mailComposerVC.mailComposeDelegate = self

    mailComposerVC.setToRecipients(nil)
    mailComposerVC.setSubject(nil)
    mailComposerVC.setMessageBody(self.textview.text, isHTML: false)
    var addPic                         = self.madelongPicture()
    var imageData                      = UIImagePNGRepresentation(addPic)
    mailComposerVC.addAttachmentData(imageData, mimeType: "", fileName: "longPicture.png")
    return mailComposerVC
}
func canSendMail() -> Bool {
    return MFMailComposeViewController.canSendMail()
}
func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {
    controller.dismissViewControllerAnimated(true, completion: nil)
}
func showSendMailErrorAlert() {
    let sendMailErrorAlert = UIAlertView(title: "Could Not Send Email", message: "Your device could not send e-mail.  Please check e-mail configuration and try again.", delegate: self, cancelButtonTitle: "OK")
    sendMailErrorAlert.show()
}

</code></pre>

代码就在上面了,自己感受一下的,解释不动了。
效果如下:

1.png

接下来也许会造个轮子,同时解决所有的BUG(至少我能发现的)。

代码已上传Github:https://github.com/superxlx/textDemo

亲们,自己下载代码感受一下,然后喜欢的换请点个喜欢同时关注一下我。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,357评论 0 17
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,448评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • 寻找女神的秘密 上篇提到雅典娜原本是一个其貌不扬, 口才普通,际遇一般的年轻女子, 为了实现幸福人生, 获得真爱,...
    爱在六次元阅读 377评论 0 0
  • 着急忙慌去做事,在岔路口看见老爷爷和老奶奶正在分别,手举高,直到一起转身走各自的路。两条路之间隔着竹子,老奶奶落落...
    睡神YZY阅读 251评论 0 0