干貨|swift,富文本編輯器

這一篇我們將實現(xiàn)一個富文本編輯器,擁有功能:

  • 1、斜體、下劃線混排。
  • 2、圖文混排
  • 3、字體大小。
  • 4、選擇自定義字體。
  • 5、制作長圖片分享到微信朋友圈與微信好友。
  • 6、調用系統(tǒng)郵件發(fā)送文本。

先來看一下大體的效果吧,還有一些效果將在后面演示:
(寫在最前面,這個Demo存在大量BUG,我只是通過他來演示一些功能,也許在后面我會做一個完整的APP,到時候也會再來寫一遍,來說說一些BUG如何處理)。

1.png

富文本編輯器在以前需要使用CoreText來實現(xiàn),但是不得不說這真的是一個不小的工程,但是在iOS7發(fā)布以后,apple發(fā)布了TextKit,通過TextKit我們能夠輕松實現(xiàn)很多從前難以實現(xiàn)的功能。


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)

這個函數(shù)給字符串添加了屬性,這個API的第1、2個參數(shù)就是確定添加的屬性類型,在這里我們添加的就是斜體這個屬性,斜體這個屬性的Value參數(shù)填寫0~1之間的數(shù)值,在這個我們填寫的是0.5.來看第二個參數(shù),第二個參數(shù)是添加屬性的范圍,填寫的是一個NSRange類型的值,應該不難理解。

以上代碼段的功能就是給textview的字符串的第0個字符開始,連續(xù)五個字符添加斜體的效果。

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

以上內容只是為了演示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時,點擊按鈕將將之改變?yōu)?,為1時則相反。我們點一下試試,神奇的事情發(fā)生啦啦啦啦~~~接下來我們輸入的文字都變成了斜體。再次點擊則變回正常。

以此類推,我們寫出下劃線、字體的增大以及減小的代碼。
<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>

實現(xiàn)方法都是類似的,十分方便的已經實現(xiàn)了很多功能,這放在以前是不可能的(其實我不知道以前實現(xiàn)究竟有多復雜——!)。


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、將剛剛創(chuàng)建的NSTextAttachment,添加在原本的attributedString的最后面
  • 4、重定義** textview.attributedText **

以上代碼中出現(xiàn)一個變量img,這個變量就是從系統(tǒng)相冊獲取圖片,代碼如下:
<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,讓你進行選擇,選擇以后,將調用下面的方法,進入到相冊進行選擇圖片。在這里你需要繼承幾個協(xié)議,不然在選擇以后不會觸發(fā)下面的方法:UIActionSheetDelegate,UIImagePickerControllerDelegate。同時進入相冊你需要添加幾個庫:AssetsLibrary.framework和MobileCoreServices.framework。具體代碼解釋在這里就不行進解釋,今天我們把重點放在富文本的實現(xiàn)上。當你選擇圖片以后,將促發(fā)以下方法,這也是前面添加的協(xié)議的功能。
<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>
同時在這個函數(shù)中我們給文本插入圖片,插入方法在前面已經說過了。

當我們選擇圖片以后你會發(fā)現(xiàn)由于圖片太大,所以在界面上只能顯示一部分,那么我們就需要壓縮圖片,壓縮方法如下:
<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>
這個方法將圖片的寬度設置為屏幕的寬度,高度按比例縮放。

好的~~~~~我們終于實現(xiàn)了插入圖片,其實在這里有一個很大的問題,就是下面這句話:

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

我們每次插入圖片都將TextView的內容全部改變了一次,這樣的做法,先不提在大量文字的情況下可能造成的卡頓問題,同時造成了編排問題,有同學可能已經發(fā)現(xiàn)了,當我們用這樣的方法插入圖片以后,當我們再次改變屬性的時候,視圖將會瞬間調到最上面,原因就是這句話。至于如何解決~~~~還沒想到——!


3、選擇字體
改變字體的方法其實和我們設置斜體,下劃線等是一樣的,方法如下:
<pre><code>
func lovefont(sender:AnyObject){
self.textview.typingAttributes[NSFontAttributeName] = UIFont(name: "1-", size: (CGFloat)(self.fontSize))
}
</code></pre>
那么我們現(xiàn)在的問題就是自定義字體了,畢竟xcode的字體大多不支持中文,同時中文顯示的時候不那么優(yōu)雅,我們要說的就是自定義字體。

加載自定義字體,并不是太過復雜,我在簡書看到這篇文章描述加載自定義字體就感覺寫的很好,http://www.lxweimin.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,所以在這里沒有實現(xiàn)這個功能,不過實現(xiàn)的方法并不復雜。

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

其實里頭已經講的很清楚了,有不明白的親可以留言哦~


5、調用系統(tǒng)郵件發(fā)送文本

調用系統(tǒng)郵件,你可以將剛剛寫好的文章或者啥的發(fā)送給好朋友,或者交給老師檢查~~~~~~~首先我們還是得制作長圖片,制作方法上面已經講過了,就不重復累贅了,這里講一下如何調用系統(tǒng)郵件。

首先你得繼承一個協(xié)議: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(至少我能發(fā)現(xiàn)的)。

代碼已上傳Github:https://github.com/superxlx/textDemo

親們,自己下載代碼感受一下,然后喜歡的換請點個喜歡同時關注一下我。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容

  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,495評論 0 17
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,771評論 25 708
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,180評論 4 61
  • 尋找女神的秘密 上篇提到雅典娜原本是一個其貌不揚, 口才普通,際遇一般的年輕女子, 為了實現(xiàn)幸福人生, 獲得真愛,...
    愛在六次元閱讀 400評論 0 0
  • 著急忙慌去做事,在岔路口看見老爺爺和老奶奶正在分別,手舉高,直到一起轉身走各自的路。兩條路之間隔著竹子,老奶奶落落...
    睡神YZY閱讀 257評論 0 0