UIMenuController method setTargetRect:inView: not working in UITableView - ios

I am displaying custom UIMenuController in my tableview like this
but issue is that it is displaying in the center I want to display it on top of label which is orange colored. For displaying on top of label I did this [menu setTargetRect:CGRectMake(10, 10, 0, 0) inView:self.lbl]; below is the entire code.
But if I am displaying the UIMenuController without UITableView setTargetRect works fine.
Why setTargetRect is not working with UITableView?
setTargetRect Doc says:
(a) This target rectangle (targetRect) is usually the bounding rectangle
of a selection. UIMenuController positions the editing menu above this
rectangle; if there is not enough space for the menu there, it
positions it below the rectangle. The menu’s pointer is placed at the
center of the top or bottom of the target rectangle as appropriate.
(b) Note that if you make the width or height of the target rectangle
zero, UIMenuController treats the target area as a line or point for
positioning (for example, an insertion caret or a single point).
(c) Once it is set, the target rectangle does not track the view; if the
view moves (such as would happen in a scroll view), you must update
the target rectangle accordingly.
What I am missing?
MyViewController
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 5;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TheCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cid" forIndexPath:indexPath];
cell.lbl.text = [NSString stringWithFormat:#"%ld", (long)indexPath.row];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
-(BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
return YES;
}
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
// required
}
MyCustomCell
- (void)awakeFromNib {
// Initialization code
UIMenuItem *testMenuItem = [[UIMenuItem alloc] initWithTitle:#"Test" action:#selector(test:)];
UIMenuController *menu = [UIMenuController sharedMenuController];
[menu setMenuItems: #[testMenuItem]];
[menu setTargetRect:CGRectMake(10, 10, 0, 0) inView:self.lbl];
[menu update];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
-(BOOL) canPerformAction:(SEL)action withSender:(id)sender {
return (action == #selector(copy:) || action == #selector(test:));
}
/// this methods will be called for the cell menu items
-(void) test: (id) sender {
NSLog(#"test");
}
-(void) copy:(id)sender {
UIMenuController *m = sender;
NSLog(#"copy");
}

1) First, please do this in your view controller's viewDidLoad method.
UIMenuItem *testMenuItem = [[UIMenuItem alloc] initWithTitle:#"Test"
action:#selector(test:)];
[[UIMenuController sharedMenuController] setMenuItems: #[testMenuItem]];
[[UIMenuController sharedMenuController] update];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(menuControllerWillShow:) name:UIMenuControllerWillShowMenuNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(menuControllerWillHide:) name:UIMenuControllerWillHideMenuNotification object:nil];
2) Then, add these two Methods. and set menuFrame inView to your cell
-(void)menuControllerWillShow:(NSNotification *)notification{
//Remove Will Show Notification to prevent
// "menuControllerWillShow" function to be called multiple times
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIMenuControllerWillShowMenuNotification object:nil];
//Hide the Original Menu View
UIMenuController* menuController = [UIMenuController sharedMenuController];
CGSize size = menuController.menuFrame.size;
CGRect menuFrame;
menuFrame.origin.x = self.tableView.frame.origin.x;
menuFrame.size = size;
[menuController setMenuVisible:NO animated:NO];
//Modify its target rect and show it again to prevent glitchy appearance
[menuController setTargetRect:menuFrame inView:cell];
[menuController setMenuVisible:YES animated:YES];
}
-(void)menuControllerWillHide:(NSNotification *)notification{
//re-register menuControllerWillShow
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(menuControllerWillShow:)
name:UIMenuControllerWillShowMenuNotification object:nil];
}
3) Implement tableView Delegates and implement these methods.
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath {
cell = (TableViewCell*)[tableView cellForRowAtIndexPath:indexPath];
return YES;
}
-(BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
return NO;
}
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
// required
}
4) Setup the cells (subclassing UITableViewCell) with
-(BOOL) canPerformAction:(SEL)action withSender:(id)sender {
return (action == #selector(copy:) || action == #selector(test:)); } /// this methods will be called for the cell menu items
-(void) test: (id) sender {
NSLog(#"test"); }
-(void) copy:(id)sender {
NSLog(#"copy"); }

Related

Displaying menu on longpress on UIView in UITableViewCell

In an IOS app, I want to display a UIMenuController when longpressing an UIView located in UITableViewCell.
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
myTableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell==nil)
{
cell=(myTableViewCell*)[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
myView=<initMyView>
[cell.contentView addSubview:myView];
UILongPressGestureRecognizer *longTap = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longTapGestureCapturedOnView:)];
longTap.minimumPressDuration=0.5f;
[myView addGestureRecognizer:longTap];
return cell;
}
- (void)longTapGestureCapturedOnView:(UITapGestureRecognizer *)gesture
{
items=[[NSMutableArray alloc]init];
if (gesture.state == UIGestureRecognizerStateBegan)
{
[items addObject:[[UIMenuItem alloc] initWithTitle:#"Copy" action:#selector(myCopy:)]];
[items addObject:[[UIMenuItem alloc] initWithTitle:#"Custom" action:#selector(myCustom:)]];
}
[[UIMenuController sharedMenuController] setMenuItems:items];
[[UIMenuController sharedMenuController] update];
[[UIMenuController sharedMenuController] setMenuVisible:YES animated:YES];
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
BOOL result = NO;
if(#selector(myCopy:) == action ||#selector(myCustom:) == action ) {
result = YES;
}
return result;
}
The longTapGestureCapturedOnView is called, as well as the canPerformAction which is returning YES in the 2 cases, but no menuitem appears on the screen. What could I have done wrong ?
You also need
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath {
return YES ;
}
//
also it's not right to add gestures in cellForRowAt as it's being called upon scroll & cellReusing so add it in awakeFormNib method of UITableViewCell custom class

ios how to show modal view on clicking uimenuitem

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.

iOS app - delete buttons not showing when table view is in editing mode

I am very new to iOS but I've created a view controller with a table view which i want to put into editing mode. This is my view controller implementation file code:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize progsTable, programmes;
- (void)viewDidLoad
{
[super viewDidLoad];
programmes = [[NSMutableArray alloc] initWithObjects:#"one",#"two",#"three",nil];
//set the title
self.title = #"Programmes";
//add an edit button
self.navigationItem.leftBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
[progsTable setEditing:editing animated:animated];
}
#pragma mark - UITableView Datasource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return programmes.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell==nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [programmes objectAtIndex: indexPath.row];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *) indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle==UITableViewCellEditingStyleDelete) {
//remove from array
[programmes removeObjectAtIndex:indexPath.row];
//remove from table
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
#pragma mark - UITableView Delegate methods
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
#end
I get the edit button in the top left but when i click it the red delete buttons don't appear - what am I doing wrong?
Running your exact code does work - the delete buttons are displaying when i test it.
I believe the problem is that you haven't assigned your #property called progsTable to your Table View.
To solve it do the following:
Go to your Storyboard
Right click on your View Controller
Click and hold the + next to the outlet called progsTable while you drag the mouse to your Table View, like this:
Try running your app - the delete buttons should now appear.
I think you need to add the code below:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleDelete; }
This tells the UITableView which editing style you want.
Use this
- (IBAction)deleteDrug:(id)sender event:(id)event {
selectedButtonIndex = [self indexPathForButton:sender event:event];
[tableView setEditing:YES animated:YES];
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath == selectedButtonIndex) {
return YES;
}
return NO;
}
You may need to implement this method for your datasource.
- (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
Check that you also have auto-layout constraints added in interface builder so that the very end of the cell lines up with the width of your device.
Run the code on the ipad in the simulator, if the delete button shows up there but not on the iphone simulator then it probably just that the contents are not aligned properly to the viewport of your device.
My 'delete' button was not showing up but the cell title content were moving over to the left when I swiped, however I had not set constraints on my cell width in my storyboard, as soon as I set the right-edge constraint to '0' or '16', the delete button appeared.

Showing UIMenuController deselects UITableViewCell

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];
...
}
}

ios UIMenuController doesn't work with balloon view

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?

Resources