UITextView Save data - ios

I wanted to know how to save the data in a UITextView once the application is double tapped out/force quitted. Is there some code I can write on the textViewDidChange call that can automatically save the input? The following is my code:
class Coordinator : NSObject,UITextViewDelegate {
var parent : MultiTextField
init(parent1 : MultiTextField) {
parent = parent1
}
func textViewDidBeginEditing(_ textView: UITextView) {
textView.textColor = .black
}
func textViewDidChange(_ textView: UITextView) {
self.parent.obj.size = textView.contentSize.height
}
func textViewDidEndEditing(_ textView: UITextView) {
print("Editing is done")
}

You could save the content in userDefault in textViewDidChange ; and reload content from the userDefault when you load the view.

Related

View coordinator/delegate disappear function?

Is there a function for the view coordinator/delegate for when the view disappears?
I'm trying to create a textview that "autosaves" notes people enter into core data. Basically it just waits 2 seconds since the last textViewDidChange and then saves the data.
It uses a simple Boolean variable for the "queue" to ensure that it won't try and save multiple times within those 2 seconds.
Here is the code for that:
struct BodyTextView: UIViewRepresentable {
#Environment(\.managedObjectContext) var managedObjectContext
#Binding var text: String
var note: Note
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.delegate = context.coordinator
return textView
}
func updateUIView(_ textView: UITextView, context: Context) {
textView.text = text
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator : NSObject, UITextViewDelegate {
var textView: BodyTextView
var saveQueued: Bool = false
init(_ textView: BodyTextView) {
self.textView = textView
}
func textViewDidChange(_ textView: UITextView) {
self.textView.text = textView.text
if (!saveQueued) {
saveQueued = true
Timer.scheduledTimer(withTimeInterval: 2, repeats: false) {_ in
print("saving from textViewDidChange: " + textView.text)
self.textView.note.body = textView.text
saveContext(self.textView.managedObjectContext)
self.saveQueued = false
}
}
}
}
}
This works well so far.
My problem is that if the view disappears before the 2 seconds is up it doesn't save the data. I'm guessing it is because when the view is dismissed it cancels the timer within the struct.
I'd like it to also save when the BodyTextView disappears as well.
Something like this:
func textViewDidDissapear(_ textView: UITextView) {
print("saving from textViewDidDissapear: " + textView.text)
self.textView.note.body = textView.text
saveContext(self.textView.managedObjectContext)
}
How do I do this?
The life cycle methods for SwiftUI.View's are a modifier on the view itself. so If you want to know when the view disappears add an .onDisappear() modifier to it.

Recognise a text by tapping on it in a textField Swift

I am a newbie and want to make a custom class for a UITextView where I will be able to get the text by tapping on it within the UITextView. How to create Custom UITextView class?
Try this, I have created custom textview class and protocol that you need to confirm in your class and implement that method to get text of textview every time when you click textview.
class CustomTextView: UITextView {
weak var delegateCustomTV: CustomTextViewDelegate?
override func awakeFromNib() {
super.awakeFromNib()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.textViewTapped))
self.addGestureRecognizer(tapGestureRecognizer)
}
#objc func textViewTapped() {
delegateCustomTV?.preparedText(text: self.text ?? "")
}
}
protocol CustomTextViewDelegate: class {
func preparedText(text: String)
}
use like i have used below,
class yourViewController: UIViewController, CustomTextViewDelegate {
#IBOutlet weak var textView: CustomTextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegateCustomTV = self
}
func preparedText(text: String) {
// You will get your text here when you click on textview
print(text)
}
}
Try this
override func viewDidLoad() {
super.viewDidLoad()
self.textView.delegate = self
}
extension YourViewController: UITextViewDelegate
{
//MARK:- TextView Editing begins Function
func textViewDidBeginEditing(_ textView: UITextView)
{
//TextView Editing Begin Function
}
// MARK:- TextView text replaced Function
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
{
//Character changed in textView
return true
}
// MARK:- TextView End Editing function
func textViewDidEndEditing(_ textView: UITextView)
{
//End editing of textView
}
}
if you want to get text every time you tap textview you have to add tap gesture
override func viewDidLoad() {
super.viewDidLoad()
self.textView.delegate = self
let tap = UITapGestureRecognizer(target: self, action: #selector(self.textViewTapped(_:)))
self.textView.addGestureRecognizer(tap)
}
#objc func textViewTapped(_ sender: UITapGestureRecognizer){
print(textView.text)
}

Swift - custom default component with delegate

