Jak uzyskać indexpath.row, gdy element jest aktywowany?

104

Mam widok tabeli z przyciskami i chcę użyć indexpath.row, gdy jeden z nich zostanie dotknięty. To jest to, co obecnie mam, ale zawsze jest to 0

var point = Int()
func buttonPressed(sender: AnyObject) {
    let pointInTable: CGPoint =         sender.convertPoint(sender.bounds.origin, toView: self.tableView)
    let cellIndexPath = self.tableView.indexPathForRowAtPoint(pointInTable)
    println(cellIndexPath)
    point = cellIndexPath!.row
    println(point)
}
Vincent
źródło
powinienem używać IndexPathForSelectedRow () zamiast zmiennej punktowej? lub gdzie powinienem go użyć?
Vincent

Odpowiedzi:

166

giorashc prawie miał to w swojej odpowiedzi, ale przeoczył fakt, że komórki mają dodatkową contentViewwarstwę. Dlatego musimy zejść o jedną warstwę głębiej:

guard let cell = sender.superview?.superview as? YourCellClassHere else {
    return // or fatalError() or whatever
}

let indexPath = itemTable.indexPath(for: cell)

Dzieje się tak, ponieważ w hierarchii widoków tableView ma komórki jako podglądy, które następnie mają swoje własne „widoki zawartości”, dlatego musisz uzyskać nadzór tego widoku zawartości, aby uzyskać samą komórkę. W rezultacie, jeśli twój przycisk znajduje się w podglądzie, a nie bezpośrednio w widoku zawartości komórki, będziesz musiał przejść o wiele warstw głębiej, aby uzyskać do niego dostęp.

Powyższe jest jednym z takich podejść, ale niekoniecznie najlepszym podejściem. Chociaż jest funkcjonalny, zakłada szczegóły dotyczące elementu, UITableViewCellktórego Apple nigdy nie dokumentował, takie jak hierarchia widoków. Może to ulec zmianie w przyszłości, a powyższy kod może w rezultacie zachowywać się nieprzewidywalnie.

W związku z powyższym, ze względu na długowieczność i niezawodność, zalecam przyjęcie innego podejścia. W tym wątku jest wiele alternatyw i zachęcam do przeczytania, ale mój osobisty faworyt jest następujący:

Przytrzymaj właściwość zamknięcia w swojej klasie komórek, niech metoda akcji przycisku wywoła this.

class MyCell: UITableViewCell {
    var button: UIButton!

    var buttonAction: ((Any) -> Void)?

    @objc func buttonPressed(sender: Any) {
        self.buttonAction?(sender)
    }
}

Następnie, kiedy utworzysz swoją komórkę w cellForRowAtIndexPath, możesz przypisać wartość do zamknięcia.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! MyCell
    cell.buttonAction = { sender in
        // Do whatever you want from your button here.
    }
    // OR
    cell.buttonAction = buttonPressed(closure: buttonAction, indexPath: indexPath) // <- Method on the view controller to handle button presses.
}

Przenosząc tutaj swój kod obsługi, możesz skorzystać z już obecnego indexPathargumentu. Jest to znacznie bezpieczniejsze podejście niż to wymienione powyżej, ponieważ nie opiera się na nieudokumentowanych cechach.

Jacob King
źródło
2
Dobrze zauważony. Jestem kompetentnym programistą, obiecuję;) - Poprawiłem odpowiedź.
Jacob King
12
To nie jest właściwy sposób na pobranie komórki z przycisku. Układ komórki zmienił się na przestrzeni lat i taki kod nie zadziała, gdy tak się stanie. Nie stosuj tego podejścia.
rmaddy
11
To jest złe rozwiązanie. Zakłada szczegóły dotyczące UITableViewCells, na które firma Apple nigdy się nie zgodziła. Chociaż UITableViewCells mają właściwość contentView, nie ma gwarancji, że superview contentView zawsze będzie Cell.
bpapa
1
@PintuRajput Czy możesz mi opisać swoją hierarchię widoków? Prawdopodobnie widzisz ten komunikat, ponieważ przycisk nie jest bezpośrednim podziałem widoku zawartości komórki.
Jacob King
2
@ymutlu Całkowicie się zgadzam, podałem to w odpowiedzi. Zaproponowałem również znacznie solidniejsze rozwiązanie. Powodem, dla którego zostawiłem oryginał na miejscu, jest to, że uważam, że lepiej jest pokazać innym programistom problemy z podejściem, niż całkowicie go unikać, to niczego nie uczy. :)
Jacob King
61

