Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UILongPressGestureRecognizer breaks tableView scrolling

I created a custom Label class that contains a UILongPressGestureRecognizer, and I am invoking it in tableview cells in a TableViewController. The long press gesture recognizer works (two clickable zones in an attributed sting), but the tableView containing the labels no longer scrolls (pans) if the scroll gesture begins in one of the UILongPressGestureRecognizer zones of my CustomLabel. I have tried cancelsTouchesInView = false as well as the various responses below, to no avail. Any suggestions would be greatly appreciated. I've spent a week on this problem. My code is below.

Here is the CustomLabel class:

class CustomLabel: UILabel {

    let layoutManager = NSLayoutManager()
    let textContainer = NSTextContainer(size: CGSize.zero)
    var textStorage = NSTextStorage() {
        didSet {
            textStorage.addLayoutManager(layoutManager)
        }
    }

    var onCharacterTapped: ((_ label: UILabel, _ characterIndex: Int, _ state: Bool) -> Void)?

    let tapGesture = UILongPressGestureRecognizer()

    override var attributedText: NSAttributedString? {
        didSet {
            if let attributedText = attributedText {

                if attributedText.string != textStorage.string {

                textStorage = NSTextStorage(attributedString: attributedText)

                DispatchQueue.main.async {

                    let characterDelay = TimeInterval(0.01 + Float(arc4random()) /  Float(UInt32.max)) / 100

                    for (index, char) in attributedText.string.characters.enumerated() {

                        DispatchQueue.main.asyncAfter(deadline: .now() + characterDelay * Double(index)) {
                            print("character ch is: \(char) at index: \(index)")
                            super.attributedText = attributedText.attributedSubstring(from: NSRange(location: 0, length: index+1))
                        }
                    }
                }
                }

            } else {
                textStorage = NSTextStorage()
            }
        }
    }

    override var lineBreakMode: NSLineBreakMode {
        didSet {
            textContainer.lineBreakMode = lineBreakMode
        }
    }

    override var numberOfLines: Int {
        didSet {
            textContainer.maximumNumberOfLines = numberOfLines
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setUp()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setUp()
    }

    func setUp() {
        isUserInteractionEnabled = true
        layoutManager.addTextContainer(textContainer)
        textContainer.lineFragmentPadding = 0
        textContainer.lineBreakMode = lineBreakMode
        textContainer.maximumNumberOfLines = numberOfLines
        tapGesture.addTarget(self, action: #selector(CustomLabel.labelTapped(_:)))
        tapGesture.minimumPressDuration = 0
        tapGesture.cancelsTouchesInView = false
        //tapGesture.delegate = self.superview
        addGestureRecognizer(tapGesture)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        textContainer.size = bounds.size
    }

    func labelTapped(_ gesture: UILongPressGestureRecognizer) {

        let locationOfTouch = gesture.location(in: gesture.view)
        let textBoundingBox = layoutManager.usedRect(for: textContainer)
        let textContainerOffset = CGPoint(x: (bounds.width - textBoundingBox.width) / 2 - textBoundingBox.minX, y: (bounds.height - textBoundingBox.height) / 2 - textBoundingBox.minY)
        let locationOfTouchInTextContainer = CGPoint(x: locationOfTouch.x - textContainerOffset.x, y: locationOfTouch.y - textContainerOffset.y)
        let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

        if gesture.state == .began {

            onCharacterTapped?(self, indexOfCharacter, true)

        } else if gesture.state == .ended {

            onCharacterTapped?(self, indexOfCharacter, false)

        }

    }

}

Here is the cellClass:

class friendTextCell: UITableViewCell {

    @IBOutlet weak var labelText: CustomLabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        self.layoutIfNeeded()
    }

}

And here is selections from the TableViewControllerClass where CustomCells are created:

class UsersViewController: UITableViewController, UIGestureRecognizerDelegate {

private func gestureRecognizer(gestureRecognizer: UIPanGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UILongPressGestureRecognizer) -> Bool {return true}


    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return gestureRecognizer === longPressRecognizer &&
            (otherGestureRecognizer.view?.isDescendant(of:tableView) ?? false)
    }

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "friendText", for: indexPath) as! friendTextCell

        print("keyArrrrray is: \(keyArray)")

        if indexPath.section == 0 && indexPath.row < keyArray.count {
            self.removeInstructions()
            cell.labelText.font = cell.labelText.font.withSize(17)
            let text = "> "+namesArray[indexPath.row] + ": " + linkArray[indexPath.row]
            let name = namesArray[indexPath.row]
            let link = linkArray[indexPath.row]
            let imageLink = imageURLArray[indexPath.row]
            let nameChCount = name.characters.count
            let linkChCount = link.characters.count

            let attributedString = NSMutableAttributedString(string: name + ": " + link, attributes: nil)

            let totalChCount = attributedString.string.characters.count

            let linkRange = NSMakeRange(0, nameChCount) // for the word "link" in the string above

            let linkAttributes: [String : AnyObject] = [
                NSForegroundColorAttributeName : UIColor.white, NSUnderlineStyleAttributeName : NSUnderlineStyle.styleSingle.rawValue as AnyObject]
            attributedString.setAttributes(linkAttributes, range:linkRange)

            cell.labelText.attributedText = attributedString

            cell.labelText.onCharacterTapped = { label, characterIndex, state in

                let highlight: [String : AnyObject] = [NSForegroundColorAttributeName : UIColor.black, NSBackgroundColorAttributeName : UIColor.white]

                if state == true {
                    if characterIndex < nameChCount {
                        print("name press began at character \(characterIndex)")
                        attributedString.addAttributes(highlight, range:NSMakeRange(0, nameChCount))
                        cell.labelText.attributedText = attributedString
                    } else if characterIndex > nameChCount {
                        print("link press began at character \(characterIndex)")
                        let startPos = nameChCount + 2
                        let endPos = totalChCount-nameChCount-2
                        attributedString.addAttributes(highlight, range:NSMakeRange(startPos, endPos))
                        cell.labelText.attributedText = attributedString
                    }

                } else if state == false {

                    if characterIndex < name.characters.count {

                        if let userVC:UserViewTableViewController = self.storyboard?.instantiateViewController(withIdentifier: "UserVC") as? UserViewTableViewController {
                            userVC.userName = name
                            userVC.shareLink = link
                            userVC.imageLink = imageLink
                            self.navigationController?.pushViewController(userVC, animated: true)
                        }

                }

                if characterIndex > name.characters.count && characterIndex <= link.characters.count + name.characters.count {

                    //extract link from array
                    let link = self.linkArray[indexPath.row]
                    print("on click link is: \(link)")

                    //Present SafariViewController with link
                    let svc = SFSafariViewController(url: NSURL(string: link)! as URL)
                    self.present(svc, animated: true, completion: nil)

                }
                }

            }

        } else if keyArray.isEmpty && indexPath.section == 0 {

            cell.labelText.text = "..."

        }

        if indexPath.section == 1 && keyArray.count <= 1 {
            let message = "> Press the + button to add more friends."
            cell.labelText.animate(newText: message, characterDelay: TimeInterval(0.01 + Float(arc4random()) /  Float(UInt32.max)) / 200)
        } else if indexPath.section == 1 {
            cell.labelText.text = ""
        }

        return cell

    }
like image 481
lucius degeer Avatar asked Nov 29 '17 15:11

lucius degeer


1 Answers

Replace

tapGesture.minimumPressDuration = 0

With

tapGesture.minimumPressDuration = 0.5

The recognition is starting too soon and not getting the table to get touches

like image 62
Yitzchak Avatar answered Nov 04 '22 10:11

Yitzchak