text editing & saving in IOS app - ios

I have a UITextview showing certain text by calling rtf files.
I want to let users to edit the text on UITextView and save the changes they made in rtf file.
Is there any way I can change the rtf file itself by changing the text on UITextView??

If you want a save-as-you-type solution to the problem:
Make your view controller conform to UITextViewDelegate
Connect the text view's delegate to the view controller
Add the following function to your view controller:
// Path to your RTF file
let url = NSBundle.mainBundle().URLForResource("my_document", withExtension: "rtf")!
func textViewDidChange(textView: UITextView) {
let data = try! textView.attributedText.dataFromRange(NSMakeRange(0, textView.attributedText.length), documentAttributes: [NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType])
data.writeToURL(url, atomically: true)
}
This will save every keystroke to the file. No idea on the performance though. I have not tested this on a real iPhone. If it's too hard on the device, consider putting a time delay between saves or have an NSTimer to save every n seconds.

Related

SWIFT iOS - Get text from multi-line uitextview with line breaks as they are visible on the screen

I have a multi-line UITextView and sometimes the text wraps several lines. I want to get the text from it with line breaks from what is visible on the iphone screen.
This is useful because I have a UITextView that changes font size with the UI and I want to capture the text as the user sees it on the screen.
Getting the text from the UITextView with textview.text does not contain those. (Although it does contain any line breaks that a user typed by pressing "return" on the keyboard.)
I think maybe you need to measure the height of the UITextView and go through each character but I am not sure how to do that. I also want it to work with emojis.
It seems that you want to get a snapshot of UITextView instead of get textview.text, so what you need is to learn how to draw UITextView's content as a picture.
For how to drawing a snapshot of UIView, try this.
For how to drawing a snapshot of NSAttribeString, try draw(with:options:context:).
Core Text or TextKit are the best options to work with text.
This answer shows how to get word-wrapped lines using Core Text: https://stackoverflow.com/a/13588322/1837959.
Or you could access the layoutManager (NSLayoutManager) of the UITextView to access the layout data.
Sounds like a hacky solution but kinda not really:
Use the Vision Framework's text recognition to convert an image to text.
convert the view to image
extension UIView {
var toImage: UIImage? {
let renderer = UIGraphicsImageRenderer(bounds: self.bounds)
return renderer.image { (context) in
self.layer.render(in: context.cgContext)
}
}
}
let yourTextView = UITextView()
let img = yourTextView.toImage()
Follow article
guard let cgimg = img.cgImage else {return}
// Create a new image-request handler.
let requestHandler = VNImageRequestHandler(cgImage: cgImage)
// Create a new request to recognize text.
let request = VNRecognizeTextRequest(completion: recognizeTextHandler)
do {
// Perform the text-recognition request.
try requestHandler.perform([request])
} catch {
print("Unable to perform the requests: \(error).")
}
// Process the Results
func recognizeTextHandler(request: VNRequest, error: Error?) {
guard let observations =
request.results as? [VNRecognizedTextObservation] else {
return
}
let recognizedStrings = observations.compactMap { observation in
// Return the string of the top VNRecognizedText instance.
return observation.topCandidates(1).first?.string
}
// Use the result
processResults(recognizedStrings)
}
UITextView Text will show '\n' for user input(when he press enter).
If User does not press enter/return and keep writing the word wrap will happen according to size/width of UITextView. So you should not anything. So if you see "\n"
just change it to , if you are displaying it in Website or using Web Views html layout. Or Just replace all "\n" with space and Don't wrap it let width of the view decide how to wrap it.
Capturing the Screen of IOS iPhone and showing it in same way looks more weird.
For Example.
Think this is
Data even
there is no
return.
It should be in one line.

Instantly format a UITextView using AttributedString