Moje podejście do tego rodzaju problemu polega na użyciu protokołu delegata między komórką a widokiem tabeli. Pozwala to zachować procedurę obsługi przycisku w podklasie komórki, co umożliwia przypisanie procedury obsługi operacji retuszu do komórki prototypu w programie Interface Builder, jednocześnie zachowując logikę obsługi przycisku w kontrolerze widoku.

Pozwala również uniknąć potencjalnie kruchego podejścia do poruszania się po hierarchii widoków lub korzystania z tagwłaściwości, co powoduje problemy, gdy zmieniają się indeksy komórek (w wyniku wstawienia, usunięcia lub zmiany kolejności)

CellSubclass.swift

protocol CellSubclassDelegate: class {
    func buttonTapped(cell: CellSubclass)
}

class CellSubclass: UITableViewCell {

@IBOutlet var someButton: UIButton!

weak var delegate: CellSubclassDelegate?

override func prepareForReuse() {
    super.prepareForReuse()
    self.delegate = nil
}

@IBAction func someButtonTapped(sender: UIButton) {
    self.delegate?.buttonTapped(self)
}

ViewController.swift

class MyViewController: UIViewController, CellSubclassDelegate {

    @IBOutlet var tableview: UITableView!

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CellSubclass

        cell.delegate = self

        // Other cell setup

    } 

    //  MARK: CellSubclassDelegate

    func buttonTapped(cell: CellSubclass) {
        guard let indexPath = self.tableView.indexPathForCell(cell) else {
            // Note, this shouldn't happen - how did the user tap on a button that wasn't on screen?
            return
        }

        //  Do whatever you need to do with the indexPath

        print("Button tapped on row \(indexPath.row)")
    }
} 
Paulw11
źródło
buttonTappedjest funkcją delegata i znajduje się w kontrolerze widoku. W moim przykładzie someButtonTappedjest to metoda akcji w komórce
Paulw11
@ paulw11 I got cell has no member buttonTapped in this method@IBAction func someButtonTapped(sender: UIButton) { self.delegate?.buttonTapped(self) }
EI Captain v2.0
1
To całkiem dobre rozwiązanie (wcale nie tak złe, jak te dwa, które obecnie mają więcej głosów, używając tagu patrząc na superview), ale wydaje się, że dodanie dodatkowego kodu jest zbyt duże.
bpapa
2
To jest poprawne rozwiązanie i powinno być akceptowaną odpowiedzią. Nie nadużywa właściwości tagu, nie zakłada budowy komórek (które Apple może łatwo zmienić) i będzie nadal działać (bez dodatkowego kodowania), gdy komórki zostaną przeniesione lub nowe komórki zostaną dodane między istniejącymi komórkami.
Robotic Cat
1
@ Paulw11 Początkowo myślałem, że to dużo kodu, ale okazało się, że jest dużo bardziej odporny niż to, czego używałem wcześniej. Dziękujemy za przesłanie tego solidnego rozwiązania.
Adrian
53

UPDATE : Pobieranie indexPath komórki zawierającej przycisk (zarówno sekcja, jak i wiersz):

Korzystanie z pozycji przycisku

Wewnątrz buttonTappedmetody możesz pobrać położenie przycisku, przekonwertować go na współrzędną w tableView, a następnie pobrać indexPath wiersza o tej współrzędnej.

