【问题标题】:UIMenuController with custom item not working with UICollectionview带有自定义项目的 UIMenuController 不适用于 UICollectionview
【发布时间】:2013-06-06 03:41:22
【问题描述】:

我在 UICollectionViewCell 上长按时添加了自定义菜单控制器

    [self becomeFirstResponder];
    UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Custom Action"
                                                      action:@selector(customAction:)];
    [[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObject:menuItem]];
    [[UIMenuController sharedMenuController] setTargetRect: self.frame inView:self.superview];
    [[UIMenuController sharedMenuController] setMenuVisible:YES animated: YES];

canBecomeFirstResponder 也被调用

- (BOOL)canBecomeFirstResponder {
    // NOTE: This menu item will not show if this is not YES!
    return YES;
}

//这个方法没有被调用

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    NSLog(@"canPerformAction");
    // The selector(s) should match your UIMenuItem selector
    if (action == @selector(customAction:)) {
        return YES;
    }
    return NO;
}

我也实现了这些方法

- (BOOL)collectionView:(UICollectionView *)collectionView
      canPerformAction:(SEL)action
    forItemAtIndexPath:(NSIndexPath *)indexPath
            withSender:(id)sender {


    if([NSStringFromSelector(action) isEqualToString:@"customAction:"]){
        NSLog(@"indexpath : %@",indexPath);
        UIAlertView *alertview = [[UIAlertView alloc] initWithTitle:@"warning.." message:@"Do you really want to delete this photo?" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
        [alertview show];
        return YES;
    }

    return YES;

}

- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath {
    return YES;
}

- (void)collectionView:(UICollectionView *)collectionView
         performAction:(SEL)action
    forItemAtIndexPath:(NSIndexPath *)indexPath
            withSender:(id)sender {
    NSLog(@"performAction");
}

虽然它只显示“剪切、复制和粘贴”菜单

【问题讨论】:

    标签: objective-c uicollectionview uimenucontroller uiresponder


    【解决方案1】:

    可能有点晚了,但我可能为那些仍在寻找这个的人找到了更好的解决方案:

    在 UICollectionViewController 的 viewDidLoad 中添加您的项目:

    UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Title" action:@selector(action:)];
    [[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObject:menuItem]];
    

    添加以下委托方法:

    //This method is called instead of canPerformAction for each action (copy, cut and paste too)
    - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
            if (action == @selector(action:)) {
                return YES;
            }
            return NO;
        }
        //Yes for showing menu in general
        - (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath {
            return YES;
        }
    

    子类 UICollectionViewCell 如果你还没有。添加您为项目指定的方法:

    - (void)action:(UIMenuController*)menuController {
    
    }
    

    这样你就不需要任何 becomeFirstResponder 或其他方法。您可以将所有操作集中在一个地方,如果您以单元格本身作为参数调用通用方法,则可以轻松处理不同的单元格。

    编辑:uicollectionview 不知何故需要此方法的存在(您的自定义操作不会调用此方法,我认为 uicollectionview 只是检查是否存在)

    - (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
    
    }
    

    【讨论】:

    • 不适用于 ios7 吗?有任何想法吗。被困在这几天了。
    • 对我来说,它在 ios 7 中的工作方式与此完全相同。是否调用了委托方法?
    • 您是否在为 collectionViewCell 使用自定义类?
    • 是的,正如我在回答中提到的那样。
    • 对不起,我不知道为什么它不起作用。我在应用商店中有一个这样的工作应用。
    【解决方案2】:

    您需要从自定义 UICollectionViewCell 触发委托函数

    这是我的 Swift3 工作示例代码

    CollectionViewController

    override func viewDidLoad() {
        super.viewDidLoad()
        let editMenuItem = UIMenuItem(title: "Edit", action: NSSelectorFromString("editCollection"))
        let deleteMenuItem = UIMenuItem(title: "Delete", action: NSSelectorFromString("deleteCollection"))
        UIMenuController.shared.menuItems = [editMenuItem, deleteMenuItem]
    
    }
    
    override func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool {
        return true
    }
    
    override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
        return action == NSSelectorFromString("editCollection") || action == NSSelectorFromString("deleteCollection")
    }
    
    override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {
        print("action:\(action.description)")
        //Custom actions here..
    }
    

    将以下函数添加到您的自定义 UICollectionViewCell

    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        return action == NSSelectorFromString("editCollection") || action == NSSelectorFromString("deleteCollection")
    }
    

    从单元格调用委托函数(需要在您的自定义 UICollectionViewCell 中)

    func editCollection()
    {
        let collectionView = self.superview as! UICollectionView
        let d:UICollectionViewDelegate = collectionView.delegate!
        d.collectionView!(collectionView, performAction: NSSelectorFromString("editCollection"), forItemAt: collectionView.indexPath(for: self)!, withSender: self)
    }
    func deleteCollection()
    {
        let collectionView = self.superview as! UICollectionView
        let d:UICollectionViewDelegate = collectionView.delegate!
        d.collectionView!(collectionView, performAction: NSSelectorFromString("deleteCollection"), forItemAt: collectionView.indexPath(for: self)!, withSender: self)
    }
    

    【讨论】:

    • 感谢您的解决方案,但我崩溃了。并且错误日志是“-[roompi.OutgoingTextCell copyCollection]: unrecognized selector sent to instance 0x10bc05fd0 2019-06-17 10:43:41.878416+0700 roompi[3356:428347] *** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:'-[roompi.OutgoingTextCell copyCollection]: 无法识别的选择器发送到实例 0x10bc05fd0''"
    【解决方案3】:

    我刚刚花了两天时间试图找出“正确”的做法,并用周围的一些建议找出错误的树。

    这篇文章展示了正确的做法。我希望通过在这里发布它可以节省几个小时。

    http://dev.glide.me/2013/05/custom-item-in-uimenucontroller-of.html

    【讨论】:

    • 我还没有开始在 iOS7 上工作,但根据您的评论,我预计会出现问题。您找到任何解决方法了吗?我想我会在接下来的几天内解决它。
    • 是的,我终于让它工作了。在这里查看我对自己问题的回答stackoverflow.com/questions/18774265/…
    • 谢谢;我想我有那个代码,但会仔细检查。很高兴你成功了。
    • 这在 iOS7 上也适用于我。根据 Smick 的链接,看起来关键是要实现 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
    • 虽然您提到的链接可能是一个解决方案,但该链接不再有效,因此该答案不再有用。因此,请务必发布代码的相关部分,而不仅仅是链接到它。
    【解决方案4】:

    Swift 3 解决方案:

    只需在 UICollectionView 类中执行所有操作,并将该类分配给 UICollectionView 对象。

    import UIKit
    
    class MyAppCollectionView: UICollectionView {
    
        required public init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    
            addLongPressGesture()
        }
    
        func addLongPressGesture() {
            let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(MyAppCollectionView.longPressed(_:)))
            longPressGesture.minimumPressDuration = 0.5
            self.addGestureRecognizer(longPressGesture)
        }
    
        func longPressed(_ gesture: UILongPressGestureRecognizer) {
    
            let point = gesture.location(in: self)
            let indexPath = self.indexPathForItem(at: point)
    
            if indexPath != nil {
    
                MyAppViewController.cellIndex = indexPath!.row
                let editMenu = UIMenuController.shared
                becomeFirstResponder()
                let custom1Item = UIMenuItem(title: "Custom1", action: #selector(MyAppViewController.custome1Method))
                let custom2Item = UIMenuItem(title: "Custom2", action: #selector(MyAppViewController.custome2Method))
                editMenu.menuItems = [custom1Item, custom2Item]
                editMenu.setTargetRect(CGRect(x: point.x, y: point.y, width: 20, height: 20), in: self)
                editMenu.setMenuVisible(true, animated: true)
            }
    
        }
    
        override var canBecomeFirstResponder: Bool {
    
            return true
        }
    }
    
    class MyAppViewController: UIViewController {
    
         override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
                // You need to only return true for the actions you want, otherwise you get the whole range of
                //  iOS actions. You can see this by just removing the if statement here.
    
                //For folder edit
                if action == #selector(MyAppViewController.custome1Method) {
                    return true
                }
    
                if action == #selector(MyAppViewController.custome2Method) {
                    return true
                }
    
                return false
            }
    }
    

    【讨论】:

      【解决方案5】:

      当人们无法让菜单在集合视图(或表格视图,就此而言)中长按时工作时,总是出于以下两个原因之一:

      • 您正在使用长按手势识别器进行某些操作。例如,您不能在同一个集合视图中同时拥有拖动和菜单。

      • 您忘记在单元格中实现选择器

      例如,OP的代码说:

      UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Custom Action"
                                                 action:@selector(customAction:)];
      

      暗示customAction是一个方法this类。这是错误customAction: 必须是 cell 类的方法。原因是运行时会查看单元类,除非单元实现菜单项的操作方法,否则不会显示菜单项。

      有关完整的最小工作示例(在 Swift 中),请在此处查看我的答案:https://stackoverflow.com/a/51898182/341994

      【讨论】:

        【解决方案6】:

        在带有 SwiftiOS 9 上仅显示自定义项目(没有默认剪切、粘贴等),我只设法使用以下代码。

        关于方法viewDidLoad

        let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(contextMenuHandler))
        longPressRecognizer.minimumPressDuration = 0.3
        longPressRecognizer.delaysTouchesBegan = true
        self.collectionView?.addGestureRecognizer(longPressRecognizer)
        

        覆盖方法canBecomeFirstResponder

        override func canBecomeFirstResponder() -> Bool {
            return true
        }
        

        重写这两个集合相关的方法:

        override func collectionView(collectionView: UICollectionView, shouldShowMenuForItemAtIndexPath indexPath: NSIndexPath) -> Bool {
            return true
        }
        
        override func collectionView(collectionView: UICollectionView, canPerformAction action: Selector,
                                     forItemAtIndexPath indexPath: NSIndexPath, withSender sender: AnyObject?) -> Bool {
            return (action == #selector(send) || action == #selector(delete))
        }
        

        创建手势处理方法:

        func contextMenuHandler(gesture: UILongPressGestureRecognizer) {
        
            if gesture.state == UIGestureRecognizerState.Began {
        
                let indexPath = self.collectionView?.indexPathForItemAtPoint(gesture.locationInView(self.collectionView))
        
                if indexPath != nil {
        
                    self.selectedIndexPath = indexPath!
        
                    let cell = self.collectionView?.cellForItemAtIndexPath(self.selectedIndexPath)
                    let menu = UIMenuController.sharedMenuController()
                    let sendMenuItem = UIMenuItem(title: "Send", action: #selector(send))
                    let deleteMenuItem = UIMenuItem(title: "Delete", action: #selector(delete))
                    menu.setTargetRect(CGRectMake(0, 5, 60, 80), inView: (cell?.contentView)!)
                    menu.menuItems = [sendMenuItem, deleteMenuItem]
                    menu.setMenuVisible(true, animated: true)
                }
            }
        }
        

        最后,创建选择器的方法:

        func send() {
            print("Send performed!")
        }
        
        func delete() {
            print("Delete performed!")
        }
        

        希望对您有所帮助。 :)

        干杯。

        【讨论】:

        • 你可以在没有gestureRecognizer的情况下做所有这些事情,只需覆盖默认方法。
        • @NosovPavel 使用默认方法可以将值添加到默认菜单。如果您想要一个仅包含您的条目的菜单,则必须使用手势识别器。请不要这么快投票。
        • 您的解决方案有效,但这不是做所有这些事情的正确方法。您还可以使用没有识别器的默认菜单添加自定义菜单项。例如:dev.glide.me/2013/05/custom-item-in-uimenucontroller-of.html
        • 我尝试了此处描述的所有其他解决方案,以及您在此处粘贴的链接以及许多其他教程。没有适用于我的上下文:仅显示没有默认条目的自定义条目。你试过这样做吗?如果是这样,请用代码放置您的解决方案的屏幕截图(仅包含自定义条目,没有默认的复制、粘贴等)。谢谢。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多