I'm developing a macOS rich-text editor that applies pre-defined style for each line of the text view.
To format the lines, I'm using NSAttributedString, then, I'm inserting that string into my UITextView. To make things easier, I'm using a tool called SwiftRichString.
My code looks like below. It's straight-forward and works fine.
import Cocoa
import SwiftRichString
import AppKit
class ViewController: NSViewController {
#IBOutlet var textView: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
// Format the string
let style = Style {
$0.font = NSFont(name: "Arial", size: 20)
$0.color = NSColor.black
$0.alignment = .center
}
let attributedText = "Hello World!".set(style: style)
// Add formatted string to the text view
textView.textStorage?.append(attributedText)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
Current situation:
User is typing a formatted line. Then when user hits Return and types something, format of the new line returns back to the default style of the UITextView.
What I want:
User is typing a certain formatted line, then he hits Return. The next line should be formatted to another pre-defined style on-the-go.
Example:
User is typing the Title line. Current style is (Arial, bold, 20pt).
He hits Return.
Next line should be styled as Normal Text using a pre-defined style (Arial, 12pt).
Important Note:
In my above code, I was able to format the line easily because it's hard-coded. My real issue is, how can I instantly format the next line, because the next line will be entered by the user. The style should be applied to the next line before user begins writing it.
Okay, I just figured out how to use typingAttributtes to solve this question (thanks to #Larme for the hint).
// Define next attributes
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: NSColor.red,
.font: NSFont(name: "Arial", size: 12)!,
]
// Assign attributes to the text view typing attributes
textView.typingAttributes = attributes
Very easy!
Pardon off topic. If you're making a text editor, you may consider using a table view, where each text line is a cell - this is extra work for you as a programmer but it'll boost the performance significantly. That's how Xcode editor is built.
Maybe you might use optional func textViewDidChange(_ textView: UITextView)
https://developer.apple.com/documentation/uikit/uitextviewdelegate/1618599-textviewdidchange
and after the change, just parse the text, find new lines, split them and apply all styling you want

iOS 10, QLPreviewViewController is not displaying files

I am working on a iOS app that displays lots of different files to the user. From video and audio files to html and Office/iWorks files. I have subclassed QLPreviewController, and want to use it to display some of these files. I create the object, pass it the file's url, set the ViewController's view's frame to replace the webView's frame in the parent viewcontroller. :
else if (QuickLookViewController.canOpenFile(previewItem: contentUrl as NSURL)) {
hideControls()
quickLook.contentURLs = [contentUrl as NSURL]
//add the QuickLookController's view to content display using webview's frame
self.view.addSubview(quickLook.view)
quickLook.view.frame = webview!.frame
quickLook.reloadData()
Using the view debug hierarchy tool, it shows the view set correctly, as well as setting quickLook.backgroundColor = UIColor.black just to test.
So I thought that maybe there was a problem with accessing the download and stored file, so in the viewDidLoad of QuickLookController I added some tests to ensure that the file is there:
super.viewDidLoad()
print("\nContent URL: \(contentURLs[0])")
print("\nContent URL Path: \(contentURLs[0].path!)")
self.delegate = self
self.dataSource = self
var error : NSError?
print("\nis reachable: \(contentURLs[0].checkResourceIsReachableAndReturnError(&error))")
if (error != nil) {
print("is reachable error -> \(error.debugDescription)")
}
print("\ndoes exist: \(FileManager.default.fileExists(atPath: contentURLs[0].path!))")
print("\nCan open: \(QuickLookViewController.canOpenFile(previewItem: contentURLs[0]))")
and the log statements come out as I expect:
Content URL: file:///var/mobile/Containers/Data/Application/B9D5C288-F889-4513-941E-2564F1C12F02/Documents/588c5a1e-dffe-47a8-9824-bc19463aafc2/d88a8dd5-40d1-4fdb-adf3-10fce1f6bf1f/fd73c162-5ac3-4269-8573-9c0b61bef7a7/fd73c162-5ac3-4269-8573-9c0b61bef7a7.pages
Content URL Path: /var/mobile/Containers/Data/Application/B9D5C288-F889-4513-941E-2564F1C12F02/Documents/588c5a1e-dffe-47a8-9824-bc19463aafc2/d88a8dd5-40d1-4fdb-adf3-10fce1f6bf1f/fd73c162-5ac3-4269-8573-9c0b61bef7a7/fd73c162-5ac3-4269-8573-9c0b61bef7a7.pages
is reachable: true
does exist: true
Can open: true
I even used a breakpoint in the the viewDidLoad to check that the quickLook's superview is set using 'po self.view.superview!.frame' as a llbd statement, and again received the output I expected.
I used this same class for another view stack in the app and it displays the files that are clicked, so it isn't making much sense to me. The only difference between the two uses, the second I am presenting the quickLook's view in a viewController that is presented modally.
ok I wanted to post the answer to the problem I was having. The issue was I was not adding the quickLook view controller to the ContentDisplayViewController with a parent-child relationship. So the updated code to add the view I wanted looks like:
//create the parent-child relationship for the view controllers
self.addChildViewController(quickLook)
//give the view it's frame
quickLook.view.frame = webview!.frame
//add the view and call the child's didMove() method.
self.view.addSubview(quickLook.view)
quickLook.didMove(toParentViewController: self)
This question gave me the answer:
Add child view controller to current view controller
Gave me me some reading material to read into, thanks for the post #Piyush Patel

Structuring UIViewControllers with images inside of text

I have to create an application that has 5 UIViewControllers, each with big text and images inside the text. What I have done so far was to add a UITextView in the UIViewController and load with an rtf file the whole text (every text is big).
Now I have to add images inside the text in some places. What do you propose that could be the best way to construct it? I tried to add the image inside the the rtf file but it is not working properly. Since the text is too big I did not want to add the text manually by typing it. Also, I have a top bar menu that slides the view to each content, that is why I had to have only one UITextView. I am looking for a best solution.
What about adding your texts with images on UIWebView with loading these texts by wrapping them into html?
You can also add javascript callbacks which you will be able to handle in swift or obj-c by adding JavascriptCore.framework to your build phases:
Add button in your code:
<button text="Close" onclick="javascript:callSwiftCode()">Call swift code</button>
And in your UIWebViewDelegate class:
func webViewDidFinishLoad(webView: UIWebView) {
let context: JSContext = webView.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") as! JSContext
let codeClosure: #convention(block) ()->() = { ()->() in
print ("This is callback from javascript you can add your code in this closure")
}
let casted: AnyObject = unsafeBitCast(codeClosure, AnyObject.self) as AnyObject
context.setObject(casted, forKeyedSubscript: "callSwiftCode")
}
You can achieve this using NSAttributedString and NSTextAttachment. Attributed strings are strings with formatting attached(bold, italics, colors, etc), but you can also attach the images inside attributed strings, and they just get drawn right along with the text. Below example might help you understand:
//Create a mutable attributed string so that we could append everything to it.
let bigText = NSMutableAttributedString(string: "Your text starts here")
//Create a NSTextAttachment
let image1Attachment = NSTextAttachment()
image1Attachment.image = UIImage(named: "image1.png")
// wrap the attachment in its own attributed string so we can append it
let image1String = NSAttributedString(attachment: image1Attachment)
// add the NSTextAttachment wrapper to our full string, then add some more text.
bigText.appendAttributedString(image1String)
bigText.appendAttributedString(NSAttributedString(string: "End of text"))
// Then set this bigText to your label's attributedText property.
yourLabel.attributedText = bigText

iOS 8 Custom Keyboard

I am trying to build a custom keyboard, it's like a emoji keyboard, but the keyboard's data is from a json file. After parse this json file and get the data, how to make the custom keyboard use it and show in the keyboard view, like the emoji keyboard that built in? Right now, I follow App Extension Keyboard: Custom Keyboard guide, and there only small bits of information here. Is there any tutorial or guide about how to create a custom emoji keyboard online? The current codes I am trying are below:
class KeyboardViewController: UIInputViewController {
override func viewDidLoad() {
super.viewDidLoad()
var error: NSError?
let yanFile = NSBundle.mainBundle().pathForResource("yan", ofType: "json")
let yanData = NSData(contentsOfFile: yanFile) as NSData
let yanDict = NSJSONSerialization.JSONObjectWithData(yanData, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSDictionary
println("dict: \(yanDict)") //print nothing in console
// Perform custom UI setup here
self.nextKeyboardButton = UIButton.buttonWithType(.System) as UIButton
self.nextKeyboardButton.setTitle(NSLocalizedString("Next Keyboard", comment: "Title for 'Next Keyboard' button"), forState: .Normal)
}
}
The json like below:
{
"list":
[
{
"tag": "laugh",
"yan":
[
"o(*≧▽≦)ツ┏━┓",
"(/≥▽≤/)",
"ヾ(o◕∀◕)ノ"
]
},
{
"tag": "wanna",
"yan":
[
"✪ω✪",
"╰(*°▽°*)╯",
"≖‿≖✧",
">ㅂ<",
"ˋ▽ˊ",
"✪ε✪",
"✪υ✪",
"ヾ (o ° ω ° O ) ノ゙",
"(。◕ˇ∀ˇ◕)",
"(¯﹃¯)"
]
}
]
}
You can build a xib file by clicking new file -> view
1) inside the xib file create a uiview 320x216 and you can drag'n'drop whatever controls you want into it
2) then you can load the nib like this into your keyboard's inputView:
// Perform custom UI setup here
UIView *layout = [[[NSBundle mainBundle] loadNibNamed:#"keyboardXib" owner:self options:nil] objectAtIndex:0];
[self.inputView addSubview:layout];
3) i think it's amazing if you build a JSON to keyboard api
you send a JSON of the keyboard map to your app
and the app knows how to arrange the keys on the inputView accordingly
let us know if you build this project!
EDIT:
4) Most of what you need to do is parse the JSON and display the content you want from the JSON uibuttons, and also decide what text they are inserting into the text field
check out this question: How to parse a JSON file in swift?
Good luck!
First of all you need to create your UI for keyboard in your KeyboardViewController. It's up to you how you customize it, add buttons, views, gestures etc.. (By the way height of view is limited, to standard keyboard height size, so don't try to make it higher it won't draw) Template that is generated it's just sample to show how you can put a single button in it. After you setup your UI make sure you have Next Keyboard Button it's required.
Regarding Emoji, it's not real images, they are just unicode characters that later replaced with images by system. So you can't pass images, the only input that you can provide is NSString [self.textDocumentProxy insertText:#"hello "]; // Inserts the string "hello " at the insertion point
More details can be found here https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/ExtensibilityPG/Keyboard.html.

Resources