func buttonTapped(_ sender:AnyObject) {
    let buttonPosition:CGPoint = sender.convert(CGPoint.zero, to:self.tableView)
    let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
}

UWAGA : Czasami możesz napotkać przypadek krawędzi, gdy użycie funkcji view.convert(CGPointZero, to:self.tableView)powoduje znalezienie nilwiersza w punkcie, nawet jeśli znajduje się tam komórka tableView. Aby to naprawić, spróbuj podać rzeczywistą współrzędną, która jest nieco odsunięta od początku, na przykład:

let buttonPosition:CGPoint = sender.convert(CGPoint.init(x: 5.0, y: 5.0), to:self.tableView)

Poprzednia odpowiedź: Używanie właściwości tagu (zwraca tylko wiersz)

Zamiast wspinać się po drzewach Superview, aby złapać wskaźnik do komórki, w której znajduje się przycisk UIButton, istnieje bezpieczniejsza, bardziej powtarzalna technika wykorzystująca właściwość button.tag wspomnianą przez Antonio powyżej, opisaną w tej odpowiedzi i pokazaną poniżej:

W cellForRowAtIndexPath:ustawiasz właściwość tagu:

button.tag = indexPath.row
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)

Następnie w buttonClicked:funkcji odwołujesz się do tego tagu, aby pobrać wiersz indexPath, w którym znajduje się przycisk:

func buttonClicked(sender:UIButton) {
    let buttonRow = sender.tag
}

Wolę tę metodę, ponieważ odkryłem, że kołysanie się w drzewach nadzoru może być ryzykownym sposobem projektowania aplikacji. Ponadto w przypadku obiektywu C używałem tej techniki w przeszłości i byłem zadowolony z wyniku.

Żelazny John Bonney
źródło
5
To fajny sposób na zrobienie tego i zagłosuję za nim, aby trochę uruchomić przedstawiciela, jednak jedyną wadą jest to, że nie daje dostępu do sekcji indexPath.section, jeśli jest to również potrzebne. Świetna odpowiedź!
Jacob King
Dzięki Jacob! Doceniam karmę rep. Jeśli chcesz uzyskać indexPath.sectiondodatek do indexPath.row(bez resetowania właściwości tagu jako indexPath.section), cellForRowAtIndexPath:możesz po prostu zmienić tag na button.tag = indexPath, a następnie w buttonClicked:funkcji można uzyskać dostęp zarówno za pomocą, jak sender.tag.rowi sender.tag.section.
Iron John Bonney
1
Czy jest to nowa funkcja, ponieważ jestem pewien, że pamiętam, że właściwość tagu była typu Int, a nie AnyObject, chyba że zmieniło się to w Swift 2.3?
Jacob King
@JacobKing masz rację! Moja wina, że ​​pisząc ten komentarz, całkowicie się rozłożyłem i pomyślałem, że tag to AnyObject. Derp - nie przejmuj się mną. Przydałoby się jednak, gdybyś mógł przekazać indexPath jako tag ...
Iron John Bonney
3
Niezbyt dobre podejście. Po pierwsze, działałoby to tylko w widokach tabeli, w których jest jedna sekcja.
bpapa
16

Użyj rozszerzenia UITableView, aby pobrać komórkę dla dowolnego widoku:


@ Paulw11 odpowiedź dotycząca skonfigurowania niestandardowego typu komórki z właściwością delegata, która wysyła komunikaty do widoku tabeli, jest dobrym rozwiązaniem, ale wymaga pewnej ilości pracy do skonfigurowania.

Myślę, że chodzenie po hierarchii widoku komórki w widoku tabeli w poszukiwaniu komórki to zły pomysł. Jest kruchy - jeśli później umieścisz przycisk w widoku w celach związanych z układem, kod prawdopodobnie się zepsuje.

Używanie tagów widoku jest również delikatne. Musisz pamiętać o skonfigurowaniu tagów podczas tworzenia komórki, a jeśli użyjesz tego podejścia w kontrolerze widoku, który używa tagów widoku do innego celu, możesz mieć zduplikowane numery znaczników, a kod może nie działać zgodnie z oczekiwaniami.

