用Swift写了一个Textkit图文混排Demo,类似新浪微博的头条文章编辑功能
?
实现如下功能:
?
效果如下:
?
源码地址如下:
https://github.com/thierryxing/swift-textkit-demo
?
这个版本刚刚写完,目前还有很多需要改进的地方,希望有兴趣的同学可以和我一块维护
?
贴上部分代码:
class TextKitViewController: UIViewController, UITextViewDelegate, NSTextStorageDelegate { let textStorage:NSTextStorage = NSTextStorage() let layoutManger = NSLayoutManager() let textViewInset:CGFloat = 10.0 let toolbarHeight:CGFloat = 50.0 var container:NSTextContainer? var textContent:NSMutableAttributedString = NSMutableAttributedString(string: "The NSParagraphStyle class and its subclass NSMutableParagraphStyle encapsulate the paragraph or ruler attributes used by the NSAttributedString classes.", attributes: nil); var textView:UITextView? var range:NSRange = NSRange.init(location: 0, length: 0) var viewWidth:CGFloat? = nil var viewHeight:CGFloat? = nil override func viewDidLoad() { super.viewDidLoad() viewWidth = self.view.frame.size.width viewHeight = self.view.frame.size.height self.addKeyboardNotification() self.initTextView() self.initToolbar() } func addKeyboardNotification(){ NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardShow), name: UIKeyboardWillShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardHide), name: UIKeyboardWillHideNotification, object: nil) } func initTextView(){ container = NSTextContainer(size:CGSizeMake(viewWidth!, CGFloat.max)) container!.widthTracksTextView = true; layoutManger.addTextContainer(container!) textStorage.addLayoutManager(layoutManger) textStorage.delegate = self textView = UITextView(frame: self.view.bounds, textContainer: container); textView?.autoresizingMask = UIViewAutoresizing.FlexibleHeight textView?.scrollEnabled = true; textView?.textContainerInset = UIEdgeInsetsMake(textViewInset, textViewInset, 0, textViewInset) textView?.keyboardDismissMode = UIScrollViewKeyboardDismissMode.OnDrag; textView?.dataDetectorTypes = UIDataDetectorTypes.None textView?.delegate = self textView?.editable = true textView?.selectable = true self.view.addSubview(textView!) textStorage.setAttributedString(textContent); } func initToolbar(){ let numberToolbar = UIToolbar(frame: CGRectMake(0, 0, self.view.frame.size.width, toolbarHeight)) numberToolbar.barStyle = UIBarStyle.Default numberToolbar.items = [ UIBarButtonItem(title: "Insert Picture", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(insertPicture)), UIBarButtonItem(title: "Export Plain Text", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(exportPlainText))] numberToolbar.sizeToFit() textView!.inputAccessoryView = numberToolbar } //MARK: Toolbar function /** insert picture in textview */ func insertPicture(){ textStorage.beginEditing() let image = GMImage(data: UIImagePNGRepresentation(UIImage(named: "testImage")!)!) // set remote url, cause you may want to send it to server image?.remoteUrl = "http://7xjlg5.com1.z0.glb.clouddn.com/1.png" let imgAttachment = NSTextAttachment(data: nil, ofType: nil) let imageWidth = viewWidth!-textViewInset*3 let blankString = NSMutableAttributedString(string: "\n\n", attributes: nil) imgAttachment.image = image imgAttachment.bounds = CGRectMake(0, 0, imageWidth, image!.size.height*(imageWidth/image!.size.width)) let imgAttachmentString = NSAttributedString(attachment:imgAttachment) if range.location==0 || range.location>textContent.length { textContent.appendAttributedString(blankString) textContent.appendAttributedString(imgAttachmentString) textContent.appendAttributedString(blankString) }else{ textContent.insertAttributedString(blankString, atIndex: range.location) textContent.insertAttributedString(imgAttachmentString, atIndex: range.location+blankString.length) textContent.insertAttributedString(NSMutableAttributedString(string: "\n\n", attributes: nil), atIndex: range.location+imgAttachmentString.length+blankString.length) } textStorage.setAttributedString(textContent) textStorage.endEditing() textView!.scrollRangeToVisible(NSMakeRange(textView!.attributedText.length, 0)) } func exportPlainText(){ let exportTextStorage = NSTextStorage() exportTextStorage.setAttributedString(textContent) exportTextStorage.enumerateAttribute(NSAttachmentAttributeName, inRange: NSMakeRange(0, textStorage.length), options:.LongestEffectiveRangeNotRequired) { (value, range, stop) in if (value != nil) { if value is NSTextAttachment{ let attachment = value as! NSTextAttachment let imgTag = "<img src=‘\((attachment.image as! GMImage).remoteUrl)‘/>" exportTextStorage.replaceCharactersInRange(range, withString: imgTag) } } } NSLog("%@", exportTextStorage.string) } // MARK: textView Delegate func textViewDidChange(textView: UITextView) { textContent = textView.attributedText.mutableCopy() as! NSMutableAttributedString } func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool { textView.scrollRangeToVisible(range) return true; } func textViewDidChangeSelection(textView: UITextView) { range = textView.selectedRange; if textStorage.length>=range.location-1{ if let _:NSTextAttachment = textStorage.attribute(NSAttachmentAttributeName, atIndex: range.location-1, effectiveRange: nil) as? NSTextAttachment{ confirmDeleteImage(NSMakeRange(range.location-3, 3)) textView.selectedRange = NSMakeRange(1, 1) } } } func confirmDeleteImage(range:NSRange){ textView?.resignFirstResponder() let alertController = UIAlertController(title: "Delete this image?", message:"", preferredStyle: .ActionSheet) let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil) let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: { (action: UIAlertAction!) in self.textStorage.beginEditing() self.textContent.deleteCharactersInRange(range) self.textStorage.setAttributedString(self.textContent) self.textStorage.endEditing() }) alertController.addAction(cancelAction) alertController.addAction(okAction) self.presentViewController(alertController, animated: true, completion: nil) } // MARK: keyboard Event handle func keyboardShow(noti:NSNotification){ let userInfo:Dictionary = noti.userInfo! let keyboardSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size textView?.frame = CGRectMake(0, 0, viewWidth!, viewHeight! - keyboardSize.height - 10) } func keyboardHide(noti:NSNotification){ textView?.frame = self.view.bounds } // MARK: lifecycle deinit{ NSNotificationCenter.defaultCenter().removeObserver(self) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
?
原文:http://thierry-xing.iteye.com/blog/2289374