├── IMG_0926.PNG ├── README.md ├── TextEditor-2.gif ├── TextEditorDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── TextEditorDemo.xccheckout └── xcuserdata │ └── lifubing.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── TextEditorDemo.xcscheme │ └── xcschememanagement.plist ├── TextEditorDemo ├── AppDelegate.swift ├── Base.lproj │ └── LaunchScreen.xib ├── Header.h ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── fa-bold.imageset │ │ ├── Contents.json │ │ └── fa-bold 2.png │ ├── fa-down.imageset │ │ ├── Contents.json │ │ └── Fill 91.png │ ├── fa-italic.imageset │ │ ├── Contents.json │ │ └── fa-italic 2.png │ ├── fa-left.imageset │ │ ├── Contents.json │ │ └── Fill 215.png │ ├── fa-right.imageset │ │ ├── Contents.json │ │ └── fa-caret-right 2.png │ ├── fa-tu.imageset │ │ ├── Contents.json │ │ └── Fill 249.png │ ├── fa-underline.imageset │ │ ├── Contents.json │ │ └── fa-underline 2.png │ ├── lock.imageset │ │ ├── Contents.json │ │ ├── lock.png │ │ └── lock2.png │ └── share2.imageset │ │ ├── Contents.json │ │ └── sharex2.png ├── Info.plist ├── Main.storyboard ├── TableViewController.swift ├── TextEditorDemo.xcdatamodeld │ ├── .xccurrentversion │ └── TextEditorDemo.xcdatamodel │ │ └── contents ├── lookView.swift └── showtext.swift ├── TextEditorDemoTests ├── Info.plist └── TextEditorDemoTests.swift └── myTest-1.gif /IMG_0926.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/IMG_0926.PNG -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TextEditorDemo 2 | swift:textEditorDemo一个简单的富文本编辑器 3 | ###一个简单的富文本编辑器 4 | (IPhone 5s Xcode 6.3 swift 1.2) 5 | 6 | 实现并解决了一些基本功能: 7 | 8 | 1. 更改字体大小,粗体,下划线,斜体字。并进行了数据的存储 更多请查看网友StringX的文章:http://www.jianshu.com/p/ab5326850e74/comments/327660#comment-327660 9 | 2. 在TextView中添加照片,以及照片存储 10 | 3. 实现键盘隐藏和弹出 11 | 4. 实现默认提示文字效果:点击进行编辑时提示文字自动消失 12 | 5. 解决改变文字属性,TextView自动滑到顶部问题 13 | 6. 让TextView滑到光标所在点 14 | 7. 利用自动布局 实现点击按钮底部工具栏隐藏到右端 ps:没有动画效果。。 15 | 8. 简单封装了提示文字的功能 更多请查看网友johnlui的开源项目:https://github.com/johnlui/SwiftNotice 16 | 9. 设置点击隐藏导航栏,设置滑动隐藏导航栏 17 | 18 | ####重要说明: 19 | 这个Demo可能还隐藏了一些BUG..如果你解决了希望能共享,谢谢! 20 | #### 导入的两个framework是用于选取照片,以及拍照的 21 | 联系方式: 22 | 邮箱:lfb.cd@qq.com QQ:962429707 23 | 还有我的微博号:[lfb](http://weibo.com/p/1005052009667563/home?from=page_100505&mod=TAB#place%20%E6%88%91%E7%9A%84%E5%BE%AE%E5%8D%9A) 24 | 25 | ####1. 更改字体: 26 | ``` 27 | //更改字体大小: 28 | self.text.typingAttributes[NSFontAttributeName] = UIFont.systemFontOfSize((CGFloat)(self.fontSize)) 29 | //下划线: 30 | self.text.typingAttributes[NSUnderlineStyleAttributeName] = 1 31 | //粗体: 32 | self.text.typingAttributes[NSFontAttributeName] = UIFont.boldSystemFontOfSize((CGFloat)(self.fontSize)) 33 | //斜体: 34 | text.typingAttributes[NSObliquenessAttributeName] = 0.5 35 | ``` 36 | 37 | ####2. 插入图片: 38 | ``` 39 | /* 40 | //选取照片 41 | */ 42 | @IBAction func photeSelect(sender: AnyObject) { 43 | self.text.resignFirstResponder() 44 | var sheet:UIActionSheet 45 | if(UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)){ 46 | sheet = UIActionSheet(title: nil, delegate: self, cancelButtonTitle: "取消", destructiveButtonTitle: nil,otherButtonTitles: "从相册选择", "拍照") 47 | }else{ 48 | sheet = UIActionSheet(title:nil, delegate: self, cancelButtonTitle: "取消", destructiveButtonTitle: nil, otherButtonTitles: "从相册选择") 49 | } 50 | sheet.showInView(self.view) 51 | } 52 | func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: Int) { 53 | var sourceType = UIImagePickerControllerSourceType.PhotoLibrary 54 | if(buttonIndex != 0){ 55 | if(buttonIndex==1){ //相册 56 | sourceType = UIImagePickerControllerSourceType.PhotoLibrary 57 | self.text.resignFirstResponder() 58 | }else{ 59 | sourceType = UIImagePickerControllerSourceType.Camera 60 | } 61 | let imagePickerController:UIImagePickerController = UIImagePickerController() 62 | imagePickerController.delegate = self 63 | imagePickerController.allowsEditing = true //true为拍照、选择完进入图片编辑模式 64 | imagePickerController.sourceType = sourceType 65 | self.presentViewController(imagePickerController, animated: true, completion: { 66 | }) 67 | } 68 | } 69 | 70 | func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]){ 71 | var string:NSMutableAttributedString 72 | string = NSMutableAttributedString(attributedString: self.text.attributedText) 73 | var img = info[UIImagePickerControllerEditedImage] as! UIImage 74 | img = self.scaleImage(img) 75 | var textAttachment= NSTextAttachment() 76 | textAttachment.image = img 77 | var textAttachmentString = NSAttributedString(attachment: textAttachment) 78 | 79 | 80 | var countString:Int = count(self.text.text) as Int 81 | string.insertAttributedString(textAttachmentString, atIndex: countString) //可以用这个函数实现 插入到光标所在点 ps:如果你实现了希望能共享 82 | text.attributedText = string 83 | /* 84 | // 85 | */ 86 | //string.appendAttributedString(textAttachmentString) 87 | picker.dismissViewControllerAnimated(true, completion: nil) 88 | 89 | } 90 | 91 | func scaleImage(image:UIImage)->UIImage{ 92 | UIGraphicsBeginImageContext(CGSizeMake(self.view.bounds.size.width, image.size.height*(self.view.bounds.size.width/image.size.width))) 93 | image.drawInRect(CGRectMake(0, 0, self.view.bounds.size.width, image.size.height*(self.view.bounds.size.width/image.size.width))) 94 | var scaledimage = UIGraphicsGetImageFromCurrentImageContext() 95 | UIGraphicsEndImageContext() 96 | return scaledimage 97 | 98 | } 99 | 100 | ``` 101 | 102 | ####3. 实现键盘隐藏和弹出 103 | 104 | ``` 105 | /* 106 | //此bool 标志是为了让键盘 出现和隐藏 成对出现,否则会出现跳出两次的情况.我也只有用这样的办法解决 = = 107 | // ps:如果你有更好的解决办法,希望能与我分享哦!上面有一个联系方式的 108 | */ 109 | var bool:Bool = true 110 | func handleKeyboardWillShowNotification(notification: NSNotification) { 111 | if bool { 112 | keyboardWillChangeFrameWithNotification(notification, showsKeyboard: true) 113 | println("---show") 114 | bool = !bool 115 | } 116 | } 117 | func handleKeyboardWillHideNotification(notification: NSNotification) { 118 | if !bool { 119 | keyboardWillChangeFrameWithNotification(notification, showsKeyboard: false) 120 | println("---hide") 121 | bool = !bool 122 | 123 | } 124 | } 125 | 126 | func keyboardWillChangeFrameWithNotification(notification: NSNotification, showsKeyboard: Bool) { 127 | println("4") 128 | let userInfo = notification.userInfo! 129 | let animationDuration: NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue 130 | // Convert the keyboard frame from screen to view coordinates. 131 | let keyboardScreenBeginFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue() 132 | let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue() 133 | 134 | let keyboardViewBeginFrame = view.convertRect(keyboardScreenBeginFrame, fromView: view.window) 135 | let keyboardViewEndFrame = view.convertRect(keyboardScreenEndFrame, fromView: view.window) 136 | var originDelta = abs((keyboardViewEndFrame.origin.y - keyboardViewBeginFrame.origin.y)) 137 | println("the origin:\(originDelta)") 138 | // The text view should be adjusted, update the constant for this constraint. 139 | if showsKeyboard { 140 | textViewBottomLayoutGuideConstraint.constant += (originDelta) 141 | self.toolBarLayOut.constant += originDelta 142 | }else { 143 | textViewBottomLayoutGuideConstraint.constant -= (originDelta) 144 | self.toolBarLayOut.constant -= originDelta 145 | } 146 | UIView.animateWithDuration(animationDuration, delay: 0, options: .BeginFromCurrentState, animations: { 147 | self.view.layoutIfNeeded() 148 | }, completion: nil) 149 | 150 | // Scroll to the selected text once the keyboard frame changes. 151 | self.text.scrollRangeToVisible(self.text.selectedRange) //让TextView滑到光标所在地方 152 | } 153 | 154 | ``` 155 | 156 | ####4. 实现默认提示文字效果:点击进行编辑时提示文字自动消失 157 | ``` 158 | /* 159 | // 实现默认提示文字效果:点击文字则会自动消失。 160 | */ 161 | 162 | func textViewShouldBeginEditing(textView: UITextView) -> Bool { 163 | if !isThereHavedata { 164 | text.text = "" 165 | text.textColor = UIColor.blackColor() 166 | isThereHavedata = true 167 | } 168 | return true 169 | } 170 | ``` 171 | 172 | ####5. 解决改变文字属性,TextView自动滑到顶部问题 173 | ``` 174 | self.text.layoutManager.allowsNonContiguousLayout = false //用于解决改变文字属性,TextView自动滑到顶部问题 175 | 176 | 177 | ####6.让TextView滑到光标所在点 178 | 179 | self.text.scrollRangeToVisible(self.text.selectedRange) 180 | ####7.利用自动布局 实现点击按钮底部工具栏隐藏到右端 181 | ``` 182 | @IBAction func toright(sender: UIBarButtonItem) { 183 | if self.toRight.constant < 0{ //简单判断左移还是右移 184 | self.Toolbar.layer.cornerRadius = 22 //改成圆角 185 | self.toRight.constant += (text.bounds.width - 10) 186 | sender.image = UIImage(named: "fa-left") //改变图片 187 | }else { 188 | self.Toolbar.layer.cornerRadius = 0 //恢复原来不是圆角那样 189 | self.toRight.constant -= (text.bounds.width - 10) 190 | sender.image = UIImage(named: "fa-right") 191 | } 192 | } 193 | 194 | 195 | ####8.简单封装了提示文字的功能 196 | ``` 197 | //复制showtext.swift文件到工程 198 | Notice.showText("减小字体", fontsize: fontSize,obliqueness: 0)//弹出提示 199 | 200 | ``` 201 | ####9.设置点击隐藏导航栏,设置滑动隐藏导航栏 202 | ``` 203 | self.navigationController?.hidesBarsOnTap = false //设置点击隐藏导航栏,false为取消 204 | self.navigationController?.hidesBarsOnSwipe = true //设置滑动隐藏导航栏 205 | ``` 206 | 207 | ####10.解决UITextView经常出现光标不在最下方的情况 208 | ``` 209 | /* 210 | 使用UITextView的时候经常出现光标不在最下方的情况。。。(iOS8) 211 | 解决方法: 212 | 1、首先去除所有的Padding: 213 | self.text.textContainerInset = UIEdgeInsetsZero 214 | self.text.textContainer.lineFragmentPadding = 0 215 | 216 | 2、然后在委托方法里加上一行: 217 | func textViewDidChange(textView: UITextView) { 218 | self.text.scrollRangeToVisible(self.text.selectedRange) 219 | } 220 | ps:委托方法在最下边。 221 | */ 222 | self.text.textContainerInset = UIEdgeInsetsZero 223 | self.text.textContainer.lineFragmentPadding = 0 224 | ``` 225 | 226 | ####项目地址 227 | 228 | [github地址](https://github.com/lfb-cd/TextEditorDemo) 229 | 230 | https://github.com/lfb-cd/TextEditorDemo 231 | 232 | 如果有更新微博上会发消息的:[我的微博](http://weibo.com/p/1005052009667563/home?from=page_100505&mod=TAB#place%20%E6%88%91%E7%9A%84%E5%BE%AE%E5%8D%9A) 233 | 234 | 235 | 236 | 237 | ####效果浏览: 238 | ![image](https://github.com/lfb-cd/TextEditorDemo/blob/master/IMG_0926.PNG) 239 | 240 | 241 | (gif图片大约10MB): 242 | 243 | ![image](https://github.com/lfb-cd/TextEditorDemo/blob/master/myTest-1.gif) 244 | ![image](https://github.com/lfb-cd/TextEditorDemo/blob/master/TextEditor-2.gif) 245 | -------------------------------------------------------------------------------- /TextEditor-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/TextEditor-2.gif -------------------------------------------------------------------------------- /TextEditorDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 67B26BDE1B0F2C07002C9D5B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B26BDD1B0F2C07002C9D5B /* AppDelegate.swift */; }; 11 | 67B26BE11B0F2C07002C9D5B /* TextEditorDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 67B26BDF1B0F2C07002C9D5B /* TextEditorDemo.xcdatamodeld */; }; 12 | 67B26BE81B0F2C07002C9D5B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 67B26BE71B0F2C07002C9D5B /* Images.xcassets */; }; 13 | 67B26BEB1B0F2C07002C9D5B /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67B26BE91B0F2C07002C9D5B /* LaunchScreen.xib */; }; 14 | 67B26BF71B0F2C07002C9D5B /* TextEditorDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B26BF61B0F2C07002C9D5B /* TextEditorDemoTests.swift */; }; 15 | 67B26C011B0F2C2B002C9D5B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67B26C001B0F2C2B002C9D5B /* Main.storyboard */; }; 16 | 67B26C031B0F2C79002C9D5B /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B26C021B0F2C79002C9D5B /* TableViewController.swift */; }; 17 | 67B26C051B0F2C7E002C9D5B /* lookView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B26C041B0F2C7E002C9D5B /* lookView.swift */; }; 18 | 67B26C071B0F2C82002C9D5B /* showtext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B26C061B0F2C82002C9D5B /* showtext.swift */; }; 19 | 67B26C091B0F520C002C9D5B /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B26C081B0F520C002C9D5B /* AssetsLibrary.framework */; }; 20 | 67B26C0B1B0F5216002C9D5B /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B26C0A1B0F5216002C9D5B /* MobileCoreServices.framework */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 67B26BF11B0F2C07002C9D5B /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 67B26BD01B0F2C07002C9D5B /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 67B26BD71B0F2C07002C9D5B; 29 | remoteInfo = TextEditorDemo; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 67B26BD81B0F2C07002C9D5B /* TextEditorDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TextEditorDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 67B26BDC1B0F2C07002C9D5B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | 67B26BDD1B0F2C07002C9D5B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 67B26BE01B0F2C07002C9D5B /* TextEditorDemo.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TextEditorDemo.xcdatamodel; sourceTree = ""; }; 38 | 67B26BE71B0F2C07002C9D5B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 39 | 67B26BEA1B0F2C07002C9D5B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 40 | 67B26BF01B0F2C07002C9D5B /* TextEditorDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TextEditorDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 67B26BF51B0F2C07002C9D5B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | 67B26BF61B0F2C07002C9D5B /* TextEditorDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEditorDemoTests.swift; sourceTree = ""; }; 43 | 67B26C001B0F2C2B002C9D5B /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 44 | 67B26C021B0F2C79002C9D5B /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; 45 | 67B26C041B0F2C7E002C9D5B /* lookView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = lookView.swift; sourceTree = ""; }; 46 | 67B26C061B0F2C82002C9D5B /* showtext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = showtext.swift; sourceTree = ""; }; 47 | 67B26C081B0F520C002C9D5B /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; }; 48 | 67B26C0A1B0F5216002C9D5B /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; 49 | 67B26C0C1B1042D7002C9D5B /* Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Header.h; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 67B26BD51B0F2C07002C9D5B /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | 67B26C0B1B0F5216002C9D5B /* MobileCoreServices.framework in Frameworks */, 58 | 67B26C091B0F520C002C9D5B /* AssetsLibrary.framework in Frameworks */, 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | 67B26BED1B0F2C07002C9D5B /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXFrameworksBuildPhase section */ 70 | 71 | /* Begin PBXGroup section */ 72 | 67B26BCF1B0F2C07002C9D5B = { 73 | isa = PBXGroup; 74 | children = ( 75 | 67B26C0A1B0F5216002C9D5B /* MobileCoreServices.framework */, 76 | 67B26C081B0F520C002C9D5B /* AssetsLibrary.framework */, 77 | 67B26BDA1B0F2C07002C9D5B /* TextEditorDemo */, 78 | 67B26BF31B0F2C07002C9D5B /* TextEditorDemoTests */, 79 | 67B26BD91B0F2C07002C9D5B /* Products */, 80 | ); 81 | sourceTree = ""; 82 | }; 83 | 67B26BD91B0F2C07002C9D5B /* Products */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 67B26BD81B0F2C07002C9D5B /* TextEditorDemo.app */, 87 | 67B26BF01B0F2C07002C9D5B /* TextEditorDemoTests.xctest */, 88 | ); 89 | name = Products; 90 | sourceTree = ""; 91 | }; 92 | 67B26BDA1B0F2C07002C9D5B /* TextEditorDemo */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 67B26BDD1B0F2C07002C9D5B /* AppDelegate.swift */, 96 | 67B26C0C1B1042D7002C9D5B /* Header.h */, 97 | 67B26C061B0F2C82002C9D5B /* showtext.swift */, 98 | 67B26C021B0F2C79002C9D5B /* TableViewController.swift */, 99 | 67B26C001B0F2C2B002C9D5B /* Main.storyboard */, 100 | 67B26C041B0F2C7E002C9D5B /* lookView.swift */, 101 | 67B26BE71B0F2C07002C9D5B /* Images.xcassets */, 102 | 67B26BE91B0F2C07002C9D5B /* LaunchScreen.xib */, 103 | 67B26BDF1B0F2C07002C9D5B /* TextEditorDemo.xcdatamodeld */, 104 | 67B26BDB1B0F2C07002C9D5B /* Supporting Files */, 105 | ); 106 | path = TextEditorDemo; 107 | sourceTree = ""; 108 | }; 109 | 67B26BDB1B0F2C07002C9D5B /* Supporting Files */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 67B26BDC1B0F2C07002C9D5B /* Info.plist */, 113 | ); 114 | name = "Supporting Files"; 115 | sourceTree = ""; 116 | }; 117 | 67B26BF31B0F2C07002C9D5B /* TextEditorDemoTests */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 67B26BF61B0F2C07002C9D5B /* TextEditorDemoTests.swift */, 121 | 67B26BF41B0F2C07002C9D5B /* Supporting Files */, 122 | ); 123 | path = TextEditorDemoTests; 124 | sourceTree = ""; 125 | }; 126 | 67B26BF41B0F2C07002C9D5B /* Supporting Files */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 67B26BF51B0F2C07002C9D5B /* Info.plist */, 130 | ); 131 | name = "Supporting Files"; 132 | sourceTree = ""; 133 | }; 134 | /* End PBXGroup section */ 135 | 136 | /* Begin PBXNativeTarget section */ 137 | 67B26BD71B0F2C07002C9D5B /* TextEditorDemo */ = { 138 | isa = PBXNativeTarget; 139 | buildConfigurationList = 67B26BFA1B0F2C07002C9D5B /* Build configuration list for PBXNativeTarget "TextEditorDemo" */; 140 | buildPhases = ( 141 | 67B26BD41B0F2C07002C9D5B /* Sources */, 142 | 67B26BD51B0F2C07002C9D5B /* Frameworks */, 143 | 67B26BD61B0F2C07002C9D5B /* Resources */, 144 | ); 145 | buildRules = ( 146 | ); 147 | dependencies = ( 148 | ); 149 | name = TextEditorDemo; 150 | productName = TextEditorDemo; 151 | productReference = 67B26BD81B0F2C07002C9D5B /* TextEditorDemo.app */; 152 | productType = "com.apple.product-type.application"; 153 | }; 154 | 67B26BEF1B0F2C07002C9D5B /* TextEditorDemoTests */ = { 155 | isa = PBXNativeTarget; 156 | buildConfigurationList = 67B26BFD1B0F2C07002C9D5B /* Build configuration list for PBXNativeTarget "TextEditorDemoTests" */; 157 | buildPhases = ( 158 | 67B26BEC1B0F2C07002C9D5B /* Sources */, 159 | 67B26BED1B0F2C07002C9D5B /* Frameworks */, 160 | 67B26BEE1B0F2C07002C9D5B /* Resources */, 161 | ); 162 | buildRules = ( 163 | ); 164 | dependencies = ( 165 | 67B26BF21B0F2C07002C9D5B /* PBXTargetDependency */, 166 | ); 167 | name = TextEditorDemoTests; 168 | productName = TextEditorDemoTests; 169 | productReference = 67B26BF01B0F2C07002C9D5B /* TextEditorDemoTests.xctest */; 170 | productType = "com.apple.product-type.bundle.unit-test"; 171 | }; 172 | /* End PBXNativeTarget section */ 173 | 174 | /* Begin PBXProject section */ 175 | 67B26BD01B0F2C07002C9D5B /* Project object */ = { 176 | isa = PBXProject; 177 | attributes = { 178 | LastUpgradeCheck = 0630; 179 | ORGANIZATIONNAME = lifubing; 180 | TargetAttributes = { 181 | 67B26BD71B0F2C07002C9D5B = { 182 | CreatedOnToolsVersion = 6.3; 183 | }; 184 | 67B26BEF1B0F2C07002C9D5B = { 185 | CreatedOnToolsVersion = 6.3; 186 | TestTargetID = 67B26BD71B0F2C07002C9D5B; 187 | }; 188 | }; 189 | }; 190 | buildConfigurationList = 67B26BD31B0F2C07002C9D5B /* Build configuration list for PBXProject "TextEditorDemo" */; 191 | compatibilityVersion = "Xcode 3.2"; 192 | developmentRegion = English; 193 | hasScannedForEncodings = 0; 194 | knownRegions = ( 195 | en, 196 | Base, 197 | ); 198 | mainGroup = 67B26BCF1B0F2C07002C9D5B; 199 | productRefGroup = 67B26BD91B0F2C07002C9D5B /* Products */; 200 | projectDirPath = ""; 201 | projectRoot = ""; 202 | targets = ( 203 | 67B26BD71B0F2C07002C9D5B /* TextEditorDemo */, 204 | 67B26BEF1B0F2C07002C9D5B /* TextEditorDemoTests */, 205 | ); 206 | }; 207 | /* End PBXProject section */ 208 | 209 | /* Begin PBXResourcesBuildPhase section */ 210 | 67B26BD61B0F2C07002C9D5B /* Resources */ = { 211 | isa = PBXResourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | 67B26BEB1B0F2C07002C9D5B /* LaunchScreen.xib in Resources */, 215 | 67B26C011B0F2C2B002C9D5B /* Main.storyboard in Resources */, 216 | 67B26BE81B0F2C07002C9D5B /* Images.xcassets in Resources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | 67B26BEE1B0F2C07002C9D5B /* Resources */ = { 221 | isa = PBXResourcesBuildPhase; 222 | buildActionMask = 2147483647; 223 | files = ( 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | }; 227 | /* End PBXResourcesBuildPhase section */ 228 | 229 | /* Begin PBXSourcesBuildPhase section */ 230 | 67B26BD41B0F2C07002C9D5B /* Sources */ = { 231 | isa = PBXSourcesBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | 67B26C051B0F2C7E002C9D5B /* lookView.swift in Sources */, 235 | 67B26C071B0F2C82002C9D5B /* showtext.swift in Sources */, 236 | 67B26BE11B0F2C07002C9D5B /* TextEditorDemo.xcdatamodeld in Sources */, 237 | 67B26BDE1B0F2C07002C9D5B /* AppDelegate.swift in Sources */, 238 | 67B26C031B0F2C79002C9D5B /* TableViewController.swift in Sources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | 67B26BEC1B0F2C07002C9D5B /* Sources */ = { 243 | isa = PBXSourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | 67B26BF71B0F2C07002C9D5B /* TextEditorDemoTests.swift in Sources */, 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | }; 250 | /* End PBXSourcesBuildPhase section */ 251 | 252 | /* Begin PBXTargetDependency section */ 253 | 67B26BF21B0F2C07002C9D5B /* PBXTargetDependency */ = { 254 | isa = PBXTargetDependency; 255 | target = 67B26BD71B0F2C07002C9D5B /* TextEditorDemo */; 256 | targetProxy = 67B26BF11B0F2C07002C9D5B /* PBXContainerItemProxy */; 257 | }; 258 | /* End PBXTargetDependency section */ 259 | 260 | /* Begin PBXVariantGroup section */ 261 | 67B26BE91B0F2C07002C9D5B /* LaunchScreen.xib */ = { 262 | isa = PBXVariantGroup; 263 | children = ( 264 | 67B26BEA1B0F2C07002C9D5B /* Base */, 265 | ); 266 | name = LaunchScreen.xib; 267 | sourceTree = ""; 268 | }; 269 | /* End PBXVariantGroup section */ 270 | 271 | /* Begin XCBuildConfiguration section */ 272 | 67B26BF81B0F2C07002C9D5B /* Debug */ = { 273 | isa = XCBuildConfiguration; 274 | buildSettings = { 275 | ALWAYS_SEARCH_USER_PATHS = NO; 276 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 277 | CLANG_CXX_LIBRARY = "libc++"; 278 | CLANG_ENABLE_MODULES = YES; 279 | CLANG_ENABLE_OBJC_ARC = YES; 280 | CLANG_WARN_BOOL_CONVERSION = YES; 281 | CLANG_WARN_CONSTANT_CONVERSION = YES; 282 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 283 | CLANG_WARN_EMPTY_BODY = YES; 284 | CLANG_WARN_ENUM_CONVERSION = YES; 285 | CLANG_WARN_INT_CONVERSION = YES; 286 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 287 | CLANG_WARN_UNREACHABLE_CODE = YES; 288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 289 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 290 | COPY_PHASE_STRIP = NO; 291 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 292 | ENABLE_STRICT_OBJC_MSGSEND = YES; 293 | GCC_C_LANGUAGE_STANDARD = gnu99; 294 | GCC_DYNAMIC_NO_PIC = NO; 295 | GCC_NO_COMMON_BLOCKS = YES; 296 | GCC_OPTIMIZATION_LEVEL = 0; 297 | GCC_PREPROCESSOR_DEFINITIONS = ( 298 | "DEBUG=1", 299 | "$(inherited)", 300 | ); 301 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 302 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 303 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 304 | GCC_WARN_UNDECLARED_SELECTOR = YES; 305 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 306 | GCC_WARN_UNUSED_FUNCTION = YES; 307 | GCC_WARN_UNUSED_VARIABLE = YES; 308 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 309 | MTL_ENABLE_DEBUG_INFO = YES; 310 | ONLY_ACTIVE_ARCH = YES; 311 | SDKROOT = iphoneos; 312 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 313 | TARGETED_DEVICE_FAMILY = "1,2"; 314 | }; 315 | name = Debug; 316 | }; 317 | 67B26BF91B0F2C07002C9D5B /* Release */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | ALWAYS_SEARCH_USER_PATHS = NO; 321 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 322 | CLANG_CXX_LIBRARY = "libc++"; 323 | CLANG_ENABLE_MODULES = YES; 324 | CLANG_ENABLE_OBJC_ARC = YES; 325 | CLANG_WARN_BOOL_CONVERSION = YES; 326 | CLANG_WARN_CONSTANT_CONVERSION = YES; 327 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 328 | CLANG_WARN_EMPTY_BODY = YES; 329 | CLANG_WARN_ENUM_CONVERSION = YES; 330 | CLANG_WARN_INT_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_UNREACHABLE_CODE = YES; 333 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 334 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 335 | COPY_PHASE_STRIP = NO; 336 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 337 | ENABLE_NS_ASSERTIONS = NO; 338 | ENABLE_STRICT_OBJC_MSGSEND = YES; 339 | GCC_C_LANGUAGE_STANDARD = gnu99; 340 | GCC_NO_COMMON_BLOCKS = YES; 341 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 342 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 343 | GCC_WARN_UNDECLARED_SELECTOR = YES; 344 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 345 | GCC_WARN_UNUSED_FUNCTION = YES; 346 | GCC_WARN_UNUSED_VARIABLE = YES; 347 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 348 | MTL_ENABLE_DEBUG_INFO = NO; 349 | SDKROOT = iphoneos; 350 | TARGETED_DEVICE_FAMILY = "1,2"; 351 | VALIDATE_PRODUCT = YES; 352 | }; 353 | name = Release; 354 | }; 355 | 67B26BFB1B0F2C07002C9D5B /* Debug */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 359 | INFOPLIST_FILE = TextEditorDemo/Info.plist; 360 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 361 | PRODUCT_NAME = "$(TARGET_NAME)"; 362 | }; 363 | name = Debug; 364 | }; 365 | 67B26BFC1B0F2C07002C9D5B /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 369 | INFOPLIST_FILE = TextEditorDemo/Info.plist; 370 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 371 | PRODUCT_NAME = "$(TARGET_NAME)"; 372 | }; 373 | name = Release; 374 | }; 375 | 67B26BFE1B0F2C07002C9D5B /* Debug */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | BUNDLE_LOADER = "$(TEST_HOST)"; 379 | FRAMEWORK_SEARCH_PATHS = ( 380 | "$(SDKROOT)/Developer/Library/Frameworks", 381 | "$(inherited)", 382 | ); 383 | GCC_PREPROCESSOR_DEFINITIONS = ( 384 | "DEBUG=1", 385 | "$(inherited)", 386 | ); 387 | INFOPLIST_FILE = TextEditorDemoTests/Info.plist; 388 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 389 | PRODUCT_NAME = "$(TARGET_NAME)"; 390 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TextEditorDemo.app/TextEditorDemo"; 391 | }; 392 | name = Debug; 393 | }; 394 | 67B26BFF1B0F2C07002C9D5B /* Release */ = { 395 | isa = XCBuildConfiguration; 396 | buildSettings = { 397 | BUNDLE_LOADER = "$(TEST_HOST)"; 398 | FRAMEWORK_SEARCH_PATHS = ( 399 | "$(SDKROOT)/Developer/Library/Frameworks", 400 | "$(inherited)", 401 | ); 402 | INFOPLIST_FILE = TextEditorDemoTests/Info.plist; 403 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 404 | PRODUCT_NAME = "$(TARGET_NAME)"; 405 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TextEditorDemo.app/TextEditorDemo"; 406 | }; 407 | name = Release; 408 | }; 409 | /* End XCBuildConfiguration section */ 410 | 411 | /* Begin XCConfigurationList section */ 412 | 67B26BD31B0F2C07002C9D5B /* Build configuration list for PBXProject "TextEditorDemo" */ = { 413 | isa = XCConfigurationList; 414 | buildConfigurations = ( 415 | 67B26BF81B0F2C07002C9D5B /* Debug */, 416 | 67B26BF91B0F2C07002C9D5B /* Release */, 417 | ); 418 | defaultConfigurationIsVisible = 0; 419 | defaultConfigurationName = Release; 420 | }; 421 | 67B26BFA1B0F2C07002C9D5B /* Build configuration list for PBXNativeTarget "TextEditorDemo" */ = { 422 | isa = XCConfigurationList; 423 | buildConfigurations = ( 424 | 67B26BFB1B0F2C07002C9D5B /* Debug */, 425 | 67B26BFC1B0F2C07002C9D5B /* Release */, 426 | ); 427 | defaultConfigurationIsVisible = 0; 428 | }; 429 | 67B26BFD1B0F2C07002C9D5B /* Build configuration list for PBXNativeTarget "TextEditorDemoTests" */ = { 430 | isa = XCConfigurationList; 431 | buildConfigurations = ( 432 | 67B26BFE1B0F2C07002C9D5B /* Debug */, 433 | 67B26BFF1B0F2C07002C9D5B /* Release */, 434 | ); 435 | defaultConfigurationIsVisible = 0; 436 | }; 437 | /* End XCConfigurationList section */ 438 | 439 | /* Begin XCVersionGroup section */ 440 | 67B26BDF1B0F2C07002C9D5B /* TextEditorDemo.xcdatamodeld */ = { 441 | isa = XCVersionGroup; 442 | children = ( 443 | 67B26BE01B0F2C07002C9D5B /* TextEditorDemo.xcdatamodel */, 444 | ); 445 | currentVersion = 67B26BE01B0F2C07002C9D5B /* TextEditorDemo.xcdatamodel */; 446 | path = TextEditorDemo.xcdatamodeld; 447 | sourceTree = ""; 448 | versionGroupType = wrapper.xcdatamodel; 449 | }; 450 | /* End XCVersionGroup section */ 451 | }; 452 | rootObject = 67B26BD01B0F2C07002C9D5B /* Project object */; 453 | } 454 | -------------------------------------------------------------------------------- /TextEditorDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TextEditorDemo.xcodeproj/project.xcworkspace/xcshareddata/TextEditorDemo.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 49591E6C-6119-4C21-8B4F-8BF93CD96AA8 9 | IDESourceControlProjectName 10 | TextEditorDemo 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | B3151F8697654B7896AC61F3DA0EFA8B144220EF 14 | https://github.com/lfb-cd/TextEditorDemo.git 15 | 16 | IDESourceControlProjectPath 17 | TextEditorDemo.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | B3151F8697654B7896AC61F3DA0EFA8B144220EF 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/lfb-cd/TextEditorDemo.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | B3151F8697654B7896AC61F3DA0EFA8B144220EF 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | B3151F8697654B7896AC61F3DA0EFA8B144220EF 36 | IDESourceControlWCCName 37 | TextEditorDemo 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /TextEditorDemo.xcodeproj/xcuserdata/lifubing.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /TextEditorDemo.xcodeproj/xcuserdata/lifubing.xcuserdatad/xcschemes/TextEditorDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /TextEditorDemo.xcodeproj/xcuserdata/lifubing.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TextEditorDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 67B26BD71B0F2C07002C9D5B 16 | 17 | primary 18 | 19 | 20 | 67B26BEF1B0F2C07002C9D5B 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /TextEditorDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TextEditorDemo 4 | // 5 | // Created by lifubing on 15/5/22. 6 | // Copyright (c) 2015年 lifubing. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | 18 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 19 | // Override point for customization after application launch. 20 | return true 21 | } 22 | 23 | func applicationWillResignActive(application: UIApplication) { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | func applicationDidEnterBackground(application: UIApplication) { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | func applicationWillEnterForeground(application: UIApplication) { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | func applicationDidBecomeActive(application: UIApplication) { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | func applicationWillTerminate(application: UIApplication) { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | // Saves changes in the application's managed object context before the application terminates. 44 | self.saveContext() 45 | } 46 | 47 | // MARK: - Core Data stack 48 | 49 | lazy var applicationDocumentsDirectory: NSURL = { 50 | // The directory the application uses to store the Core Data store file. This code uses a directory named "lfb.com.TextEditorDemo" in the application's documents Application Support directory. 51 | let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) 52 | return urls[urls.count-1] as! NSURL 53 | }() 54 | 55 | lazy var managedObjectModel: NSManagedObjectModel = { 56 | // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model. 57 | let modelURL = NSBundle.mainBundle().URLForResource("TextEditorDemo", withExtension: "momd")! 58 | return NSManagedObjectModel(contentsOfURL: modelURL)! 59 | }() 60 | 61 | lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = { 62 | // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. 63 | // Create the coordinator and store 64 | var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) 65 | let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("TextEditorDemo.sqlite") 66 | var error: NSError? = nil 67 | var failureReason = "There was an error creating or loading the application's saved data." 68 | if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil { 69 | coordinator = nil 70 | // Report any error we got. 71 | var dict = [String: AnyObject]() 72 | dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" 73 | dict[NSLocalizedFailureReasonErrorKey] = failureReason 74 | dict[NSUnderlyingErrorKey] = error 75 | error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) 76 | // Replace this with code to handle the error appropriately. 77 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 78 | NSLog("Unresolved error \(error), \(error!.userInfo)") 79 | abort() 80 | } 81 | 82 | return coordinator 83 | }() 84 | 85 | lazy var managedObjectContext: NSManagedObjectContext? = { 86 | // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail. 87 | let coordinator = self.persistentStoreCoordinator 88 | if coordinator == nil { 89 | return nil 90 | } 91 | var managedObjectContext = NSManagedObjectContext() 92 | managedObjectContext.persistentStoreCoordinator = coordinator 93 | return managedObjectContext 94 | }() 95 | 96 | // MARK: - Core Data Saving support 97 | 98 | func saveContext () { 99 | if let moc = self.managedObjectContext { 100 | var error: NSError? = nil 101 | if moc.hasChanges && !moc.save(&error) { 102 | // Replace this implementation with code to handle the error appropriately. 103 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 104 | NSLog("Unresolved error \(error), \(error!.userInfo)") 105 | abort() 106 | } 107 | } 108 | } 109 | 110 | } 111 | 112 | -------------------------------------------------------------------------------- /TextEditorDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /TextEditorDemo/Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Header.h 3 | // MyTest 4 | // 5 | // Created by lifubing on 15/5/21. 6 | // Copyright (c) 2015年 lifubing. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface Header : NSManagedObject 13 | @property (nonatomic,retain) NSAttributedString *attributedText;//使得数据库可以直接存储NSAttributedString格式数据 14 | 15 | @end -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-bold.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "fa-bold 2.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-bold.imageset/fa-bold 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/TextEditorDemo/Images.xcassets/fa-bold.imageset/fa-bold 2.png -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-down.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "Fill 91.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-down.imageset/Fill 91.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/TextEditorDemo/Images.xcassets/fa-down.imageset/Fill 91.png -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-italic.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "fa-italic 2.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-italic.imageset/fa-italic 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/TextEditorDemo/Images.xcassets/fa-italic.imageset/fa-italic 2.png -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-left.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "Fill 215.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-left.imageset/Fill 215.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/TextEditorDemo/Images.xcassets/fa-left.imageset/Fill 215.png -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-right.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "fa-caret-right 2.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-right.imageset/fa-caret-right 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/TextEditorDemo/Images.xcassets/fa-right.imageset/fa-caret-right 2.png -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-tu.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "Fill 249.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-tu.imageset/Fill 249.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/TextEditorDemo/Images.xcassets/fa-tu.imageset/Fill 249.png -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-underline.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "fa-underline 2.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/fa-underline.imageset/fa-underline 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/TextEditorDemo/Images.xcassets/fa-underline.imageset/fa-underline 2.png -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/lock.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "lock.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "lock2.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/lock.imageset/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/TextEditorDemo/Images.xcassets/lock.imageset/lock.png -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/lock.imageset/lock2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/TextEditorDemo/Images.xcassets/lock.imageset/lock2.png -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/share2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "sharex2.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TextEditorDemo/Images.xcassets/share2.imageset/sharex2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/TextEditorDemo/Images.xcassets/share2.imageset/sharex2.png -------------------------------------------------------------------------------- /TextEditorDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | lfb.com.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /TextEditorDemo/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /TextEditorDemo/TableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.swift 3 | // MyCoreData 4 | // 5 | // Created by lifubing on 15/5/3. 6 | // Copyright (c) 2015年 lifubing. All rights reserved. 7 | // 8 | // 9 | // 一个功能还算完整的富文本编辑器 (IPhone 5s Xcode 6.3 swift 1.2) 10 | // 11 | // 实现并解决了一些基本功能: 12 | // 1.更改字体大小,粗体,下划线,斜体字。并进行了数据的存储 更多请查看网友StringX的文章:http://www.jianshu.com/p/ab5326850e74/comments/327660#comment-327660 13 | // 2.在TextView中添加照片,以及照片存储 14 | // 3.实现键盘隐藏和弹出 15 | // 4.实现默认提示文字效果:点击进行编辑时提示文字自动消失 16 | // 5.解决改变文字属性,TextView自动滑到顶部问题 17 | // 6.让TextView滑到光标所在点 18 | // 7.利用自动布局 实现点击按钮底部工具栏隐藏到右端 ps:没有动画效果。。。 19 | // 8.简单封装了提示文字的功能 更多请查看网友johnlui的开源项目:https://github.com/johnlui/SwiftNotice 20 | // 9.设置点击隐藏导航栏,设置滑动隐藏导航栏 21 | // 10.解决UITextView经常出现光标不在最下方的情况 22 | // 23 | // 最后重要说明:这个Demo可能还隐藏了一些BUG:如果你解决了希望能共享,谢谢! 24 | // 导入的两个framework是用于选取照片,以及拍照的 25 | // 联系方式: 26 | // 邮箱:lfb.cd@qq.com QQ:962429707 27 | // 2015/6/5/20:12 28 | 29 | import UIKit 30 | import CoreData 31 | 32 | class TableViewController: UITableViewController { 33 | 34 | var array:[AnyObject] = [] 35 | var string:String = "" 36 | var context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext 37 | 38 | override func viewWillAppear(animated: Bool){ 39 | reFlash() 40 | } 41 | override func viewDidLoad() { 42 | super.viewDidLoad() 43 | getData() 44 | // Uncomment the following line to preserve selection between presentations 45 | // self.clearsSelectionOnViewWillAppear = false 46 | 47 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller. 48 | // self.navigationItem.rightBarButtonItem = self.editButtonItem() 49 | } 50 | 51 | func reFlash(){ 52 | getData() 53 | self.tableView.reloadData() 54 | } 55 | 56 | func getData(){ 57 | // var applicationDelegate = UIApplication.sharedApplication().delegate as! AppDelegate 58 | // var managedObjectContext = applicationDelegate.managedObjectContext 59 | var fetchRequest = NSFetchRequest(entityName: "Users") 60 | var error:NSError? 61 | var fetchResults = context?.executeFetchRequest(fetchRequest, error: &error)! 62 | if let results = fetchResults{ 63 | // println(array) 64 | self.array = results 65 | }else{ 66 | println(error) 67 | } 68 | } 69 | override func didReceiveMemoryWarning() { 70 | super.didReceiveMemoryWarning() 71 | // Dispose of any resources that can be recreated. 72 | } 73 | 74 | // MARK: - Table view data source 75 | 76 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 77 | // #warning Potentially incomplete method implementation. 78 | // Return the number of sections. 79 | return 1 80 | } 81 | 82 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 83 | // #warning Incomplete method implementation. 84 | // Return the number of rows in the section. 85 | return array.count 86 | } 87 | 88 | 89 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 90 | let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell 91 | 92 | // Configure the cell... 93 | cell.textLabel?.text = array[array.count - 1 - indexPath.row].valueForKey("string") as? String //这样使后面的数据呈现在前面 94 | return cell 95 | } 96 | 97 | 98 | 99 | // Override to support conditional editing of the table view. 100 | override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { 101 | // Return NO if you do not want the specified item to be editable. 102 | return true 103 | } 104 | 105 | 106 | 107 | override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 108 | if editingStyle == .Delete { 109 | println("delete") 110 | context?.deleteObject(array[array.count - 1 - indexPath.row] as! NSManagedObject) 111 | context?.save(nil) 112 | reFlash() 113 | } 114 | 115 | } 116 | 117 | // MARK: - Navigation 118 | 119 | // In a storyboard-based application, you will often want to do a little preparation before navigation 120 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 121 | // Get the new view controller using [segue destinationViewController]. 122 | // Pass the selected object to the new view controller. 123 | 124 | if segue.identifier == "edit" { 125 | if let secondeVc = segue.destinationViewController as? lookView { 126 | let indexPath = self.tableView.indexPathForSelectedRow() 127 | let rowd = array[array.count - 1 - indexPath!.row] as! NSManagedObject 128 | secondeVc.Data = rowd 129 | secondeVc.toSave = true //这两个都是做标记用的,可以先不管。 130 | secondeVc.isThereHavedata = true 131 | } 132 | println("edit") 133 | }else if (segue.identifier == "add"){ 134 | if let secondeVc = segue.destinationViewController as? lookView { 135 | let indexPath = self.tableView.indexPathForSelectedRow() 136 | let row: AnyObject = NSEntityDescription.insertNewObjectForEntityForName("Users", inManagedObjectContext: context!) 137 | row.setValue("", forKey: "string") 138 | context?.save(nil) 139 | secondeVc.Data = row as! NSManagedObject 140 | secondeVc.isThereHavedata = false 141 | println("add") 142 | } 143 | }else { 144 | NSLog("Error") 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /TextEditorDemo/TextEditorDemo.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | TextEditorDemo.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /TextEditorDemo/TextEditorDemo.xcdatamodeld/TextEditorDemo.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /TextEditorDemo/lookView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // lookView.swift 3 | // MyCoreData 4 | // 5 | // Created by lifubing on 15/5/3. 6 | // Copyright (c) 2015年 lifubing. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | import MobileCoreServices 12 | import AssetsLibrary 13 | 14 | class lookView: UIViewController,UIImagePickerControllerDelegate,UITextViewDelegate,UIToolbarDelegate,UIActionSheetDelegate,UINavigationControllerDelegate{ 15 | var Data:NSManagedObject! //接收传入数据 16 | let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext//获取入口类的context 17 | 18 | var isThereHavedata = false //判断是否有新数据 19 | var toSave:Bool = false //用于退出时删除数据 20 | var fontSize: CGFloat = 24.0 21 | 22 | @IBOutlet weak var Toolbar: UIToolbar! 23 | @IBOutlet weak var text: UITextView! 24 | @IBOutlet weak var toRight: NSLayoutConstraint! 25 | @IBOutlet weak var toolBarLayOut: NSLayoutConstraint! 26 | @IBOutlet weak var textViewBottomLayoutGuideConstraint: NSLayoutConstraint! 27 | 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | 32 | self.navigationController?.hidesBarsOnTap = false //设置点击隐藏导航栏,false为取消 33 | self.navigationController?.hidesBarsOnSwipe = true //设置滑动隐藏导航栏 34 | setup() 35 | //self.tabBarController?.hidesBottomBarWhenPushed = true //设置压入时是否隐藏导航栏 36 | } 37 | 38 | func setup(){ 39 | /* 40 | 使用UITextView的时候经常出现光标不在最下方的情况。。。(iOS8) 41 | 解决方法: 42 | 1、首先去除所有的Padding: 43 | self.text.textContainerInset = UIEdgeInsetsZero 44 | self.text.textContainer.lineFragmentPadding = 0 45 | 46 | 2、然后在委托方法里加上一行: 47 | func textViewDidChange(textView: UITextView) { 48 | self.text.scrollRangeToVisible(self.text.selectedRange) 49 | } 50 | ps:委托方法在最下边。 51 | */ 52 | self.text.textContainerInset = UIEdgeInsetsZero 53 | self.text.textContainer.lineFragmentPadding = 0 54 | 55 | self.text.layoutManager.allowsNonContiguousLayout = false //用于解决改变文字属性,TextView自动滑到顶部问题 56 | self.Toolbar.clipsToBounds = true 57 | if isThereHavedata { 58 | text.text = Data.valueForKey("string") as! String 59 | text.textColor = UIColor.blackColor() 60 | }else { 61 | text.text = "Typing you want!" 62 | text.textColor = UIColor.lightGrayColor() 63 | } 64 | 65 | if Data.valueForKey("theAttributedText") != nil { 66 | let size = Data.valueForKey("theAttributedText") as! NSAttributedString 67 | text.attributedText = size 68 | } 69 | self.text.editable = true 70 | } 71 | 72 | /* 73 | //移动Toolbar到右边 74 | */ 75 | @IBAction func toright(sender: UIBarButtonItem) { 76 | if self.toRight.constant < 0{ //简单判断左移还是右移 77 | self.Toolbar.layer.cornerRadius = 22 //改成圆角 78 | self.toRight.constant += (text.bounds.width - 10) 79 | sender.image = UIImage(named: "fa-left") //改变图片 80 | }else { 81 | self.Toolbar.layer.cornerRadius = 0 //恢复原来不是圆角那样 82 | self.toRight.constant -= (text.bounds.width - 10) 83 | sender.image = UIImage(named: "fa-right") 84 | } 85 | } 86 | /* 87 | //减小字体 88 | */ 89 | @IBAction func down(sender: AnyObject) { 90 | if fontSize > 16 { 91 | self.fontSize = text.font.pointSize 92 | self.fontSize -= 2 93 | self.text.typingAttributes[NSFontAttributeName] = UIFont.systemFontOfSize((CGFloat)(self.fontSize)) 94 | } 95 | Notice.showText("减小字体", fontsize: fontSize,obliqueness: 0)//弹出提示 96 | 97 | } 98 | 99 | /* 100 | //增大字体 101 | */ 102 | @IBAction func up(sender: AnyObject) { 103 | 104 | if fontSize < 40 { 105 | self.fontSize = text.font.pointSize 106 | fontSize += 4 107 | self.text.typingAttributes[NSFontAttributeName] = UIFont.systemFontOfSize((CGFloat)(self.fontSize)) 108 | 109 | } 110 | Notice.showText("增大字体", fontsize: fontSize,obliqueness: 0)//弹出提示 111 | } 112 | /* 113 | //隐藏键盘 114 | */ 115 | @IBAction func keybordDown(sender: UIBarButtonItem) { 116 | self.text.resignFirstResponder() 117 | } 118 | 119 | /* 120 | //下划线 121 | */ 122 | @IBAction func underLine(sender: UIBarButtonItem) { 123 | let changedFontDescriptor = UIFont(name:NSUnderlineStyleAttributeName, size: fontSize) 124 | let typ = text.typingAttributes[NSUnderlineStyleAttributeName] as? NSNumber 125 | if (typ == 1) { 126 | self.text.typingAttributes[NSUnderlineStyleAttributeName] = 0 127 | Notice.showText("取消下划线", fontsize: fontSize, obliqueness: 0)//弹出提示 128 | }else { 129 | self.text.typingAttributes[NSUnderlineStyleAttributeName] = 1 130 | Notice.showText("下划线", fontsize: fontSize, obliqueness: 0)//弹出提示 131 | 132 | } 133 | 134 | } 135 | /* 136 | //粗体字 137 | */ 138 | @IBAction func bold(sender: UIBarButtonItem) { 139 | let changedFontDescriptor = UIFont.systemFontOfSize((CGFloat)(self.fontSize)) 140 | let typ = text.typingAttributes[NSFontAttributeName] as? UIFont 141 | if ( typ == changedFontDescriptor) { 142 | self.text.typingAttributes[NSFontAttributeName] = UIFont.boldSystemFontOfSize((CGFloat)(self.fontSize)) 143 | Notice.showText("粗体", fontsize: fontSize, obliqueness: 2) 144 | }else { 145 | self.text.typingAttributes[NSFontAttributeName] = UIFont.systemFontOfSize((CGFloat)(self.fontSize)) 146 | Notice.showText("取消粗体", fontsize: fontSize, obliqueness: 2) 147 | 148 | } 149 | 150 | } 151 | /* 152 | //斜体字 153 | */ 154 | @IBAction func Obliqueness(sender: UIBarButtonItem) { 155 | var typ = text.typingAttributes[NSObliquenessAttributeName] as? NSNumber 156 | if typ == 0.5 { 157 | // text.typingAttributes[NSObliquenessAttributeName] = (text.typingAttributes[NSObliquenessAttributeName] as? NSNumber) == 0.5 ? 0 : 0.5 158 | text.typingAttributes[NSObliquenessAttributeName] = 0 159 | Notice.showText("取消斜体", fontsize: fontSize, obliqueness: 1) 160 | }else { 161 | text.typingAttributes[NSObliquenessAttributeName] = 0.5 162 | Notice.showText("斜体", fontsize: fontSize, obliqueness: 0) 163 | } 164 | 165 | } 166 | 167 | @IBAction func save(sender: AnyObject) { 168 | toSave = true //默认是要删除数据的,用这个判断是否保存数据 169 | Data.setValue(text.text, forKey: "string") 170 | Data.setValue(text.attributedText, forKey: "theAttributedText") 171 | context?.save(nil) 172 | self.navigationController?.popViewControllerAnimated(true) 173 | } 174 | 175 | /* 176 | //选取照片 177 | */ 178 | @IBAction func photeSelect(sender: AnyObject) { 179 | self.text.resignFirstResponder() 180 | var sheet:UIActionSheet 181 | if(UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)){ 182 | sheet = UIActionSheet(title: nil, delegate: self, cancelButtonTitle: "取消", destructiveButtonTitle: nil,otherButtonTitles: "从相册选择", "拍照") 183 | }else{ 184 | sheet = UIActionSheet(title:nil, delegate: self, cancelButtonTitle: "取消", destructiveButtonTitle: nil, otherButtonTitles: "从相册选择") 185 | } 186 | sheet.showInView(self.view) 187 | } 188 | func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: Int) { 189 | var sourceType = UIImagePickerControllerSourceType.PhotoLibrary 190 | if(buttonIndex != 0){ 191 | if(buttonIndex==1){ //相册 192 | sourceType = UIImagePickerControllerSourceType.PhotoLibrary 193 | self.text.resignFirstResponder() 194 | }else{ 195 | sourceType = UIImagePickerControllerSourceType.Camera 196 | } 197 | let imagePickerController:UIImagePickerController = UIImagePickerController() 198 | imagePickerController.delegate = self 199 | imagePickerController.allowsEditing = true //true为拍照、选择完进入图片编辑模式 200 | imagePickerController.sourceType = sourceType 201 | self.presentViewController(imagePickerController, animated: true, completion: { 202 | }) 203 | } 204 | } 205 | 206 | func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]){ 207 | var string:NSMutableAttributedString = NSMutableAttributedString(attributedString: self.text.attributedText) 208 | var img = info[UIImagePickerControllerEditedImage] as! UIImage 209 | img = self.scaleImage(img) 210 | var textAttachment = NSTextAttachment() 211 | textAttachment.image = img 212 | var textAttachmentString = NSAttributedString(attachment: textAttachment) 213 | 214 | var countString:Int = count(self.text.text) as Int 215 | string.insertAttributedString(textAttachmentString, atIndex: countString) //可以用这个函数实现 插入到光标所在点 ps:如果你实现了希望能共享 216 | text.attributedText = string 217 | /* 218 | // 219 | */ 220 | //string.appendAttributedString(textAttachmentString) //也可以直接添加都后面 221 | picker.dismissViewControllerAnimated(true, completion: nil) 222 | 223 | } 224 | 225 | func scaleImage(image:UIImage)->UIImage{ 226 | UIGraphicsBeginImageContext(CGSizeMake(self.view.bounds.size.width, image.size.height*(self.view.bounds.size.width/image.size.width))) 227 | image.drawInRect(CGRectMake(0, 0, self.view.bounds.size.width, image.size.height*(self.view.bounds.size.width/image.size.width))) 228 | var scaledimage = UIGraphicsGetImageFromCurrentImageContext() 229 | UIGraphicsEndImageContext() 230 | return scaledimage 231 | 232 | } 233 | 234 | override func viewWillAppear(animated: Bool) { 235 | super.viewWillAppear(animated) 236 | 237 | // Listen for changes to keyboard visibility so that we can adjust the text view accordingly. 238 | 239 | /* 240 | // 用于注册键盘通知服务 241 | */ 242 | let notificationCenter = NSNotificationCenter.defaultCenter() 243 | notificationCenter.addObserver(self, selector: "handleKeyboardWillShowNotification:", name: UIKeyboardWillShowNotification, object: nil) 244 | notificationCenter.addObserver(self, selector: "handleKeyboardWillHideNotification:", name: UIKeyboardWillHideNotification, object: nil) 245 | } 246 | 247 | /* 248 | //退出时判断是否保存数据 249 | */ 250 | 251 | @IBAction func cancel(sender: AnyObject) { 252 | if !toSave { 253 | context?.deleteObject(Data) 254 | context?.save(nil) 255 | } 256 | println("cancel") 257 | self.navigationController?.popViewControllerAnimated(true) 258 | NSNotificationCenter.defaultCenter().removeObserver(self) 259 | } 260 | 261 | /* 262 | //此bool 标志是为了让键盘 出现和隐藏 成对出现,否则会出现跳出两次的情况.我也只有用这样的办法解决 = = 263 | // ps:如果你有更好的解决办法,希望能与我分享哦!上面有一个联系方式的 264 | */ 265 | var bool:Bool = true 266 | func handleKeyboardWillShowNotification(notification: NSNotification) { 267 | if bool { 268 | keyboardWillChangeFrameWithNotification(notification, showsKeyboard: true) 269 | println("---show") 270 | bool = !bool 271 | } 272 | } 273 | func handleKeyboardWillHideNotification(notification: NSNotification) { 274 | if !bool { 275 | keyboardWillChangeFrameWithNotification(notification, showsKeyboard: false) 276 | println("---hide") 277 | bool = !bool 278 | 279 | } 280 | } 281 | 282 | deinit { 283 | NSNotificationCenter.defaultCenter().removeObserver(self) 284 | } 285 | 286 | func keyboardWillChangeFrameWithNotification(notification: NSNotification, showsKeyboard: Bool) { 287 | println("4") 288 | 289 | let userInfo = notification.userInfo! 290 | let animationDuration: NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue 291 | // Convert the keyboard frame from screen to view coordinates. 292 | let keyboardScreenBeginFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue() 293 | let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue() 294 | 295 | let keyboardViewBeginFrame = view.convertRect(keyboardScreenBeginFrame, fromView: view.window) 296 | let keyboardViewEndFrame = view.convertRect(keyboardScreenEndFrame, fromView: view.window) 297 | println(keyboardViewBeginFrame.origin.y) 298 | println(keyboardViewEndFrame.origin.y) 299 | var originDelta = abs((keyboardViewEndFrame.origin.y - keyboardViewBeginFrame.origin.y)) 300 | println("the origin:\(originDelta)") 301 | // The text view should be adjusted, update the constant for this constraint. 302 | if showsKeyboard { 303 | textViewBottomLayoutGuideConstraint.constant += (originDelta) 304 | self.toolBarLayOut.constant += originDelta 305 | println(self.toolBarLayOut.constant) 306 | }else { 307 | textViewBottomLayoutGuideConstraint.constant -= (originDelta) 308 | self.toolBarLayOut.constant -= originDelta 309 | } 310 | UIView.animateWithDuration(animationDuration, delay: 0, options: .BeginFromCurrentState, animations: { 311 | self.view.layoutIfNeeded() 312 | }, completion: nil) 313 | 314 | // Scroll to the selected text once the keyboard frame changes. 315 | self.text.scrollRangeToVisible(self.text.selectedRange) //让TextView滑到光标所在地方 316 | } 317 | 318 | override func didReceiveMemoryWarning() { 319 | super.didReceiveMemoryWarning() 320 | // Dispose of any resources that can be recreated. 321 | } 322 | 323 | /* 324 | //UITextViewDelegate 325 | */ 326 | 327 | /* 328 | // 实现默认提示文字效果:点击文字则会自动消失。 329 | */ 330 | 331 | func textViewShouldBeginEditing(textView: UITextView) -> Bool { 332 | if !isThereHavedata { 333 | text.text = "" 334 | text.textColor = UIColor.blackColor() 335 | isThereHavedata = true 336 | } 337 | return true 338 | } 339 | 340 | /* 341 | // 让TextView滑到光标所在地方 342 | */ 343 | 344 | func textViewDidChange(textView: UITextView) { 345 | self.text.scrollRangeToVisible(self.text.selectedRange) 346 | } 347 | 348 | /* 349 | // MARK: - Navigation 350 | 351 | // In a storyboard-based application, you will often want to do a little preparation before navigation 352 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 353 | // Get the new view controller using segue.destinationViewController. 354 | // Pass the selected object to the new view controller. 355 | } 356 | */ 357 | 358 | } 359 | -------------------------------------------------------------------------------- /TextEditorDemo/showtext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // showtext.swift 3 | // MyTest 4 | // 5 | // Created by lifubing on 15/5/21. 6 | // Copyright (c) 2015年 lifubing. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension UIViewController { 13 | func noticeText(text:String,fontsize:CGFloat,obliqueness:NSNumber){ 14 | Notice.showText(text, fontsize: fontsize ,obliqueness: obliqueness) 15 | } 16 | func noticeText(text:String,fontsize:CGFloat){ 17 | } 18 | } 19 | 20 | class Notice:NSObject { 21 | static var windows = Array() 22 | static let rv = UIApplication.sharedApplication().keyWindow?.subviews.first as! UIView 23 | static func clear() { 24 | windows.removeAll(keepCapacity: false) 25 | } 26 | 27 | static func showText(text: String,fontsize:CGFloat,obliqueness:NSNumber) { 28 | let frame = CGRectMake(0, 0, 200, 60) 29 | let window = UIWindow(frame: frame) 30 | let mainView = UIView(frame: frame) 31 | mainView.layer.cornerRadius = 12//设置圆角 32 | mainView.backgroundColor = UIColor(red:0, green:0, blue:0, alpha: 0.8) 33 | 34 | let label = UILabel(frame: frame) 35 | label.text = text 36 | if obliqueness == 0{ 37 | label.font = UIFont.systemFontOfSize(fontsize) 38 | }else if (obliqueness == 1) { 39 | label.font = UIFont(name: NSObliquenessAttributeName, size: fontsize) 40 | 41 | }else if (obliqueness == 2) { 42 | label.font = UIFont.boldSystemFontOfSize(fontsize) 43 | } 44 | label.textAlignment = NSTextAlignment.Center 45 | label.textColor = UIColor.whiteColor() 46 | mainView.addSubview(label) 47 | 48 | window.windowLevel = UIWindowLevelAlert 49 | window.center = rv.center 50 | window.frame = CGRectMake(rv.center.x - frame.size.width/2, rv.center.y/3, frame.size.width, frame.size.height) 51 | window.hidden = false 52 | window.addSubview(mainView) 53 | windows.append(window) 54 | 55 | 56 | let selector = Selector("hideNotice:") 57 | self.performSelector(selector, withObject: mainView, afterDelay: 1) 58 | } 59 | static func hideNotice(sender: AnyObject) { 60 | // if sender is UIView { 61 | // sender.removeFromSuperview() 62 | // (sender as! UIView). = false 63 | // } 64 | sender.removeFromSuperview() 65 | windows.removeAll(keepCapacity: false)//将生成的windows也同时删除,否则会影响到下一层view的操作 66 | 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /TextEditorDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | lfb.com.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /TextEditorDemoTests/TextEditorDemoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextEditorDemoTests.swift 3 | // TextEditorDemoTests 4 | // 5 | // Created by lifubing on 15/5/22. 6 | // Copyright (c) 2015年 lifubing. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class TextEditorDemoTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /myTest-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb-cd/TextEditorDemo/fb19c84e036cec2ef8a9a30d144580879bfd1194/myTest-1.gif --------------------------------------------------------------------------------