Utworzyłem rozszerzenie do UITableView, które pozwala uzyskać indexPath dla dowolnego widoku zawartego w komórce widoku tabeli. Zwraca wartość, Optionalktóra będzie nil, jeśli przekazany widok faktycznie nie mieści się w komórce widoku tabeli. Poniżej znajduje się plik źródłowy rozszerzenia w całości. Możesz po prostu umieścić ten plik w swoim projekcie, a następnie użyć dołączonej indexPathForView(_:)metody, aby znaleźć indexPath, która zawiera dowolny widok.

//
//  UITableView+indexPathForView.swift
//  TableViewExtension
//
//  Created by Duncan Champney on 12/23/16.
//  Copyright © 2016-2017 Duncan Champney.
//  May be used freely in for any purpose as long as this 
//  copyright notice is included.

import UIKit

public extension UITableView {
  
  /**
  This method returns the indexPath of the cell that contains the specified view
   
   - Parameter view: The view to find.
   
   - Returns: The indexPath of the cell containing the view, or nil if it can't be found
   
  */
  
    func indexPathForView(_ view: UIView) -> IndexPath? {
        let center = view.center
        let viewCenter = self.convert(center, from: view.superview)
        let indexPath = self.indexPathForRow(at: viewCenter)
        return indexPath
    }
}

Aby go użyć, możesz po prostu wywołać metodę w IBAction dla przycisku zawartego w komórce:

func buttonTapped(_ button: UIButton) {
  if let indexPath = self.tableView.indexPathForView(button) {
    print("Button tapped at indexPath \(indexPath)")
  }
  else {
    print("Button indexPath not found")
  }
}

(Zwróć uwagę, że indexPathForView(_:) funkcja będzie działać tylko wtedy, gdy przekazany obiekt widoku jest zawarty w komórce, która jest obecnie na ekranie. Jest to rozsądne, ponieważ widok, którego nie ma na ekranie, nie należy w rzeczywistości do określonej ścieżki indexPath; prawdopodobnie być przypisane do innego indexPath, gdy zawiera komórkę jest odtwarzana).

EDYTOWAĆ:

Możesz pobrać działający projekt demonstracyjny, który używa powyższego rozszerzenia z Github: TableViewExtension.git

Duncan C
źródło
Dzięki, użyłem rozszerzenia, aby uzyskać indexPath widoku tekstowego w komórce - działało idealnie.
Jeremy Andrews
9

Dla Swift2.1

Znalazłem sposób, żeby to zrobić, mam nadzieję, że to pomoże.

let point = tableView.convertPoint(CGPoint.zero, fromView: sender)

    guard let indexPath = tableView.indexPathForRowAtPoint(point) else {
        fatalError("can't find point in tableView")
    }
specjalna ofiara
źródło
Co to znaczy, że błąd działa? Jakie są powody, dla których nie może znaleźć punktu w tableView?
OOProg
To (lub podobnie, przy użyciu metod konwersji UIView) powinno być akceptowaną odpowiedzią. Nie jestem pewien, dlaczego obecnie zajmuje 4 miejsce, ponieważ nie przyjmuje założeń dotyczących prywatnej hierarchii widoku tabeli, nie używa właściwości tag (prawie zawsze jest to zły pomysł) i nie wymaga dużo dodatkowego kodu.
bpapa
9

Rozwiązanie Swift 4:

Masz przycisk (myButton) lub inny widok w komórce. Przypisz tag w cellForRowAt w ten sposób

cell.myButton.tag = indexPath.row

Teraz w tobie TapFunction lub jakikolwiek inny. Pobierz go w ten sposób i zapisz w zmiennej lokalnej.

currentCellNumber = (sender.view?.tag)!

Następnie możesz użyć w dowolnym miejscu tego currentCellNumber, aby uzyskać indexPath.row wybranego przycisku.

Cieszyć się!

