1

Before I begin, let me acknowledge that similar questions exist, some with answers, others without. However, they do not address my specific problem. If you know of any, please refer me.

Now, when I add images to the UITextView, I place the images at the point where the cursor is. I achieve this by appending 5 spaces each time to make room for the image. When the cursor gets to the end of the UItextView, it stays there without moving automatically to the next line unless I type a key. Even when I append spaces, it still stays there so my images just pile up there. I decided to add a line-break "\n" to move it to the next line manually. Now I have two problems. First, only part of the images at the corner display even though I have a condition like:

if ((cursorPosition.x + 30) >= message.frame.width) {

        message.text = message.text.stringByAppendingString("\n");
    }
    else {
        message.text = message.text.stringByAppendingString("      ");
    }

How do I fix this to make it not go out of the frame?

Secondly, because am adding new line manually, when I delete a text, the cursor moves to some arbitrary position which is quite weird to me. Example, I could be deleting text on line 4 but it will just jump to line 2. Any suggestion on how to solve either or both would be very much appreciated. Below is how the UITextView looks like:

Edited: I am editing this to help anyone who may have a similar problem. I wanted to create an app where users add text and images together like we do in WhatsApp for example. I struggled to do this until the solution below was suggested to me. It works great. Hope it helps someone.

Michael Woyo
  • 138
  • 2
  • 15
  • Can you demonstrate how you are adding the images? – Jeremy Pope Feb 10 '15 at 04:43
  • And when do you set cursorPosition1? Before or after you've added the newline character? – Jeremy Pope Feb 10 '15 at 05:00
  • @Jeremy Pope, I set it after adding the image. For you information it is same as `cursorPosition from above. I just changed. – Michael Woyo Feb 10 '15 at 05:02
  • Have you tried solving your problem using NSTextAttachment? By combining that with NSAttributedString, it seems that you wouldn't have to deal with any room for the image by yourself, it is made to handle that for you. – rocir Feb 10 '15 at 05:14
  • @rocir I am quite new to iOS and Swift. I will read about NSTextAttachment but if you could also elaborate further on its usage, I'll be grateful. How can I use it to solve this particular problem? – Michael Woyo Feb 10 '15 at 05:24
  • You would have to do something like: var textAttachment = NSTextAttachment() textAttachment.image = UIImage(named: "image.png") var attributedString = NSMutableString(attachment: textAttachment) textView.attributedText = attributedString – rocir Feb 10 '15 at 05:27
  • @roci, i get an error on this line 'var attributedString = NSMutableString(attachment: textAttachment);' When I add the argument label, it displays "extra argument" error. When i leave it out, it shows "Cannot invoke init with value of type NSTextAttachment" – Michael Woyo Feb 10 '15 at 05:43
  • Sorry, I mistyped it here (not using Xcode now). It's not NSMutableString. It's NSAttributedString. So it would be: NSAttributedString(attachment: textAttachment) – rocir Feb 10 '15 at 05:46
  • Right, I tried it but realized because am using `UIImage`, am not able to set the size and so the image just displays very big. – Michael Woyo Feb 10 '15 at 06:02
  • Then this is a different problem. It depends on your image (maybe @2x?) You can have an different UIImage with a different size, but you can accomplish that taking a look into this: http://stackoverflow.com/questions/23300349/ios-7-uitextview-size-of-nstextattachment-getting-2x-after-reopening-the-applic – rocir Feb 10 '15 at 06:19
  • Alright, two questions. Is it possible to add more than one image using this approach? Second, can I not use `UIImageView` since it's easier to set size with that? – Michael Woyo Feb 10 '15 at 06:27
  • 1
    Yes, it is possible. You can use a NSMutableAttributedString initially and call the appendAttributedString method every time you need to append more text, or image... The sky is the limit. As for using UIImageView, maybe you could, but that wouldn't make your life easier. What you're trying to accomplish is simple writing text that would go into a text container and UITextView provides all of that for you. But if you want to follow that path, add your UIImageViews and take a look at UITextView exclusion paths. That would avoid your text being drawn in certain areas described by your path. – rocir Feb 10 '15 at 06:36
  • Alright then. Thanks @rocir. I will try what you suggested and post the results here when am done. – Michael Woyo Feb 10 '15 at 06:40
  • @rocir, I'm still having problems with the image size but that is not the bigger issue now. I tried using the `NSMutableAttributedString` but each time I append a `NSAttributedString` object, it replaces the old text and image. What am I doing wrong? – Michael Woyo Feb 10 '15 at 06:49
  • Take a look into how to add an image to an existing text here: http://stackoverflow.com/questions/20930462/ios-7-textkit-how-to-insert-images-inline-with-text – rocir Feb 10 '15 at 07:02
  • @rocir, Sorry for being such a bugger. There is no error when I add multiple images. Just when I insert text into the `UITextView` and try adding another image. It throws this exception: `Terminating app due to uncaught exception 'NSRangeException', reason: 'NSMutableRLEArray insertObject:range:: Out of bounds'` This is what am doing: `let img = UIImage(named: "bmw"); textAttachment.image = img; var attributedString = NSAttributedString(attachment: textAttachment); mutable.replaceCharactersInRange(curpos, withAttributedString: attributedString); message.attributedText = mutable; – Michael Woyo Feb 10 '15 at 07:13
  • I have done what you suggested. @rocir, I have updated my question. Do you have any idea how to solve this last error? – Michael Woyo Feb 11 '15 at 11:34
  • @MichaelWoyo, try to break your problems into smaller pieces and try to solve one at once. You first started asking about inline images, then you brought the image size problem and now the cursor positioning problem. First of all, read the documentation of UITextView, NSAttributedString and NSTextAttachment. If you don't find your answers, browse Google/StackOverFlow for your answers. If you can't find them, make *atomic* questions here and don't stack them up every time you have a question about the feature you have to develop. – rocir Feb 11 '15 at 17:59
  • About your last question, see if this answers your question: http://stackoverflow.com/questions/10135086/how-to-set-cursor-position-for-uitextview-on-user-input – rocir Feb 11 '15 at 17:59
  • how to do override paste to paste the image in the uitextview? – Nat Serrano Sep 16 '22 at 03:03