I want to write a custom text view by inheriting default UITextView. My implementation uses some of the methods from the delegate of the original component:
class CustomTextView: UITextView {
fileprivate func applyStyles() {
self.layer.cornerRadius = 5
self.layer.borderColor = .black
self.layer.borderWidth = 5
self.clipsToBounds = true
}
override func awakeFromNib() {
super.awakeFromNib()
applyStyles()
delegate = self
}
}
extension CustomTextView: UITextViewDelegate {
func textViewDidBeginEditing(_ textView: UITextView) {
print("aaaa")
}
func textViewDidEndEditing(_ textView: UITextView) {
print("bbbb")
}
}
Now I want to use this text view everywhere instead of the standard one, but in the current implementation, I lose the ability to set another delegate to the component without losing the delegate functions of my own.
I mean if I will create a component in other class like this:
let customView = CustomTextView()
customView.delegate = self
textViewDidBeginEditing and textViewDidEndEditing functions in CustomTextView will not be called. How can I get around this limitation? Thanks.
The trick is to override the delegate property, so that you can capture any value that is assigned and then call that delegate after your code is done. For this to work, your subclass will need to implement all of the UITextViewDelegate functions in order to pass the invocation on to the "real" delegate:
class CustomTextView: UITextView {
private weak var externalDelegate: UITextViewDelegate?
override var delegate: UITextViewDelegate? {
set {
self.externalDelegate = newValue
}
get {
return self.externalDelegate
}
}
fileprivate func applyStyles() {
self.layer.cornerRadius = 5
self.layer.borderColor = UIColor.black.cgColor
self.layer.borderWidth = 5
self.clipsToBounds = true
}
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
super.delegate = self
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
super.delegate = self
}
}
extension CustomTextView: UITextViewDelegate {
func textViewDidBeginEditing(_ textView: UITextView) {
print("aaaa")
self.externalDelegate?.textViewDidBeginEditing?(textView)
}
func textViewDidEndEditing(_ textView: UITextView) {
print("bbbb")
self.externalDelegate?.textViewDidEndEditing?(textView)
}
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
return self.externalDelegate?.textViewShouldEndEditing?(textView) ?? true
}
func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
return self.externalDelegate?.textViewShouldEndEditing?(textView) ?? true
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return (self.externalDelegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text)) ?? true
}
func textViewDidChange(_ textView: UITextView) {
self.externalDelegate?.textViewDidChange?(textView)
}
func textViewDidChangeSelection(_ textView: UITextView) {
self.externalDelegate?.textViewDidChangeSelection?(textView)
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
return self.externalDelegate?.textView?(textView, shouldInteractWith: URL, in: characterRange, interaction: interaction) ?? true
}
func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
return self.externalDelegate?.textView?(textView, shouldInteractWith: textAttachment, in: characterRange, interaction: interaction) ?? true
}
}
Despite the reason may be unclear behind the fact that you want the callback to be triggered both inside the custom textview class and the parentVc , but you can do
class CustomTextView: UITextView , UITextViewDelegate {
weak var parentVC:VCName?
func textViewDidBeginEditing(_ textView: UITextView) {
print("aaaa")
parentVC?.textViewDidBeginEditing(self)
}
let customView = CustomTextView()
customView.parentVC = self
or do the reverse which is to make the parentVc as the delegate and call the methods of textView subclass from it

extending UITextView and triggering the delegate for onChange events

This code works and fires:
class ViewControllerDisplay: UIViewController, UITextViewDelegate {
//.....
let textView = MyUITextView(frame: CGRect(x:10, y:20, width:270, height:65))
textView.params["dataname"] = _dataname
textView.delegate = self
view.addSubview(textView)
func textViewDidChange(_ textView: UITextView) {
print("YAAAA" + textView.text);
}
however this does not fire
func textViewDidChange(_ textView: MyUITextView) {
print("YAAAA" + textView.text);
}
here is the MyUITextView code
import UIKit
class MyUITextView: UITextView{
var params: Dictionary<String, Any>
required init?(coder aDecoder: NSCoder) {
self.params = [:]
super.init(coder: aDecoder)
}
init(frame: CGRect) {
self.params = [:]
super.init(frame: frame, textContainer: nil)
}
}
so how do I extend the delegate to include MyUITextView in the delegate 'onChange'
The method textViewDidChange (textView: MyUITextView) was not called because in the protocol UITextViewDelegate that method is specified like func textViewDidChange ( textView: UITextView)
If you need to call specifically from the custom class MyUITextView, you need to write a protocol for this class, something like:
protocol MyUITextViewDelegate {
func myTextViewDidChange(_ textView: MyUITextView)
}
Add a field with type MyUITextViewDelegate? in class MyUITextView:
var myDelegate: MyUITextViewDelegate?
Initialize it for example in the viewDidLoad class of the ViewControllerDisplay class:
override func viewDidLoad() {
super.viewDidLoad()
...
textView.myDelegate = self
...
}
The class MyUITextView need to subscribe to the UITextViewDelegate protocol and implement the textViewDidChange (_ textView: UITextView) method in which our protocol is called from myDelegate if it exists:
class MyUITextView: UITextView, UITextViewDelegate {
...
func someInitMethod() {
delegate = self
}
...
func textViewDidChange(_ textView: UITextView) {
myDelegate?.myTextViewDidChange(self)
}
}
And finally, by signing the class ViewControllerDisplay on the protocol MyUITextViewDelegate and implement the necessary method:
class ViewControllerDisplay: UIViewController, MyUITextViewDelegate {
...
func textViewDidChange(_ textView: MyUITextView) {
print("YAAAA" + textView.text);
}
}
ok I figured it out by casting
func textViewDidChange(_ textView: UITextView) {
if let sender = textView as? MyUITextView {
let dataname = sender.params["dataname"] as? String ?? ""
print("MyUITextView: dataname" + dataname + " = " + sender.text!)
}
}

which UITextView is initiating textViewDidChange in Swift?

I have a number of UITextViews in my ViewController. I am using the textViewDidChange method to handle certain user interaction:
func textViewDidChange(textView: UITextView) { // do something
}
My question is, how do I determine which textview is initiating this function, i.e. which textview is the sender?
you can identify the text views by reference
var textview1 : UITextView!
var textview2 : UITextView!
func textViewDidChange(textView: UITextView) { // do something
switch (textView) {
case textview1: // do something with textview1
case textview2: // do something with textview2
default: break
}
}
or assign tags and identify the views by tag. There are several ways.
You can also try this simple technique.
func textViewDidChange(textView: UITextView) {
//textView(Sender)
if(textView == yourtextview)
{
//do something
}
}

Resources