Sajid Zeb
źródło
To podejście działa, ale tagi widoku są delikatne, jak wspomniałem w mojej odpowiedzi. Na przykład prosty znacznik całkowity nie będzie działał w widoku tabeli podzielonej na części. (ścieżka IndexPath to 2 liczby całkowite). Moje podejście zawsze będzie działać i nie ma potrzeby instalowania tagu w przycisku (lub innym widoku, który można kliknąć).
Duncan C
6

W Swift 4 po prostu użyj tego:

func buttonTapped(_ sender: UIButton) {
        let buttonPostion = sender.convert(sender.bounds.origin, to: tableView)

        if let indexPath = tableView.indexPathForRow(at: buttonPostion) {
            let rowIndex =  indexPath.row
        }
}
DEEPAK KUMAR
źródło
Najczystsza odpowiedź powinna być wybrana. Jedyną rzeczą wartą uwagi jest to, że tableViewjest to zmienna outlet, do której należy odwołać się przed zadziałaniem tej odpowiedzi.
10000RubyPools
Działa jak urok !!
Parthpatel1105
4

Bardzo proste uzyskanie szybkiej ścieżki indeksu 4, 5

let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! Cell
cell.btn.tag = indexPath.row
cell.btn.addTarget(self, action: "buttonTapped:", forControlEvents: 
UIControlEvents.TouchUpInside)

Jak uzyskać IndexPath Inside Btn Click:

func buttonTapped(_ sender: UIButton) {
     print(sender.tag)  
}
M Murteza
źródło
3

Ponieważ nadawcą programu obsługi zdarzeń jest sam przycisk, użyłbym właściwości przycisku tagdo przechowywania indeksu zainicjowanego w cellForRowAtIndexPath.

Ale przy odrobinie więcej pracy wykonałbym zupełnie inny sposób. Jeśli używasz niestandardowej komórki, podszedłbym do problemu:

  • dodaj właściwość „indexPath” do niestandardowej komórki tabeli
  • zainicjuj go w cellForRowAtIndexPath
  • Przenieś procedurę obsługi zaczepu z kontrolera widoku do implementacji komórki
  • użyj wzorca delegowania, aby powiadomić kontroler widoku o zdarzeniu tap, przekazując ścieżkę indeksu
Antonio
źródło
Antonio, mam niestandardową komórkę i chciałbym to zrobić na swój sposób. Jednak to nie działa. Chcę, aby mój kod „machnięcia, aby odsłonić przycisk usuwania” został uruchomiony, czyli metoda tableView commitEditingStyle. Usunąłem ten kod z klasy mainVC i umieściłem go w klasie customCell, ale teraz kod już nie działa. czego mi brakuje?
Dave G
Myślę, że to najlepszy sposób na uzyskanie ścieżki indexPath komórki z x sekcjami, jednak nie widzę potrzeby wypunktowania 3 i 4 w podejściu MVC
Edward
2

Po zapoznaniu się z sugestią Paulw11 dotyczącą użycia funkcji wywołania zwrotnego delegata, chciałem ją nieco rozwinąć / przedstawić inną, podobną sugestię. Jeśli nie chcesz używać wzorca delegata, możesz szybko użyć domknięć w następujący sposób:

Twoja klasa komórkowa:

class Cell: UITableViewCell {
    @IBOutlet var button: UIButton!

    var buttonAction: ((sender: AnyObject) -> Void)?

    @IBAction func buttonPressed(sender: AnyObject) {
        self.buttonAction?(sender)
    }
}

Twoja cellForRowAtIndexPathmetoda:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! Cell
    cell.buttonAction = { (sender) in
        // Do whatever you want from your button here.
    }
    // OR
    cell.buttonAction = buttonPressed // <- Method on the view controller to handle button presses.
}
Jacob King
źródło
2

Znalazłem bardzo łatwy sposób zarządzania dowolną komórką w tableView i collectionView przy użyciu klasy Model i działa to równie doskonale.

