신속 : 셀의 버튼을 탭할 때 indexpath.row를 얻는 방법은 무엇입니까?
버튼이있는 tableview가 있고 그중 하나를 탭하면 indexpath.row를 사용하고 싶습니다. 이것은 내가 현재 가지고 있지만 항상 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는 그의 대답으로 거의 그것을 가지고 있었지만, 그는 세포에 여분의 contentView
층 이 있다는 사실을 간과했습니다 . 따라서 우리는 한 층 더 깊이 가야합니다.
guard let cell = sender.superview?.superview as? YourCellClassHere else {
return // or fatalError() or whatever
}
let indexPath = itemTable.indexPath(for: cell)
이는 뷰 계층 구조 내에서 tableView가 자체 '컨텐츠 뷰'를 갖는 하위 뷰로 셀을 가지고 있기 때문에 셀 자체를 가져 오려면이 콘텐츠 뷰의 수퍼 뷰를 가져와야합니다. 결과적으로 버튼이 셀의 콘텐츠보기에 직접 들어가는 것이 아니라 하위보기에 포함되어있는 경우 액세스하려면 더 많은 레이어로 이동해야합니다.
위의 방법은 그러한 접근 방법 중 하나이지만 반드시 최선의 방법은 아닙니다. 기능적이지만 UITableViewCell
뷰 계층 구조와 같이 Apple이 반드시 문서화하지 않은 에 대한 세부 정보를 가정 합니다. 이것은 나중에 변경 될 수 있으며 위의 코드는 결과적으로 예상치 않게 작동 할 수 있습니다.
위의 결과, 수명과 신뢰성을 위해 다른 접근 방식을 채택하는 것이 좋습니다. 이 스레드에는 여러 가지 대안이 나열되어 있으며 읽어보실 것을 권장하지만 개인적으로 가장 좋아하는 것은 다음과 같습니다.
셀 클래스에 클로저의 속성을 유지하고 버튼의 액션 메서드가 이것을 호출하도록합니다.
class MyCell: UITableViewCell {
var button: UIButton!
var buttonAction: ((Any) -> Void)?
@objc func buttonPressed(sender: Any) {
self.buttonAction?(sender)
}
}
그런 다음에서 셀을 만들 때 cellForRowAtIndexPath
클로저에 값을 할당 할 수 있습니다.
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.
}
여기로 핸들러 코드를 이동하면 이미 존재하는 indexPath
인수를 활용할 수 있습니다 . 이것은 문서화되지 않은 특성에 의존하지 않기 때문에 위에 나열된 것보다 훨씬 안전한 접근 방식입니다.
업데이트 : 버튼이 포함 된 셀의 indexPath 가져 오기 (섹션과 행 모두) :
버튼 위치 사용
buttonTapped
메서드 내 에서 버튼의 위치를 잡고 tableView의 좌표로 변환 한 다음 해당 좌표에서 행의 indexPath를 가져올 수 있습니다.
func buttonTapped(_ sender:AnyObject) {
let buttonPosition:CGPoint = sender.convert(CGPoint.zero, to:self.tableView)
let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
}
참고 : 때로는 tableView 셀이 있더라도 함수를 사용하여 한 지점에서 행을 view.convert(CGPointZero, to:self.tableView)
찾을 때 가장자리 케이스가 nil
발생할 수 있습니다. 이 문제를 해결하려면 다음과 같이 원점에서 약간 오프셋 된 실제 좌표를 전달해보십시오.
let buttonPosition:CGPoint = sender.convert(CGPoint.init(x: 5.0, y: 5.0), to:self.tableView)
이전 답변 : 태그 속성 사용 (행만 반환)
UIButton이있는 셀에 대한 포인터를 잡기 위해 superview 트리로 올라가는 대신 위에 Antonio가 언급 한 button.tag 속성을 활용하는 더 안전하고 반복 가능한 기술 이 있습니다.
에서 cellForRowAtIndexPath:
당신 태그의 속성을 설정합니다 :
button.tag = indexPath.row
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
그런 다음 buttonClicked:
함수에서 해당 태그를 참조하여 버튼이있는 indexPath의 행을 가져옵니다.
func buttonClicked(sender:UIButton) {
let buttonRow = sender.tag
}
수퍼 뷰 트리에서 스윙하는 것이 앱을 디자인하는 위험한 방법이라는 것을 알았 기 때문에이 방법을 선호합니다. 또한 Objective-C의 경우 이전 에이 기술 을 사용해 왔으며 결과에 만족했습니다.
이런 종류의 문제에 대한 나의 접근 방식은 셀과 테이블 뷰 사이에 위임 프로토콜을 사용하는 것입니다. 이를 통해 버튼 핸들러를 셀 서브 클래스에 유지할 수 있습니다.이를 통해 터치 업 액션 핸들러를 Interface Builder의 프로토 타입 셀에 할당하는 동시에 뷰 컨트롤러에서 버튼 핸들러 로직을 유지할 수 있습니다.
또한 tag
셀 인덱스가 변경 될 때 (삽입, 삭제 또는 재정렬로 인해) 문제가 발생 하는 뷰 계층 구조를 탐색하거나 속성을 사용하는 잠재적으로 취약한 접근 방식을 방지합니다.
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)")
}
}
UITableView에 대한 확장을 사용하여 모든보기의 셀을 가져옵니다.
@ Paulw11의 답변은 테이블보기로 메시지를 보내는 대리자 속성을 사용하여 사용자 지정 셀 유형을 설정하는 것이 좋은 방법이지만 설정하려면 일정량의 작업이 필요합니다.
셀을 찾는 테이블 뷰 셀의 뷰 계층 구조를 걷는 것은 나쁜 생각이라고 생각합니다. 취약합니다. 나중에 레이아웃 목적으로보기에 버튼을 포함하면 해당 코드가 깨질 수 있습니다.
뷰 태그를 사용하는 것도 취약합니다. 셀을 만들 때 태그를 설정하는 것을 기억해야하며, 다른 용도로 뷰 태그를 사용하는 뷰 컨트롤러에서 해당 접근 방식을 사용하는 경우 중복 태그 번호를 가질 수 있으며 코드가 예상대로 작동하지 않을 수 있습니다.
테이블 뷰 셀에 포함 된 모든 뷰에 대한 indexPath를 가져올 수있는 UITableView 확장을 만들었습니다. Optional
전달 된 뷰가 실제로 테이블 뷰 셀에 속하지 않으면 nil을 반환합니다 . 아래는 전체 확장 소스 파일입니다. 이 파일을 프로젝트에 넣은 다음 포함 된 indexPathForView(_:)
메서드를 사용하여 뷰가 포함 된 indexPath를 찾을 수 있습니다.
//
// 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
}
}
이를 사용하려면 셀에 포함 된 버튼에 대해 IBAction에서 메서드를 호출하기 만하면됩니다.
func buttonTapped(_ button: UIButton) {
if let indexPath = self.tableView.indexPathForView(button) {
print("Button tapped at indexPath \(indexPath)")
}
else {
print("Button indexPath not found")
}
}
(이 indexPathForView(_:)
함수는 전달 된 뷰 객체가 현재 화면에있는 셀에 포함 된 경우에만 작동합니다. 화면에없는 뷰는 실제로 특정 indexPath에 속하지 않기 때문에 합리적입니다. 셀이 재활용 될 때 다른 indexPath에 할당됩니다.)
편집하다:
Github에서 위의 확장을 사용하는 작동중인 데모 프로젝트를 다운로드 할 수 있습니다. TableViewExtension.git
에 대한 Swift2.1
나는 그것을 할 방법을 찾았습니다. 도움이 되길 바랍니다.
let point = tableView.convertPoint(CGPoint.zero, fromView: sender)
guard let indexPath = tableView.indexPathForRowAtPoint(point) else {
fatalError("can't find point in tableView")
}
Swift 4 솔루션 :
셀에 단추 (myButton) 또는 다른보기가 있습니다. 다음과 같이 cellForRowAt에 태그를 할당하십시오.
cell.myButton.tag = indexPath.row
이제 탭 함수 또는 기타. 이렇게 가져 와서 지역 변수에 저장하십시오.
currentCellNumber = (sender.view?.tag)!
이 후이 currentCellNumber를 사용하여 선택한 단추의 indexPath.row를 가져올 수 있습니다.
즐겨!
Swift 4에서는 다음을 사용하십시오.
func buttonTapped(_ sender: UIButton) {
let buttonPostion = sender.convert(sender.bounds.origin, to: tableView)
if let indexPath = tableView.indexPathForRow(at: buttonPostion) {
let rowIndex = indexPath.row
}
}
이벤트 핸들러의 발신자는 버튼 자체이기 때문에 버튼의 tag
속성을 사용하여 cellForRowAtIndexPath
.
하지만 조금 더 작업하면 완전히 다른 방식으로 할 수 있습니다. 사용자 지정 셀을 사용하는 경우 다음과 같이 문제에 접근 할 수 있습니다.
- 사용자 정의 테이블 셀에 'indexPath'속성 추가
- 그것을 초기화
cellForRowAtIndexPath
- 탭 핸들러를 뷰 컨트롤러에서 셀 구현으로 이동
- 위임 패턴을 사용하여 탭 이벤트에 대해 뷰 컨트롤러에 알리고 인덱스 경로를 전달합니다.
델리게이트 콜백 사용에 대한 Paulw11의 제안을 본 후, 약간 자세히 설명하거나 유사한 다른 제안을 전달하고 싶었습니다. 델리게이트 패턴을 사용하지 않으려면 다음과 같이 신속하게 클로저를 사용할 수 있습니다.
세포 등급 :
class Cell: UITableViewCell {
@IBOutlet var button: UIButton!
var buttonAction: ((sender: AnyObject) -> Void)?
@IBAction func buttonPressed(sender: AnyObject) {
self.buttonAction?(sender)
}
}
귀하의 cellForRowAtIndexPath
방법 :
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.
}
#selector를 사용하여 IBaction을 호출 해보십시오.
cell.editButton.tag = indexPath.row
cell.editButton.addTarget(self, action: #selector(editButtonPressed), for: .touchUpInside)
이렇게하면 editButtonPressed 메소드 내에서 indexpath에 액세스 할 수 있습니다.
func editButtonPressed(_ sender: UIButton) {
print(sender.tag)//this value will be same as indexpath.row
}
나는 convertPoint 메소드를 사용하여 tableview에서 포인트를 얻고이 포인트를 indexPathForRowAtPoint 메소드에 전달하여 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);
}
}
}
Swift 3에서는 긴 중괄호 체인을 피하면서 guard 문도 사용했습니다.
func buttonTapped(sender: UIButton) {
guard let cellInAction = sender.superview as? UITableViewCell else { return }
guard let indexPath = tableView?.indexPath(for: cellInAction) else { return }
print(indexPath)
}
때로는 버튼이 UITableViewCell의 다른 뷰 안에있을 수 있습니다. 이 경우 superview.superview는 셀 객체를 제공하지 않을 수 있으므로 indexPath는 nil이됩니다.
이 경우 셀 객체를 얻을 때까지 수퍼 뷰를 계속 찾아야합니다.
슈퍼 뷰로 셀 객체를 얻는 기능
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
}
이제 아래와 같이 버튼 탭에서 indexPath를 얻을 수 있습니다.
@IBAction func tapButton(_ sender: UIButton)
{
let cell = getCellForView(view: sender)
let indexPath = myTabelView.indexPath(for: cell)
}
Model 클래스를 사용하여 tableView 및 collectionView의 모든 셀을 관리하는 데 매우 쉽고 능숙한 방법을 찾았으며 이것은 완벽하게 작동합니다.
실제로 이것을 처리하는 훨씬 더 좋은 방법이 있습니다. 이것은 셀과 값을 관리하는 데 효과적입니다.
여기 내 코드입니다
- 모델 clas 를 만드는 것은 매우 간단 합니다. 아래 절차를 따르십시오. "RNCheckedModel"이라는 이름으로 신속한 클래스를 생성하고 아래와 같이 코드를 작성합니다.
class RNCheckedModel : NSObject {
var is_check = false
var user_name = ""
}
- 셀 클래스 만들기
class InviteCell : UITableViewCell {
@IBOutlet var imgProfileImage: UIImageView!
@IBOutlet var btnCheck: UIButton!
@IBOutlet var lblName: UILabel!
@IBOutlet var lblEmail: UILabel!
}
- 마지막으로 UITableView 를 사용할 때 UIViewController 에서 모델 클래스 를 사용하십시오 .
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()
}
}
'program tip' 카테고리의 다른 글
자바에서 로그인 사용자 이름 가져 오기 (0) | 2020.09.20 |
---|---|
div의 배경 이미지 URL을 얻을 수 있습니까? (0) | 2020.09.20 |
조각이있는 작업 표시 줄 위로 탐색 (0) | 2020.09.20 |
"abcd".StartsWith ( "")가 true를 반환하는 이유는 무엇입니까? (0) | 2020.09.20 |
Android : 프로그래밍 방식으로 옵션 메뉴 항목 변경 (0) | 2020.09.20 |