2

I have a UITextView inside of UITableViewCell. That UITextView I want be selectable but not text to. When the Menu Controller is appearing and i want to perform copy action, copy all text inside not just a part.

I want something like messenger app :

enter image description here

For the moment i have that :

enter image description here

Thank you in advance!

Tejas Ardeshna
  • 4,343
  • 2
  • 20
  • 39
Gaby Fitcal
  • 1,814
  • 1
  • 17
  • 28
  • It seems that you're looking for `tableView:shouldShowMenuForRowAtIndexPath:` method of `UITableViewDelegate` – Larme Mar 08 '16 at 10:45
  • it's depend on your implement , you can disable the interaction of your text view and add your custom UIMenu action on it and call them by gesture – Mo Farhand Mar 08 '16 at 10:49
  • @Larme i think will be no pussible because i have two Text Views in cell, i forget to say that. – Gaby Fitcal Mar 08 '16 at 10:51

4 Answers4

1

In fact it's not quite so easy, as on the road show up some strange stuff, but I managed to create a customized one.

The steps are as following:

  1. Create a subclass of UITextView
  2. Override canPerformAction: withSender: for filtering the default actions of the menu that pops up.
  3. Configuring textView in order not to be able to edit or select.
  4. Editing UIMenuController items which provide the actions and buttons on the menu
  5. Add different selectors for each UIMenuItem **** (That is because the sender of the selector is not an UIMenuItem, but a UIMenuController which leads to another matter. Check here for more info. A gist by me for that)

Code

No more talking so here is the code:

EBCustomTextView.h

//
//  EBCustomTextView.h
//  TestProject
//
//  Created by Erid Bardhaj on 3/8/16.
//  Copyright © 2016 Erid Bardhaj. All rights reserved.
//

#import <UIKit/UIKit.h>

typedef NS_ENUM(NSInteger, EBCustomTextViewMenuAction) {
    EBCustomTextViewMenuActionCopy,
    EBCustomTextViewMenuActionDefine,
    EBCustomTextViewMenuActionRead
};

@class EBCustomTextView;
@protocol EBCustomTextViewMenuActionDelegate <NSObject>

- (void)customTextView:(EBCustomTextView *)textView didSelectMenuAction:(EBCustomTextViewMenuAction)action;

@end

@interface EBCustomTextView : UITextView

@property (weak, nonatomic) id<EBCustomTextViewMenuActionDelegate> menuActionDelegate;

@end

EBCustomTextView.m

//
//  EBCustomTextView.m
//  TestProject
//
//  Created by Erid Bardhaj on 3/8/16.
//  Copyright © 2016 Erid Bardhaj. All rights reserved.
//

#import "EBCustomTextView.h"

@implementation EBCustomTextView {
    EBCustomTextViewMenuAction currentAction;
}

- (void)awakeFromNib {
    [super awakeFromNib];

    // Configure the textView
    self.editable = NO;
    self.selectable = NO;
}

#pragma mark - Datasource

- (NSArray *)items {
    UIMenuItem *item = [[UIMenuItem alloc] initWithTitle:@"Copy" action:@selector(copyMenuItemPressed)];
    UIMenuItem *item1 = [[UIMenuItem alloc] initWithTitle:@"Define" action:@selector(defineMenuItemPressed)];
    UIMenuItem *item2 = [[UIMenuItem alloc] initWithTitle:@"Read" action:@selector(readMenuItemPressed)];

    return @[item, item1, item2];
}


#pragma mark - Actions

- (void)copyMenuItemPressed {
    if ([self.menuActionDelegate respondsToSelector:@selector(customTextView:didSelectMenuAction:)]) {
        [self.menuActionDelegate customTextView:self didSelectMenuAction:EBCustomTextViewMenuActionCopy];
    }
}

- (void)defineMenuItemPressed {
    if ([self.menuActionDelegate respondsToSelector:@selector(customTextView:didSelectMenuAction:)]) {
        [self.menuActionDelegate customTextView:self didSelectMenuAction:EBCustomTextViewMenuActionDefine];
    }
}

- (void)readMenuItemPressed {
    if ([self.menuActionDelegate respondsToSelector:@selector(customTextView:didSelectMenuAction:)]) {
        [self.menuActionDelegate customTextView:self didSelectMenuAction:EBCustomTextViewMenuActionRead];
    }
}

#pragma mark - Private

- (void)menuItemPressedAtIndex:(NSInteger)index {
    currentAction = index;

    if ([self.menuActionDelegate respondsToSelector:@selector(customTextView:didSelectMenuAction:)]) {
        [self.menuActionDelegate customTextView:self didSelectMenuAction:currentAction];
    }
}

#pragma mark Helpers

- (void)showMenuController {
    UIMenuController *theMenu = [UIMenuController sharedMenuController];
    theMenu.menuItems = [self items];
    [theMenu update];

    CGRect selectionRect = CGRectMake (0, 0, self.contentSize.width, self.contentSize.height);
    [theMenu setTargetRect:selectionRect inView:self];
    [theMenu setMenuVisible:(theMenu.isMenuVisible) ? NO : YES animated:YES];
}

#pragma mark - Overridings

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    // Filter any action for this textView except our custom ones
    if (action == @selector(copyMenuItemPressed) || action == @selector(defineMenuItemPressed) || action == @selector(readMenuItemPressed)) {
        return YES;
    }
    return NO;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *theTouch = [touches anyObject];

    if ([theTouch tapCount] == 1  && [self becomeFirstResponder]) {
        [self showMenuController];
    }
}


@end

Implementation

Set your textView's class to EBCustomTextView and conform to EBCustomTextViewMenuActionDelegate

Interface

@interface ViewController () <EBCustomTextViewMenuActionDelegate>

viewDidLoad

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.textView.menuActionDelegate = self;
}