Obecnie istnieje znacznie lepszy sposób radzenia sobie z tym. To zadziała do zarządzania komórką i wartością.

Oto moje wyjście (zrzut ekranu), więc zobacz to:

wprowadź opis obrazu tutaj

  1. Bardzo łatwo jest stworzyć klasę modelu , postępuj zgodnie z poniższą procedurą. Utwórz szybką klasę o nazwie RNCheckedModel, napisz kod jak poniżej.
class RNCheckedModel: NSObject {

    var is_check = false
    var user_name = ""

    }
  1. stwórz swoją klasę komórek
class InviteCell: UITableViewCell {

    @IBOutlet var imgProfileImage: UIImageView!
    @IBOutlet var btnCheck: UIButton!
    @IBOutlet var lblName: UILabel!
    @IBOutlet var lblEmail: UILabel!
    }
  1. i na koniec użyj klasy modelu w UIViewController, gdy używasz UITableView .
    class RNInviteVC: UIViewController, UITableViewDelegate, UITableViewDataSource {


    @IBOutlet var inviteTableView: UITableView!
    @IBOutlet var btnInvite: UIButton!

    var checkArray : NSMutableArray = NSMutableArray()
    var userName : NSMutableArray = NSMutableArray()

    override func viewDidLoad() {
        super.viewDidLoad()
        btnInvite.layer.borderWidth = 1.5
        btnInvite.layer.cornerRadius = btnInvite.frame.height / 2
        btnInvite.layer.borderColor =  hexColor(hex: "#512DA8").cgColor

        var userName1 =["Olivia","Amelia","Emily","Isla","Ava","Lily","Sophia","Ella","Jessica","Mia","Grace","Evie","Sophie","Poppy","Isabella","Charlotte","Freya","Ruby","Daisy","Alice"]


        self.userName.removeAllObjects()
        for items in userName1 {
           print(items)


            let model = RNCheckedModel()
            model.user_name = items
            model.is_check = false
            self.userName.add(model)
        }
      }
     @IBAction func btnInviteClick(_ sender: Any) {

    }
       func tableView(_ tableView: UITableView, numberOfRowsInSection 
       section: Int) -> Int {
        return userName.count
    }

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

        let image = UIImage(named: "ic_unchecked")
        cell.imgProfileImage.layer.borderWidth = 1.0
        cell.imgProfileImage.layer.masksToBounds = false
        cell.imgProfileImage.layer.borderColor = UIColor.white.cgColor
        cell.imgProfileImage.layer.cornerRadius =  cell.imgProfileImage.frame.size.width / 2
        cell.imgProfileImage.clipsToBounds = true

        let model = self.userName[indexPath.row] as! RNCheckedModel
        cell.lblName.text = model.user_name

        if (model.is_check) {
            cell.btnCheck.setImage(UIImage(named: "ic_checked"), for: UIControlState.normal)
        }
        else {
            cell.btnCheck.setImage(UIImage(named: "ic_unchecked"), for: UIControlState.normal)
        }

        cell.btnCheck.tag = indexPath.row
        cell.btnCheck.addTarget(self, action: #selector(self.btnCheck(_:)), for: .touchUpInside)

        cell.btnCheck.isUserInteractionEnabled = true

    return cell

    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 80

    }

    @objc func btnCheck(_ sender: UIButton) {

        let tag = sender.tag
        let indexPath = IndexPath(row: tag, section: 0)
        let cell: InviteCell = inviteTableView.dequeueReusableCell(withIdentifier: "InviteCell", for: indexPath) as! InviteCell

        let model = self.userName[indexPath.row] as! RNCheckedModel

        if (model.is_check) {

            model.is_check = false
            cell.btnCheck.setImage(UIImage(named: "ic_unchecked"), for: UIControlState.normal)

            checkArray.remove(model.user_name)
            if checkArray.count > 0 {
                btnInvite.setTitle("Invite (\(checkArray.count))", for: .normal)
                print(checkArray.count)
                UIView.performWithoutAnimation {
                    self.view.layoutIfNeeded()
                }
            } else {
                btnInvite.setTitle("Invite", for: .normal)
                UIView.performWithoutAnimation {
                    self.view.layoutIfNeeded()
                }
            }

        }else {

            model.is_check = true
            cell.btnCheck.setImage(UIImage(named: "ic_checked"), for: UIControlState.normal)

            checkArray.add(model.user_name)
            if checkArray.count > 0 {
                btnInvite.setTitle("Invite (\(checkArray.count))", for: .normal)
                UIView.performWithoutAnimation {
                self.view.layoutIfNeeded()
                }
            } else {
                 btnInvite.setTitle("Invite", for: .normal)
            }
        }

        self.inviteTableView.reloadData()
    }

    func hexColor(hex:String) -> UIColor {
        var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()

        if (cString.hasPrefix("#")) {
            cString.remove(at: cString.startIndex)
        }

        if ((cString.count) != 6) {
            return UIColor.gray
        }

        var rgbValue:UInt32 = 0
        Scanner(string: cString).scanHexInt32(&rgbValue)

        return UIColor(
            red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
            green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
            blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
            alpha: CGFloat(1.0)
        )
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }

     }
