What event is fired when a block of text is pasted into a UITextView? I need to modify the frame of my textView when the text is pasted in.
Your UITextView will call its UITextViewDelegate method
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
if a delegate has been set up. This gets called both when a character is typed on the keyboard, and when text is pasted into the text view. The text pasted in is the replacementText argument.
Here is what i use to detect paste events in UITextView:
// Set this class to be the delegate of the UITextView. Now when a user will paste a text in that textview, this delegate will be called.
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
// Here we check if the replacement text is equal to the string we are currently holding in the paste board
if ([text isEqualToString:[UIPasteboard generalPasteboard].string]) {
// code to execute in case user is using paste
} else {
// code to execute other wise
}
return YES;
}
Checking the pasteboard's string by if string == UIPasteboard.general.string
takes a couple of seconds if you have long sentence in the pasteboard. The user sees the keypad is frozen while this check.
My solution is to check if the length of new characters is longer than 1.
If it is longer than 1, the string is from the pasteboard.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.characters.count > 1{
//User did copy & paste
}else{
//User did input by keypad
}
return true
}
Your UITextView will call its UITextViewDelegate method
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
if a delegate has been set up. This gets called both when a character is typed on the keyboard, and when text is pasted into the text view. The text pasted in is the replacementText argument.
try subclasses UITextview,and override this function.
public override func paste(_ sender: Any?)
This is working Perfect in
Xcode 11x Swift 5x
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text.contains(UIPasteboard.general.string ?? "") {
return false
}
return true
}
When ever the user try to Paste into text field the if condition will execute
This code will stop pasting
UIPasteboard.general.string
in this way causes a prompt to appear "[CurrentApp] pasted from [IncomingApp]" every time the user types. –
Imply It is for Swift5.1
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if let paste = UIPasteboard.general.string, text == paste {
print("paste")
} else {
print("normal typing")
}
return true
}
textView(_, shouldChangeTextIn, replacementText)
can be called in many cases. if you will read UIPasteboard.general.string
value every time, user will see system alert that application accessed pasteboard after every letter typed.
To avoid that problem you can create subclass of UITextView and override paste(_)
function in it. This function is being called by system once every time when user tries to paste something.
Use next call of textView(_, shouldChangeTextIn, replacementText)
to handle paste event.
var isPastingContent = false // helper variable
open override func paste(_ sender: Any?) {
isPastingContent = true // next call of `shouldChangeTextIn` will be for the paste action
super.paste(sender)
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if isPastingContent { // if we detected paste event
// paste detected, do what you want here
let pasteboardContent = UIPasteboard.general.string // you can get pasteboard content here safely. System alert will be shown only once.
isPastingContent = false // toggle helper value back to false
}
}
carlos16196 was a good approach, but I would also tweak it by changing [text isEqualToString:[UIPasteboard generalPasteboard].string]
to [text containsString:[UIPasteboard generalPasteboard].string]
By doing this, you will detect when user pastes in the textview after other typed text that is not in the UIPasteboard.
This is the code:
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
// Here we check if the replacement text is equal to the string we are currently holding in the paste board
if ([text containsString:[UIPasteboard generalPasteboard].string]) {
// code to execute in case user is using paste
} else {
// code to execute other wise
}
return YES;
}
iOS 13 with Combine
public extension UITextView {
var textDidChangePublisher: AnyPublisher<String, Never> {
NotificationCenter.default
.publisher(for: UITextView.textDidChangeNotification, object: self)
.compactMap { $0.object as? UITextView }
.compactMap(\.text)
.eraseToAnyPublisher()
}
var attributedTextDidChangePublisher: AnyPublisher<NSAttributedString, Never> {
NotificationCenter.default
.publisher(for: UITextView.textDidChangeNotification, object: self)
.compactMap { $0.object as? UITextView }
.compactMap(\.attributedText)
.eraseToAnyPublisher()
}
}
var cancellable = Set<AnyCancellable>()
textView.textDidChangePublisher
.removeDuplicates()
.sink { [weak self] newValue in
guard let self = self else { return }
// what you want
}
.store(in: &cancellable)
This is the only way that I was able to get it to work. I used a textField but the same concept should still work for a textView.
In the shouldChangeCharactersIn
delegate method below I casted the string
argument to a NSString, then back to a String. Then I compared it to whatever was pasted. Everything else is in the comments above the code.
// 1. class property for anything that was copied and will be pasted
var pasted: String?
// 2. When the user first taps the textfield set the above class property to the copied text (if there is any)
func textFieldDidBeginEditing(_ textField: UITextField) {
// 3. set it here
pasted = UIPasteboard.general.string // this is what was copied and will be pasted
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let safeText = textField.text else { return true }
let currentString: NSString = safeText as NSString
let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString
let str = newString as String
// 4. compare the above str constant to the pasted variable
if str == self.pasted {
print("pasted")
} else {
print("typed")
}
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
// 5. when the user is finished with the textField set the pasted property to nil
pasted = nil
}
This is what I use to detect pasted images:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if (UIPasteboard.generalPasteboard.image &&
[UIPasteboard.generalPasteboard.string.lowercaseString isEqualToString:text.lowercaseString]) {
//Pasted image
return NO;
}
return YES;
}
In SWIFT 4:
func textViewDidChange(_ textView: UITextView) {
if(textView.text == UIPasteboard.general.string)
{
//Text pasted
}
}
© 2022 - 2024 — McMap. All rights reserved.