3 Answers3

10

If I understand your goal, and how you currently have it implemented. You should check if you need to return first.

if ((cursorPosition.x + 30) >= message.frame.width) {
    message.text = message.text.stringByAppendingString("\n");
}

Then you should get the now current cursor position, and add your UIImageView there.

    let size = CGSize(width: 30, height: 30);
    let img = UIImage(named: change_arr[indexPath.row]);
    let addImg = UIImageView(image: UIImage(named: change_arr[indexPath.row]));
    addImg.frame = CGRect(origin: newCursorPosition, size: size);
message.addSubview(addImg);

then if you need to add spaces, add them now.

As stated in the previous comments using NSTextAttachment will simplify all of this, and prevent you from having to create UIViews, etc...

it would look something like this, and you don't need to check for the cursor position, because it will handle that just like it does with text.

//create your UIImage
let image = UIImage(named: change_arr[indexPath.row]);
//create and NSTextAttachment and add your image to it.
let attachment = NSTextAttachment()
attachment.image = image
//put your NSTextAttachment into and attributedString
let attString = NSAttributedString(attachment: attachment)
//add this attributed string to the current position.
textView.textStorage.insertAttributedString(attString, atIndex: textView.selectedRange.location)

Now the image is inline, and treated like text. If the user backspaces over it, it deletes just like text.

slider
  • 2,736
  • 4
  • 33
  • 69
Jeremy Pope
  • 3,342
  • 1
  • 16
  • 17
  • this works fine. Now the second problem still persists. When I delete any text, the cursor jumps to some arbitrary position. Any idea what am doing wrong or how to handle it? – Michael Woyo Feb 10 '15 at 05:26
  • Have you tried this after I updated it. Using an NSTextAttachment resolves the delete issues. – Jeremy Pope Feb 10 '15 at 15:10
  • I fixed the problem. It works great now. Thanks. When you `insertAttributedString(attString, atIndex)`, the cursor stays behind the newly inserted object. You have to manually move it to after the object using: `textView.selectedRange.location += 1;` – Michael Woyo Feb 12 '15 at 04:10
0
__block  NSTextAttachment *imageAttachment = [NSTextAttachment new];
        imageAttachment.bounds = CGRectMake(0, -5, 20, 20);
        NSAttributedString *stringWithImage = [NSAttributedString attributedStringWithAttachment:imageAttachment];
        [deCodedString replaceCharactersInRange:NSMakeRange(deCodedString.length, 0) withAttributedString:stringWithImage];
        incomingMessage.messageAttributedString = deCodedString;

    SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
    imageAttachment.image = [UIImage imageNamed:@"profile_main_placeholder"];

    [downloader downloadImageWithURL:aURL
                             options:0
                            progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                                // progression tracking code
                            }
                           completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
                               if (image && finished) {
                                   [image drawInRect:CGRectMake(0, 0, 20, 20)];
                                   imageAttachment.image = image;

                                   dispatch_async(dispatch_get_main_queue(), ^(void)
                                                  {

                                                      [self.tbl_Conversation reloadRowsAtIndexPaths:[self.tbl_Conversation indexPathsForVisibleRows]
                                                                                   withRowAnimation:UITableViewRowAnimationNone];
                                                      [self.tbl_Conversation reloadData];
                                                  });



                                   //                                                              NSAttributedString *stringWithImage = [NSAttributedString attributedStringWithAttachment:imageAttachment];
                                   //                                                              [deCodedString replaceCharactersInRange:NSMakeRange(deCodedString.length, 0) withAttributedString:stringWithImage];
                                   //                                                              incomingMessage.messageAttributedString = deCodedString;
                               }
                           }];
Jamie Eltringham
  • 810
  • 3
  • 16
  • 25
Matloob Hasnain
  • 1,025
  • 6
  • 21
0

Actually, I solved this problem using the NSAttributedString class. It allows programmers to combine images in the text buffer and be treated just like a single character in the buffer which means hitting the space bar with the cursor before that image will move the image and all other text one space to the right. Likewise hitting the delete key before the image deletes it from the buffer. Hope it helps someone

Michael Woyo
  • 138
  • 2
  • 15