Protocol conformance

#pragma mark - Delegation

#pragma mark EBCustomTextViewMenuActionDelegate 

- (void)customTextView:(EBCustomTextView *)textView didSelectMenuAction:(EBCustomTextViewMenuAction)action {
    switch (action) {
        case EBCustomTextViewMenuActionCopy:
            NSLog(@"Copy Action");
            break;

        case EBCustomTextViewMenuActionRead:
            NSLog(@"Read Action");
            break;

        case EBCustomTextViewMenuActionDefine:
            NSLog(@"Define Action");
            break;

        default:
            break;
    }
}

Output

enter image description here

Enjoy :)

Community
  • 1
  • 1
E-Riddie
  • 14,660
  • 7
  • 52
  • 74
0

you have to use

[UITextView selectAll:self];

or (with specific range)

UITextView.selectedRange = NSMakeRange(0, 5);

or add txtview.delegate = self;

- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
    dispatch_async(dispatch_get_main_queue(), ^{
        [textView selectAll:nil];  //nil or self as per needed
    });
    return YES;
}
Vvk
  • 4,031
  • 29
  • 51
0

Use UILongPressGestureRecognizer Gesture

#import "ViewController.h"

@interface ViewController ()<UIGestureRecognizerDelegate>
{
   UITextView * txtV;
   UILongPressGestureRecognizer * longPressGestureRecognizer;
}
@end

@implementation ViewController

- (void)viewDidLoad
{
   [super viewDidLoad];

   txtV=[[UITextView alloc]initWithFrame:CGRectMake(20, 50, 280, 300)];
   txtV.text = @"Jai Maharashtra";
   txtV.userInteractionEnabled=YES;
   txtV.selectable=NO;
   txtV.editable=NO;
   txtV.font= [UIFont italicSystemFontOfSize:[UIFont systemFontSize]];
   [self.view addSubview:txtV];

   longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGesture:)];
   longPressGestureRecognizer.delegate=self;
   [txtV addGestureRecognizer:longPressGestureRecognizer];
}

- (void)longPressGesture:(UILongPressGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
    {
       txtV.selectable=YES;
       [txtV selectAll:self];
    }
}

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    [txtV selectAll:self];

    if (action == @selector(cut:))
    {
       return YES;
    }
    if (action == @selector(selectAll:))
    {
      return YES;
    }
    if (action == @selector(copy:))
    {
      return YES;
    }
    if (action == @selector(delete:))
    {
      return YES;
    }

    txtV.selectable=NO;
    return YES;
 }
Shrikant Tanwade
  • 1,391
  • 12
  • 21
0

First i start with create for every UITextView in cell a UILongPressGestureRecognizer to recognize long press.

In cellForRowAtIndexPath i just add that code :

 UILongPressGestureRecognizer *recognizer1 = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(handleLongPressText1:)];
 messageRecognizer.delaysTouchesBegan = YES;

 UILongPressGestureRecognizer *recognizer2 = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(handleLongPressText2:)];
 translateRecognizer.delaysTouchesBegan = YES;


 [cell.backgroundViewText1 addGestureRecognizer:recognizer1];
 [cell.backgroundViewText2 addGestureRecognizer:recognizer2];

I use two different selectors because i need to know which text to get from cell.

After the methods :

    -(void)handleLongPressText1:(UILongPressGestureRecognizer *)gestureRecognizer
{
    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
        CGPoint p = [gestureRecognizer locationInView:self.tableView];
        NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
        if (indexPath == nil){
            NSLog(@"couldn't find index path");
        } else {
            [self becomeFirstResponder];

            UIMenuController *menuController = [UIMenuController sharedMenuController];
            UIMenuItem *resetMenuItem = [[UIMenuItem alloc] initWithTitle:@"Copy" action:@selector(copyMethod)];
            UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Read" action:@selector(readButtonAction)];

            [menuController setMenuItems:[NSArray arrayWithObjects:resetMenuItem,menuItem, nil]];
            CGRect rect =gestureRecognizer.view.frame;
            NSLog(@"%@", NSStringFromCGRect(rect));
            [menuController setTargetRect:gestureRecognizer.view.frame inView:[[gestureRecognizer view] superview]];
            [menuController setMenuVisible:YES animated:YES];

        }
    }
}

    -(void)handleLongPressText2:(UILongPressGestureRecognizer *)gestureRecognizer
{
    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
        CGPoint p = [gestureRecognizer locationInView:self.tableView];
        NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
        if (indexPath == nil){
            NSLog(@"couldn't find index path");
        } else {
            [self becomeFirstResponder];

            UIMenuController *menuController = [UIMenuController sharedMenuController];
            UIMenuItem *resetMenuItem = [[UIMenuItem alloc] initWithTitle:@"Copy" action:@selector(copyMethod)];
            UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Read" action:@selector(readButtonAction)];

            [menuController setMenuItems:[NSArray arrayWithObjects:resetMenuItem,menuItem, nil]];
            CGRect rect =gestureRecognizer.view.frame;
            NSLog(@"%@", NSStringFromCGRect(rect));
            [menuController setTargetRect:gestureRecognizer.view.frame inView:[[gestureRecognizer view] superview]];
            [menuController setMenuVisible:YES animated:YES];
        }
    }
}

And for disable all default items in UIMenuController use above code

- (BOOL)canPerformAction:(SEL)iAction withSender:(id)iSender {
SEL copySelector = NSSelectorFromString(@"copyMethod");
SEL readSeletor = NSSelectorFromString(@"readButtonAction");

if (iAction == copySelector) {
    return YES;
}else if (iAction ==readSeletor){
    return YES;
}else{
    return NO;
}


return NO;
}
Gaby Fitcal
  • 1,814
  • 1
  • 17
  • 28