Paresh Mangukiya
źródło
1

Użyłem metody convertPoint, aby pobrać punkt z widoku tabeli i przekazać ten punkt do metody indexPathForRowAtPoint, aby uzyskać indexPath

 @IBAction func newsButtonAction(sender: UIButton) {
        let buttonPosition = sender.convertPoint(CGPointZero, toView: self.newsTableView)
        let indexPath = self.newsTableView.indexPathForRowAtPoint(buttonPosition)
        if indexPath != nil {
            if indexPath?.row == 1{
                self.performSegueWithIdentifier("alertViewController", sender: self);
            }   
        }
    }
Avijit Nagare
źródło
1

Spróbuj użyć #selector, aby wywołać IBaction.In the cellforrowatindexpath

            cell.editButton.tag = indexPath.row
        cell.editButton.addTarget(self, action: #selector(editButtonPressed), for: .touchUpInside)

W ten sposób możesz uzyskać dostęp do indexpath wewnątrz metody editButtonPressed

func editButtonPressed(_ sender: UIButton) {

print(sender.tag)//this value will be same as indexpath.row

}
Vineeth Krishnan
źródło
Najbardziej odpowiednia odpowiedź
Amalendu Kar
Nie, tagi będą wyłączone, gdy użytkownik doda lub usunie komórki.
koen
1

W moim przypadku mam wiele sekcji i zarówno indeks sekcji, jak i wiersza są kluczowe, więc w takim przypadku właśnie utworzyłem właściwość w UIButton, w której ustawiłem indexPath komórki w następujący sposób:

fileprivate struct AssociatedKeys {
    static var index = 0
}

extension UIButton {

    var indexPath: IndexPath? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.index) as? IndexPath
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.index, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

Następnie ustaw właściwość w cellForRowAt w następujący sposób:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! Cell
    cell.button.indexPath = indexPath
}

Następnie w handleTapAction możesz pobrać indexPath w następujący sposób:

@objc func handleTapAction(_ sender: UIButton) {
    self.selectedIndex = sender.indexPath

}
DvixExtract
źródło
1

Swift 4 i 5

Metoda 1 przy użyciu delegata protokołu

Na przykład masz UITableViewCellz imieniemMyCell

class MyCell: UITableViewCell {
    
    var delegate:MyCellDelegate!
    
    @IBAction private func myAction(_ sender: UIButton){
        delegate.didPressButton(cell: self)
    }
}

Teraz utwórz plik protocol

protocol MyCellDelegate {
    func didPressButton(cell: UITableViewCell)
}

W następnym kroku utwórz rozszerzenie UITableView

extension UITableView {
    func returnIndexPath(cell: UITableViewCell) -> IndexPath?{
        guard let indexPath = self.indexPath(for: cell) else {
            return nil
        }
        return indexPath
    }
}

