I'm trying to fix an issue for two days but I really don't know what to do. I tried several solutions but nothing helped.
This is the situation: I have a UIViewController and I've added a subclass of UITextView as subview.
-(void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
_textView = [[DPTextView alloc] initWithFrame:self.view.bounds];
[_textView setText:[NSString stringWithContentsOfFile:_path encoding:NSUTF8StringEncoding error:nil]];
_textView.editable = NO;
_textView.selectable = YES;
_textView.delegate = self;
_textView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self.view addSubview:_textView];
//[self.view becomeFirstResponder];
//[self becomeFirstResponder];
//[_textView becomeFirstResponder];
}
When I open this UIViewController, I see the text. If I select the text, I don't see the UIMenuController. I've already overriden some methods of the subclass of UITextView (DPTextView), like the following:
-(BOOL)canBecomeFirstResponder {
return TRUE;
}
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (action == #selector(copy:))
return YES;
return NO;
}
But it doesn't appear. After some time I've noticed that the UIMenuController appears if I follow some steps:
This controller is presented from another controller. Let's call them Controller1 (that has UISearchController and UITableView) and Controller2 (with DPTextView).
I tap the search bar into Controller1.
I dismiss the keyboard tapping "Cancel", next to the search bar.
I tap UITableViewCell that presents Controller2.
Following those steps, the UIMenuController appears when I select text into Controller2! I don't know really why! I thought something happened when pressing the search bar, like the controller becomes the first responder or something similar (and I've already tried to call "becomeFirstResponder", as you can read in the code above -- the commented lines of code).
Note that if I tap and hold UITableViewCell in Controller1, I should get UIMenuController too, because I've implemented those methods:
-(BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath {
return TRUE;
}
-(BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
return (action == #selector(copy:));
}
-(void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
if (action == #selector(copy:)) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[[UIPasteboard generalPasteboard] setString:cell.textLabel.text];
}
}
But it doesn't appear here too. It appears if after using the search controller, so the same situation.
Any suggestion? I don't know if this can help but Controller1 and Controller2 are presented modally.
Related
I am developing an app where on click of table cell i am displaying UIMenuItem with 'Info' button. Now on clicking the info button i have to show a view and dismiss on click of cancel.
In MytableView(Custom tableView)
-(void)tableView : (UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"didSelectRow");
UIMenuItem *testMenuItem = [[UIMenuItem alloc] initWithTitle:#"Info" action:#selector(handleInfo:)];
[[UIMenuController sharedMenuController] setMenuItems: #[testMenuItem]];
[[UIMenuController sharedMenuController] update];
}
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
-(BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
// if (action == #selector(handleInfo:))
// {
// return YES;
// }
return NO;
}
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
// required
}
-(IBAction)handleInfo:(id)sender{
InfoViewController *readInfo = [[InfoViewController alloc] initWithNibName:#"InfoViewController" bundle:nil];
readInfo.label.text = #"Info of table cell";
[myAppDelegate.navController pushViewController:readInfo animated:NO];
}
I am able to push the view but unable to dismiss it and value passed to readInfo.label.text is returning null.
1.
readInfo.label.text is returning null. Because at this time the label in xib still not rendered.
to solve this, can assign the text after pushViewController.
but i would prefer to pass the text in the initWithNib method.
may be create another method initWithNibName:#"xxx" text:#"Info of table cell";
store it in InfoViewController, and assign this text to it in viewDidLoad.
2.
what method did you call when dismiss that InfoViewController??
[myAppDelegate.navController popViewController]
this should do the trick.
I have created a custom cell with a textField that initially is hidden.
When I choose the cell, the textField appears and I have to press on textField to write.
This I want is when I choose the cell to write immediately in textField. This is my code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if ( indexPath.row == 0 ){
commentTextField.text = [NSString stringWithFormat:commentDetailTextLabel.text];
commentTextField.textColor = [UIColor yellowColor];
commentTextField.hidden = NO;
commentDetailTextLabel.hidden = YES;
}
}
Add [commentTextField becomeFirstResponder]; in your if body.
I'm using the SASlideMenu library to implement a left slide menu in my iOS ARC storyboard app. I have added three View Controllers called with the [self performSegueWithIdentifier:#"segueID" sender:self]; code, and the slide menu works fine, but every time it create a NEW INSTANCE of a View Controller while I need to cache each controller content (example, in View Controller 1 we have a text field: If I write a word, after changing VC I must find again same word!). This is generic Storyboard:
This is my UITableView delegate, in SASlideMenuViewController:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:
UITableViewCellStyleSubtitle reuseIdentifier:#"Cell"];
}
if (indexPath.row == 0) {
cell.textLabel.text = #"ViewController 1";
}
if (indexPath.row == 1) {
cell.textLabel.text = #"ViewController 2";
}
if (indexPath.row == 2) {
cell.textLabel.text = #"ViewController 3";
}
cell.textLabel.font = [UIFont systemFontOfSize:14];
cell.textLabel.textColor = [UIColor blackColor];
cell.imageView.image = [UIImage imageNamed:#"image.png"];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath.row==0)
{
[self performSegueWithIdentifier:#"segueID1" sender:self]; // I NEED TO RETRIEVE SAME VIEW CONTROLLER 1 CONTENT AFTER ANOTHER VIEW CONTROLLER CALL!
}
if(indexPath.row==1)
{
//[self performSegueWithIdentifier:#"segueID2" sender:nil]; // I NEED TO RETRIEVE SAME VIEW CONTROLLER 2 CONTENT AFTER ANOTHER VIEW CONTROLLER CALL!
}
if(indexPath.row==2)
{
//[self performSegueWithIdentifier:#"segueID3" sender:nil]; // I NEED TO RETRIEVE SAME VIEW CONTROLLER 3 CONTENT AFTER ANOTHER VIEW CONTROLLER CALL!
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
And this is a screenshot of slide menu in app:
In the author tutorial I can read: "If you are using dynamic cell prototype or you are using static cells and you want to cache the content view controller, assign an identifier that will be returned in the segueIdForIndexPath: method linked to the desired indexPath" but I'm losing my head and I cannot get the precise point.. please can u provide the precise code to resolve this? There is something else to add, but where? I need to reproduce this situation: EVERYTIME I call a View Controller from the left menu I MUST retrieve it identical as I have leaved before another previous VC call. Thanks!
In order to have your code working you need to implement two methods of the SASlideMenuDataSource protocol and you have to initialize the datasource property of your ViewController. Moreover you have to avoid to directly invoke performSegueWithIdentifier and you should avoid to implement tableView:didSelectRowAtIndexPath: method, because otherwise caching will not work.
The first method to implement is configureMenuButton:. You need it because you need the menu button in the content ViewController you are sliding in. One possibility is to copy the icons provided in the example project and add them to your project:
-(void) configureMenuButton:(UIButton *)menuButton{
menuButton.frame = CGRectMake(0, 0, 40, 29);
[menuButton setImage:[UIImage imageNamed:#"menuicon.png"] forState:UIControlStateNormal];
[menuButton setBackgroundImage:[UIImage imageNamed:#"menu.png"] forState:UIControlStateNormal];
[menuButton setBackgroundImage:[UIImage imageNamed:#"menuhighlighted.png"] forState:UIControlStateHighlighted];
[menuButton setAdjustsImageWhenHighlighted:NO];
[menuButton setAdjustsImageWhenDisabled:NO];
}
Than you need to implement segueIdForIndexPath:. The method returns the segueId associated to the indexPath of the row of the menu, looking at your code should be something like:
-(NSString*) segueIdForIndexPath:(NSIndexPath *)indexPath{
if(indexPath.row==0){
return #"vista1";
} else if (indexPath.row==1){
return #"vista2";
}else if(indexPath.row==2){
return #"vista3";
}
return #"vista1";
}
Finally you have to correctly initialize the dataSource property of your ViewController:
-(id) initWithCoder:(NSCoder *)aDecoder{
if (self = [super initWithCoder:aDecoder]) {
self.slideMenuDataSource = self;
}
return self;
}
I hope you will find the answer useful.
I've implemented a long press gesture recognizer on a table view cell that shows the UIMenuController. But when menu shows, the corresponding table view cell deselects. Before showing the menu, I call, as required, [self becomeFirstResponder]. I think that this call deselects the cell, but how to make it to stay selected while the UIMenuController is visible?
In your UITableViewDelegate, override tableView:willDeselectRowAtIndexPath: and return nil when you don’t want your row to deselect, according to the documentation.
A simpler way of implementing this is using the specific UITableViewDelegate methods for dealing with UIMenuController.
But first, to make the cell stay selected, store the value of the cell presenting the menu in your class:
NSIndexPath *_editingIndexPath;
Then implement the UITableViewDelegateMethods:
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomTableViewCell *cell = (MyCustomTableViewCell *) [_tableView cellForRowAtIndexPath:indexPath];
_editingIndexPath = indexPath;
cell.showingMenu = YES;
return YES;
}
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
if (action == #selector(copy:)) {
return YES;
}
return NO;
}
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
if (action == #selector(copy:))
{
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if (cell && [cell isKindOfClass:[MessageConversationCell class]])
{
[UIPasteboard generalPasteboard].string = cell.textLabel.text;
}
}
}
The code above will take care of showing a "copy" menu on the cell after a long press.
Now, if you want the cell to stay selected while the menu is displayed:
Add a #property in your custom cell named "showingMenu" (note that this property was already set in the first block of code in this answer).
#property (nonatomic, assign) BOOL showingMenu;
Add (or modified if already present) the following method to your custom cell. This will take care of keeping the cell highlighted after the menu tried to unhighlight it (you may implement your own logic for highlighting a cell, in that case put it in the first branch of the if conditional):
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
if (_showingMenu)
{
[super setHighlighted:YES]
}
else
{
[super setHighlighted:highlighted];
}
}
Add an observer to be notified when the menu is going to be presented. This goes into the view controller, NOT in the custom cell:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didShowEditMenu:) name:UIMenuControllerDidShowMenuNotification object:nil];
Add on the view controller the method to be called when the menu is displayed:
- (void)didShowEditMenu:(NSNotification *)not {
[_tableView selectRowAtIndexPath:_editingIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
MyCustomTableViewCell *cell = (MyCustomTableViewCell*)[_conversationTableView cellForRowAtIndexPath:_editingIndexPath];
cell.showingMenu = NO;
}
And don't forget to remove the observer when no longer needed:
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerDidShowMenuNotification object:nil];
This will show a menu when a cell is long pressed, and keep the cell selected until the menu disappears, either because an option was chosen or because the user tapped somewhere else. It works pretty much like Whatsapp works when you select a message.
You say you are calling [self becomeFirstResponder] but your question does not indicate which object self is. I would guess self is your controller. You should be sending the becomeFirstResponder message to the UITableViewCell that is spawning the UIMenuController.
- (void)longPress:(UILongPressGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan)
{
TSTableViewCell *cell = (TSTableViewCell *)recognizer.view;
//This is your problem
[cell becomeFirstResponder];
UIMenuItem *flag = [[UIMenuItem alloc] initWithTitle:#"Flag" action:#selector(flag:)];
UIMenuItem *approve = [[UIMenuItem alloc] initWithTitle:#"Approve" action:#selector(approve:)];
UIMenuItem *deny = [[UIMenuItem alloc] initWithTitle:#"Deny" action:#selector(deny:)];
UIMenuController *menu = [UIMenuController sharedMenuController];
[menu setMenuItems:[NSArray arrayWithObjects:flag, approve, deny, nil]];
[menu setTargetRect:cell.frame inView:cell.superview];
[menu setMenuVisible:YES animated:YES];
}
}
I have found this solution:
- (void)handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer
{
if (longPressRecognizer.state == UIGestureRecognizerStateBegan) {
UITableViewCell *cell = (UITableViewCell *)longPressRecognizer.view;
[cell setSelected:YES];
...
}
}
I have a ballon view and when I tap on cell UIMenuController doesn't appear. I tried to implement in
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath,
then I added a long press gesture
(void)longPress:(UILongPressGestureRecognizer *)recognizer,
in the end I added an implementation in extended
UITableViewCell
class, but it still doesn't work.
Here is an example
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (BOOL) canPerformAction:(SEL)selector withSender:(id) sender
{
return YES;
}
-(void)copyAction:(id)sender{
NSLog(#"copy");
}
-(void)deleteAction:(id)sender{
NSLog(#"delete");
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
BalloonCell *cell = (BalloonCell*)[tableView cellForRowAtIndexPath:indexPath];
[self becomeFirstResponder];
BOOL isCanBecomde = [self canBecomeFirstResponder];
BOOL isFirst = [self isFirstResponder];
UIMenuItem *copy = [[UIMenuItem alloc] initWithTitle:#"Копировать" action:#selector(copyAction:)];
UIMenuItem *delete = [[UIMenuItem alloc] initWithTitle:#"Удалить" action:#selector(deleteAction:)];
UIMenuController *menu = [UIMenuController sharedMenuController];
CGRect drawRect = [cell convertRect:cell.bounds toView:self.view];
[menu setTargetRect:drawRect inView:window];
[menu setMenuItems:[NSArray arrayWithObjects:copy, delete, nil]];
[menu setMenuVisible:YES animated:YES];
}
Do you have any guess why UIMenuController doesn't appear?