Saya memiliki tableview dengan tombol dan saya ingin menggunakan indexpath.turut ketika salah satu dari mereka disadap. Ini adalah apa yang saya miliki saat ini, tapi itu selalu adalah 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)
}
giorashc hampir memilikinya dengan jawabannya, tapi ia mengabaikan fakta bahwa sel's memiliki tambahan contentView
layer. Dengan demikian, kami harus pergi satu lapisan yang lebih dalam:
guard let cell = sender.superview?.superview as? YourCellClassHere else {
return // or fatalError() or whatever
}
let indexPath = itemTable.indexPath(for: cell)
Hal ini karena dalam tampilan hierarki tableView memiliki sel-sel sebagai subviews yang kemudian telah mereka sendiri 'konten pandangan' ini adalah mengapa anda harus mendapatkan superview konten ini lihat untuk mendapatkan sel itu sendiri. Sebagai hasil dari ini, jika anda tombol yang terkandung dalam subview daripada langsung ke dalam sel's konten yang melihat, kau'll harus pergi namun banyak lapisan yang lebih dalam untuk mengaksesnya.
Di atas adalah salah satu pendekatan tersebut, tetapi belum tentu pendekatan yang terbaik. Sementara itu fungsional, ia menganggap rincian tentang UITableViewCell
bahwa Apple tidak pernah harus didokumentasikan, seperti itu's melihat hirarki. Ini bisa berubah di masa depan, dan kode di atas mungkin berperilaku tak terduga sebagai akibat.
Sebagai akibat dari hal di atas, untuk umur panjang dan kehandalan alasan, saya sarankan mengadopsi pendekatan lain. Ada banyak alternatif yang tercantum di thread ini, dan saya mendorong anda untuk membaca ke bawah, tapi favorit saya pribadi adalah sebagai berikut:
Mengadakan properti penutupan pada sel kelas, memiliki tombol's metode action seru ini.
class MyCell: UITableViewCell {
var button: UIButton!
var buttonAction: ((Any) -> Void)?
@objc func buttonPressed(sender: Any) {
self.buttonAction?(sender)
}
}
Kemudian, ketika anda membuat sel dalam cellForRowAtIndexPath
, anda dapat menetapkan nilai ke penutupan.
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.
}
Dengan memindahkan handler code berikut, anda dapat mengambil keuntungan dari yang sudah ada indexPath
argumen. Ini adalah banyak pendekatan yang lebih aman itu salah satu yang tercantum di atas itu doesn't bergantung pada berdokumen ciri-ciri.
Pendekatan saya untuk masalah seperti ini adalah dengan menggunakan mendelegasikan protokol antara sel dan tableview. Hal ini memungkinkan anda untuk menjaga tombol handler dalam sel subclass, yang memungkinkan anda untuk menetapkan touch up action handler untuk prototipe sel dalam Interface Builder, sementara masih menjaga tombol pengendali logika dalam view controller.
Hal ini juga menghindari berpotensi rapuh pendekatan navigasi hirarki tampilan atau penggunaan tag
properti, yang memiliki masalah ketika sel-sel indeks perubahan (sebagai hasil dari penyisipan, penghapusan, atau penataan kembali)
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)")
}
}
UPDATE: Mendapatkan indexPath dari sel yang berisi tombol (bagian kedua dan baris):
Menggunakan Posisi Tombol
Dalam buttonTapped
metode, anda bisa ambil tombol's posisi, mengkonversi ke koordinat di tableView, kemudian mendapatkan indexPath baris pada koordinat.
func buttonTapped(_ sender:AnyObject) {
let buttonPosition:CGPoint = sender.convert(CGPoint.zero, to:self.tableView)
let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
}
CATATAN: kadang-Kadang anda bisa lari ke tepi kasus ketika menggunakan fungsi melihat.mengkonversi(CGPointZero, untuk:mandiri.tableView)
hasil dalam menemukan nihil
untuk sebuah baris pada suatu titik, meskipun ada tableView sel yang ada. Untuk memperbaiki ini, mencoba melewati koordinat yang sedikit offset dari asal, seperti:
let buttonPosition:CGPoint = sender.convert(CGPoint.init(x: 5.0, y: 5.0), to:self.tableView)
Jawaban sebelumnya: Menggunakan Tag Properti (hanya mengembalikan row)
Daripada naik ke superview pohon untuk mengambil pointer ke sel yang memegang UIButton, ada yang lebih aman, lebih berulang teknik memanfaatkan tombol.tag properti yang disebutkan oleh Antonio di atas, dijelaskan dalam ini answer, dan yang ditunjukkan di bawah ini:
Di cellForRowAtIndexPath:
anda mengatur tag property:
button.tag = indexPath.row
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
Kemudian, dalam buttonClicked:
fungsi, anda referensi yang tag untuk mengambil baris dari indexPath mana tombol ini terletak:
func buttonClicked(sender:UIButton) {
let buttonRow = sender.tag
}
Aku lebih memilih metode ini karena saya've menemukan bahwa berayun di superview pohon dapat menjadi cara yang berisiko untuk merancang sebuah aplikasi. Juga, untuk tujuan-C I've digunakan teknik ini di masa lalu dan telah senang dengan hasilnya.
@Paulw11's jawaban dari pengaturan kustom jenis sel dengan delegasi properti yang mengirimkan pesan ke tampilan tabel adalah cara yang baik untuk pergi, tetapi membutuhkan jumlah tertentu bekerja untuk mengatur.
Saya pikir berjalan table view sel's melihat hirarki mencari sel adalah ide yang buruk. Itu rapuh - jika anda kemudian melampirkan tombol anda dalam tampilan untuk tujuan tata letak, kode itu adalah mungkin untuk istirahat.
Menggunakan tampilan kategori ini juga rapuh. Anda harus ingat untuk mengatur kategori ketika anda membuat sel, dan jika anda menggunakan pendekatan tersebut dalam view controller yang menggunakan tampilan kategori untuk tujuan lain anda dapat memiliki tag duplikat nomor dan kode anda dapat gagal untuk bekerja seperti yang diharapkan.
Saya telah menciptakan sebuah ekstensi untuk UITableView yang memungkinkan anda mendapatkan indexPath untuk setiap tampilan yang terkandung dalam tampilan tabel sel. Mengembalikan sebuah Pilihan
yang akan menjadi nihil jika melihat lulus dalam benar-benar tidak jatuh dalam tampilan tabel sel. Di bawah ini adalah ekstensi file sumber dalam itu's keseluruhan. Anda hanya dapat menempatkan file ini dalam proyek anda dan kemudian gunakan termasuk indexPathForView(_:)
metode untuk menemukan indexPath yang berisi tampilan apapun.
//
// 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
}
}
Untuk menggunakannya, anda hanya dapat memanggil metode dalam IBAction untuk tombol yang's yang terkandung dalam sel:
func buttonTapped(_ button: UIButton) {
if let indexPath = self.tableView.indexPathForView(button) {
print("Button tapped at indexPath \(indexPath)")
}
else {
print("Button indexPath not found")
}
}
(Perhatikan bahwa indexPathForView(_:)
fungsi hanya akan bekerja jika melihat objek itu's berlalu terkandung oleh sel yang's saat ini pada layar. Yang's yang wajar, karena pandangan yang tidak pada layar doesn't benar-benar milik tertentu indexPath; it's kemungkinan akan ditugaskan ke berbagai indexPath ketika itu's mengandung sel daur ulang.)
Anda dapat men-download demo kerja proyek yang menggunakan ekstensi atas dari Github: TableViewExtension.git
Untuk Swift2.1
Saya menemukan cara untuk melakukan itu, mudah-mudahan, itu'll membantu.
let point = tableView.convertPoint(CGPoint.zero, fromView: sender)
guard let indexPath = tableView.indexPathForRowAtPoint(point) else {
fatalError("can't find point in tableView")
}
Swift 4 Solusi:
Anda memiliki tombol (myButton) atau lainnya lihat di sel. Menetapkan tag di cellForRowAt seperti ini
cell.myButton.tag = indexPath.row
Sekarang anda tapFunction atau lainnya. Mengambil keluar seperti ini dan menyimpannya dalam variabel lokal.
currentCellNumber = (sender.view?.tag)!
Setelah ini, anda dapat menggunakan di mana saja ini currentCellNumber untuk mendapatkan indexPath.baris yang dipilih.
Nikmati!
Di Swift 4 , gunakan ini:
func buttonTapped(_ sender: UIButton) {
let buttonPostion = sender.convert(sender.bounds.origin, to: tableView)
if let indexPath = tableView.indexPathForRow(at: buttonPostion) {
let rowIndex = indexPath.row
}
}
Sejak pengirim event handler adalah tombol itu sendiri, saya'd gunakan tombol's tag
properti untuk menyimpan indeks, diinisialisasi dalam cellForRowAtIndexPath
.
Tapi dengan sedikit lebih banyak pekerjaan saya'd lakukan dalam cara yang sama sekali berbeda. Jika anda menggunakan custom sel, ini adalah bagaimana aku akan mendekati masalah:
cellForRowAtIndexPath
Setelah melihat Paulw11's saran dari menggunakan delegasi callback, saya ingin menguraikan sedikit/mengedepankan lain, mirip saran. Jika anda tidak ingin menggunakan delegasi pola anda dapat memanfaatkan penutupan di swift sebagai berikut:
Ponsel kelas:
class Cell: UITableViewCell {
@IBOutlet var button: UIButton!
var buttonAction: ((sender: AnyObject) -> Void)?
@IBAction func buttonPressed(sender: AnyObject) {
self.buttonAction?(sender)
}
}
Anda cellForRowAtIndexPath
metode:
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.
}
Saya menemukan sangat mudah dan saficiat cara yang digunakan untuk mengelola setiap sel di tableView dan collectionView dengan menggunakan Model kelas dan ini bekerja dengan sempurna.
Memang ada banyak cara yang lebih baik untuk menangani hal ini sekarang. Ini akan bekerja untuk mengelola sel dan nilai
berikut adalah output(screenshote) jadi melihat ini
berikut adalah kode
kelas RNCheckedModel: NSObject {
var is_check = false
var user_name = ""
}
kelas InviteCell: UITableViewCell {
@IBOutlet var imgProfileImage: UIImageView!
@IBOutlet var btnCheck: UIButton!
@IBOutlet var lblName: UILabel!
@IBOutlet var lblEmail: UILabel!
}
kelas 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()
}
}
Cobalah menggunakan #pemilih untuk memanggil IBaction.Di cellforrowatindexpath
cell.editButton.tag = indexPath.row
cell.editButton.addTarget(self, action: #selector(editButtonPressed), for: .touchUpInside)
Dengan cara ini anda dapat mengakses indexpath dalam metode editButtonPressed
func editButtonPressed(_ sender: UIButton) {
print(sender.tag)//this value will be same as indexpath.row
}
Aku digunakan convertPoint metode untuk mendapatkan point dari tableview dan melewati titik ini untuk indexPathForRowAtPoint metode untuk mendapatkan 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);
}
}
}
Di Swift 3. Juga digunakan penjaga pernyataan, menghindari rantai panjang kawat gigi.
func buttonTapped(sender: UIButton) {
guard let cellInAction = sender.superview as? UITableViewCell else { return }
guard let indexPath = tableView?.indexPath(for: cellInAction) else { return }
print(indexPath)
}
Kadang-kadang tombol mungkin dalam pandangan lain dari UITableViewCell. Dalam kasus itu superview.superview mungkin tidak memberikan sel objek dan karenanya indexPath akan nihil.
Dalam hal ini kita harus tetap mencari superview sampai kita mendapatkan sel objek.
Fungsi untuk mendapatkan sel objek dengan 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
}
Sekarang kita bisa mendapatkan indexPath di tekan tombol seperti di bawah ini
@IBAction func tapButton(_ sender: UIButton)
{
let cell = getCellForView(view: sender)
let indexPath = myTabelView.indexPath(for: cell)
}