W swoim UIViewControllerzaimplementuj protokółMyCellDelegate

class ViewController: UIViewController, MyCellDelegate {
     
    func didPressButton(cell: UITableViewCell) {
        if let indexpath = self.myTableView.returnIndexPath(cell: cell) {
              print(indexpath)
        }
    }
}

Metoda 2 z użyciem domknięć

W UIViewController

override func viewDidLoad() {
        super.viewDidLoad()
       //using the same `UITableView extension` get the IndexPath here
        didPressButton = { cell in
            if let indexpath = self.myTableView.returnIndexPath(cell: cell) {
                  print(indexpath)
            }
        }
    }
 var didPressButton: ((UITableViewCell) -> Void)

class MyCell: UITableViewCell {

    @IBAction private func myAction(_ sender: UIButton){
        didPressButton(self)
    }
}

Uwaga: - jeśli chcesz uzyskać UICollectionViewindexPath, możesz użyć tego UICollectionView extensioni powtórzyć powyższe kroki

extension UICollectionView {
    func returnIndexPath(cell: UICollectionViewCell) -> IndexPath?{
        guard let indexPath = self.indexPath(for: cell) else {
            return nil
        }
        return indexPath
    }
}
Rashid Latif
źródło
0

W Swift 3. Użyto również instrukcji guard, unikając długiego łańcucha nawiasów.

func buttonTapped(sender: UIButton) {
    guard let cellInAction = sender.superview as? UITableViewCell else { return }
    guard let indexPath = tableView?.indexPath(for: cellInAction) else { return }

    print(indexPath)
}
Sean
źródło
To nie zadziała. Nadzór przycisku nie będzie komórką.
rmaddy
To działa. Należy tylko uważać, aby każdy stos widoku był inny. Może to być sender.superview, sender.superview.superview lub sender.superview.superview.superview. Ale działa naprawdę dobrze.
Sean,
0

Czasami przycisk może znajdować się wewnątrz innego widoku UITableViewCell. W takim przypadku superview.superview może nie dać obiektu komórki i dlatego indexPath będzie równa zero.

W takim przypadku powinniśmy szukać superwizji, aż otrzymamy obiekt komórki.

Funkcja pobierania obiektu komórki przez superview

func getCellForView(view:UIView) -> UITableViewCell?
{
    var superView = view.superview

    while superView != nil
    {
        if superView is UITableViewCell
        {
            return superView as? UITableViewCell
        }
        else
        {
            superView = superView?.superview
        }
    }

    return nil
}

Teraz możemy uzyskać indexPath po dotknięciu przycisku, jak poniżej

@IBAction func tapButton(_ sender: UIButton)
{
    let cell = getCellForView(view: sender)
    let indexPath = myTabelView.indexPath(for: cell)
}
Teena nath Paul
źródło
0
// CustomCell.swift

protocol CustomCellDelegate {
    func tapDeleteButton(at cell: CustomCell)
}

class CustomCell: UICollectionViewCell {
    
    var delegate: CustomCellDelegate?
    
    fileprivate let deleteButton: UIButton = {
        let button = UIButton(frame: .zero)
        button.setImage(UIImage(named: "delete"), for: .normal)
        button.addTarget(self, action: #selector(deleteButtonTapped(_:)), for: .touchUpInside)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    @objc fileprivate func deleteButtonTapped(_sender: UIButton) {
        delegate?.tapDeleteButton(at: self)
    }
    
}

//  ViewController.swift

extension ViewController: UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier, for: indexPath) as? CustomCell else {
            fatalError("Unexpected cell instead of CustomCell")
        }
        cell.delegate = self
        return cell
    }

}

extension ViewController: CustomCellDelegate {

    func tapDeleteButton(at cell: CustomCell) {
        // Here we get the indexPath of the cell what we tapped on.
        let indexPath = collectionView.indexPath(for: cell)
    }

}
iAleksandr
źródło