Search code examples
swiftuicollectionviewuigesturerecognizerreturn-typeibaction

Passing a variable from Gesture Recognizer function to an IBAction


I have a simple app with a CollectionView and items in it. On long pressing a cell a popup UIView appears with a TextField and an option to save it in the array corresponding to the cell.

Here is the code (The buttons and gestures have been added correctly in viewDidLoad() method):

class CollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate  {
var longPressedPoint: CGPoint?

public var rowOfLongPressedItem: Int? = nil

func handleLongPress(longPressRecognizer: UILongPressGestureRecognizer)  -> Int      {
    print("LONG PRESS Gesture Recognized")
    notePopup.hidden = false
    longPressedPoint =  longPressRecognizer.locationInView(longPressRecognizer.view)
    var indexPathOfLongPressedCell = self.itemCollectionView.indexPathForItemAtPoint(longPressedPoint!)
    rowOfLongPressedItem = (indexPathOfLongPressedCell?.row)
    print("rowOfLongPressedItem -> .\(rowOfLongPressedItem)")
    return rowOfLongPressedItem!
}

func saveNoteButtonTapped(rowOfLongPressedItem: Int) {
    print("rowOfLongPressedItem when Save button is tapped -> .\(rowOfLongPressedItem)")       

    //Can’t go further down as rowOfLongPressedItem is NOT available from “handleLongPress” function…

    var selectedItem = ItemsList[rowOfLongPressedItem]
    selectedItem.counts += 1
    var latest = selectedItem.counts - 1
    selectedItem.timestamp.append(NSDate())
    selectedItem.note.append(noteTextField.text)
    ItemsList[rowOfLongPressedItem] = selectedItem
    print(".\(selectedItem.title) has been tapped .\(selectedItem.counts)")
    print("The latest tap on .\(selectedItem.title) is at .\(selectedItem.timestamp[latest])")
    print("The note .\(noteTextField.text) has been added")
    notePopup.hidden = true
}
}

Tried solving the issue in couple of ways:

  • Defining a variable in the View Controller hoping that the function will return the value and save it in global variable. But, later found from Apple that "A function cannot have a higher access level than its parameter types and return type, because the function could be used in situations where its constituent types are not available to the surrounding code." https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html

  • I tried putting the Button's Selector function code inside the Long Press Gesture Function so that it's return value is easily available. But, I am unable to call the Selector function as it is inside another function.

  • Also, I tried returning the value of Long Press Gesture Function and using it in IBAction of the Save button. However, for this I need call handleLongPress again and then the longPressedPoint is detected as inside Save button. Hence, the indexPathOfLongPressedCell is nil and the app crashes.

Can someone please help me...


Solution

  • Assuming that you want to get the selected cell's row and assigns it to a global variable rowOfLongPressedItem, you don't need to let handleLongPress returns an Int.

    NOTE: This is a Swift 3 code (with the same concept):

    public var rowOfLongPressedItem: Int? = nil
    
    override func viewDidLoad() {
        //...
    
        let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.assignRowOfLongPressedItem))
    
        itemCollectionView.addGestureRecognizer(longPressRecognizer)
    
        //...
    }
    
    func assignRowOfLongPressedItem(longPressRecognizer: UILongPressGestureRecognizer) {
        let longPressedPoint = longPressRecognizer.location(in: longPressRecognizer.view)
        var indexPathOfLongPressedCell = self.itemCollectionView.indexPathForItem(at: longPressedPoint)
        rowOfLongPressedItem = (indexPathOfLongPressedCell?.row)
        // if you long press the first row -for example-, the output should be: "rowOfLongPressedItem -> .Optional(0)"
        print("rowOfLongPressedItem -> .\(rowOfLongPressedItem)")
    }
    

    Also, you don't need to let saveNoteButtonTapped to take rowOfLongPressedItem parameter. Note that rowOfLongPressedItem is optional, you should make sure it is not still nil (you can use Early Exit approach):

    func saveNoteButtonTapped(sender: UIButton) {
            guard let selectedCellRow = rowOfLongPressedItem else {
                print("rowOfLongPressedItem is nil!!")
                return
            }
    
            print("rowOfLongPressedItem when Save button is tapped -> .\(selectedCellRow)")
            var selectedItem = ItemsList[selectedCellRow]
    
            selectedItem.counts += 1
            var latest = selectedItem.counts - 1
            selectedItem.timestamp.append(NSDate())
            selectedItem.note.append(noteTextField.text)
            ItemsList[row] = selectedItem
            print(".\(selectedItem.title) has been tapped .\(selectedItem.counts)")
            print("The latest tap on .\(selectedItem.title) is at .\(selectedItem.timestamp[latest])")
            print("The note .\(noteTextField.text) has been added")
            notePopup.hidden = true
    }