Error when try becomeFirstResponder call for UIMenuController

1.1k Views Asked by At

I have a messenger, so I want to show UIMenuController on cell long tap. enter image description here I need image view to catch long press, because I have text and media messages. But I get a crash becomeFirstResponder call after I received long press gesture on iOS 8(iOS 7 works well):

*** Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency', reason: 'child view controller:<UICompatibilityInputViewController: 0x13fd75b90> should have parent view controller:<FriendPlay.WLDialogViewController: 0x13fd54d10> but requested parent is:<UIInputWindowController: 0x140815000>'

This is the part of code:

override func handleLongPress(longPress: UILongPressGestureRecognizer!) {
    let alpha: CGFloat = 0.6
    let point = longPress.locationInView(tableView)
    let indexPath = tableView.indexPathForRowAtPoint(point)
    if indexPath != nil {
        let cell = tableView.cellForRowAtIndexPath(indexPath!) as MessageCell
        let convertedPoint = longPress.locationInView(cell.balloonImageView)
        if CGRectContainsPoint(cell.balloonImageView.frame, convertedPoint) {
            var myView = cell.balloonImageView
            if cell.message.type == MessageType.Text {
                cell.balloonImageView.alpha = alpha
            }
            else {
                myView = cell.mediaImageView
                cell.mediaImageView.alpha = alpha
            }
            if longPress.state == UIGestureRecognizerState.Began {
                showMenuAtPoint(convertedPoint, myView: myView)
            }
        }
    }
}

private func showMenuAtPoint(point: CGPoint, myView: MessageImageView) {
    myView.becomeFirstResponder()// <--- that crashes

    let rect = CGRectMake(point.x, point.y, 0, 0)
    let mc = UIMenuController.sharedMenuController()
    mc.setTargetRect(rect, inView: myView)
    let sendItem = UIMenuItem(title: "Send", action: "sendAgain:")
    mc.menuItems = [sendItem]
    mc.setMenuVisible(true, animated: true)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "menuDidHide:", name: UIMenuControllerDidHideMenuNotification, object: nil)
}

func sendAgain(sender: AnyObject) {
    println("Send!")
}

For class MessageImageView (inherited from UIImageView) I redefine becomeFirstResponder to return always true. I initialize my cell in - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier messageMaxWidth:(CGFloat)messageMaxWidth

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier messageMaxWidth:(CGFloat)messageMaxWidth
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if (self) {
        self.messageMaxWidth = messageMaxWidth;
        self.backgroundColor = [UIColor clearColor];
        self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
        self.panGesture.delegate = self;
        [self addGestureRecognizer:self.panGesture];

        [self setInitialSizes];

        [[NSNotificationCenter defaultCenter] removeObserver:self];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOrientationWillChandeNote:) name:UIApplicationWillChangeStatusBarFrameNotification object:nil];
    }

    return self;
}

    - (void)setInitialSizes
{
    if (self.containerView) {
        [self.containerView removeFromSuperview];
    }
    if (self.timeLabel) {
        [self.timeLabel removeFromSuperview];
    }

    self.userImageView = [[UIImageView alloc] init];
    self.userImageView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
    self.textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, self.messageMaxWidth, 0)];
    self.timeLabel = [[UILabel alloc] init];
    self.mediaImageView = [[SOmessageImageView alloc] init];
    self.mediaOverlayView = [[UIView alloc] init];
    self.balloonImageView = [[SOmessageImageView alloc] init];

    self.balloonImageView.userInteractionEnabled = YES;

    if (!CGSizeEqualToSize(self.userImageViewSize, CGSizeZero)) {
        CGRect frame = self.userImageView.frame;
        frame.size = self.userImageViewSize;
        self.userImageView.frame = frame;
    }

    self.userImageView.contentMode = UIViewContentModeScaleAspectFill;
    self.userImageView.clipsToBounds = YES;
    self.userImageView.backgroundColor = [UIColor clearColor];
    self.userImageView.layer.cornerRadius = 5;

    if (!CGSizeEqualToSize(self.mediaImageViewSize, CGSizeZero)) {
        CGRect frame = self.mediaImageView.frame;
        frame.size = self.mediaImageViewSize;
        self.mediaImageView.frame = frame;
    }

    self.mediaImageView.contentMode = UIViewContentModeScaleAspectFill;
    self.mediaImageView.clipsToBounds = YES;
    self.mediaImageView.backgroundColor = [UIColor clearColor];
    self.mediaImageView.userInteractionEnabled = YES;
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleMediaTapped:)];
    [self.mediaImageView addGestureRecognizer:tap];

    self.mediaOverlayView.backgroundColor = [UIColor clearColor];
    [self.mediaImageView addSubview:self.mediaOverlayView];

    self.textView.textColor = [UIColor whiteColor];
    self.textView.backgroundColor = [UIColor clearColor];
    [self.textView setTextContainerInset:UIEdgeInsetsZero];
    self.textView.textContainer.lineFragmentPadding = 0;

    [self hideSubViews];

    self.containerView = [[UIView alloc] initWithFrame:self.contentView.bounds];
    self.containerView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;

    [self.contentView addSubview:self.containerView];

    [self.containerView addSubview:self.balloonImageView];
    [self.containerView addSubview:self.textView];
    [self.containerView addSubview:self.mediaImageView];
    [self.containerView addSubview:self.userImageView];

    self.contentView.clipsToBounds = NO;
    self.clipsToBounds = NO;

    self.timeLabel.font = [UIFont fontWithName:@"AvenirNextCyr-Light" size:11];
    self.timeLabel.textColor = [UIColor grayColor];
    self.timeLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;

    UIImage *backgroundImage = [UIImage imageNamed:@"messagesDateBackground"];
    if (backgroundImage) {
        self.timeLabel.textColor = [UIColor whiteColor];
        if (self.backgroundImageView) {
            [self.backgroundImageView removeFromSuperview];
        }
        self.backgroundImageView = [[UIImageView alloc] initWithImage:[backgroundImage resizableImageWithCapInsets:UIEdgeInsetsMake(20, 20, 20, 20)]];
        self.backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
        [self.contentView addSubview:self.backgroundImageView];
    }

    [self.contentView addSubview:self.timeLabel];
}

Please give me some suggestions, how to fix this strange bug.

1

There are 1 best solutions below

0
On BEST ANSWER

Your view controller probably has a property named inputView that is merely a subview, not an inputView as UIResponder interface expects it to be.

Starting with iOS 8 they check that UIResponder's inputView has no parent.