วิธีรับ UITableView จาก UITableViewCell


100

ฉันมีสิ่งUITableViewCellที่เชื่อมโยงกับวัตถุและฉันต้องการบอกว่าเซลล์นั้นมองเห็นได้หรือไม่ จากการวิจัยที่ฉันได้ทำหมายความว่าฉันจำเป็นต้องเข้าถึงสิ่งUITableViewที่มีอยู่ (จากที่นั่นมีหลายวิธีในการตรวจสอบว่ามองเห็นได้หรือไม่) ฉันจึงสงสัยว่าUITableViewCellมีตัวชี้ไปที่UITableViewหรือมีวิธีอื่นในการดึงตัวชี้จากเซลล์หรือไม่?


2
จุดประสงค์ของสิ่งนี้คืออะไร?
max_

[cell superView]อาจจะ?
Chris Loonam

6
มันคุ้มค่าที่จะอธิบายว่าทำไมคุณถึงคิดว่าคุณต้องการสิ่งนี้เนื่องจากนี่อาจเป็นสัญญาณของการออกแบบที่ไม่ดีเนื่องจากฉันไม่สามารถคิดถึงเหตุผลที่ถูกต้องมากมายที่เซลล์จะรู้ได้ว่ามันอยู่บนหน้าจอหรือไม่
ปอ.

@ Paul.s เรามีเครื่องมือจดจำท่าทางบนรูปภาพในเซลล์และเมื่อสัมผัสเซลล์มันจะเปิดมุมมองภาพซ้อนทับขึ้นมาอีกแบบคิดว่าสไตล์ป็อปโอเวอร์ซึ่งควรซ้อนทับเซลล์ให้มากที่สุดเท่าที่จำเป็นเพื่อให้แสดงได้อย่างถูกต้อง เพื่อให้สามารถใช้งานได้ต้องใช้ TableView หรือมุมมองอื่น ๆ ที่กำหนดให้เพื่อแสดงผลไม่พอใจกับโซลูชัน แต่เพื่อให้ได้เอฟเฟกต์ที่ต้องการเพื่อให้ได้ UITableView ของ UITableViewCell เป็นสิ่งที่ดีที่สุดที่เรามี
chadbag

1
@chadbag ไม่ต้องกังวลหวังว่าฉันจะให้ความคิดกับคนอื่นที่มีปัญหาเดียวกัน
PJ_Finnegan

คำตอบ:


153

เพื่อหลีกเลี่ยงการตรวจสอบเวอร์ชัน iOS ให้เดินขึ้นไปบนซูเปอร์วิวซ้ำ ๆ จากมุมมองของเซลล์จนกว่าจะพบ UITableView:

id view = [tableViewCellInstance superview];

while (view && [view isKindOfClass:[UITableView class]] == NO) {
    view = [view superview]; 
}

UITableView *tableView = (UITableView *)view;

1
ขอบคุณ. ดูเหมือนว่าจะมีการเปลี่ยนแปลงอีกครั้งใน iOS 8 และสิ่งนี้จะดูแลทุกเวอร์ชันอย่างดี
djskinner

2
ฉันอยากจะแนะนำให้เพิ่มการอ้างอิงที่อ่อนแอไปยัง tableview ลงในเซลล์เพื่อหลีกเลี่ยงปัญหาความเข้ากันได้ในการอัปเดตในอนาคต
Cenny

1
ค่อนข้างเป็นวิธีที่อ่อนแอ จะใช้ไม่ได้หากลำดับชั้นของมุมมองมีการเปลี่ยนแปลงในอนาคต
RomanN

2
มันจะดีกว่าถ้าเพียงสร้างคุณสมบัติที่อ่อนแอซึ่งเป็นตัวชี้ไปที่มุมมองตาราง ในประเภทรอง@property (weak, nonatomic) UITableView *tableView;และในเพียงชุดtableView:cellForRowAtIndexPath: cell.tableView = tableView;
Alejandro Iván

ประสบการณ์ปัจจุบันคือหลังจากเลิกจัดคิวเซลล์แล้ว tableview จะไม่ปรากฏเป็น superview ของเซลล์ทันที - ถ้าฉันเลื่อนเซลล์ออกจากมุมมองและกลับเข้ามาอีกครั้งฉันจะพบมุมมองตารางเป็น superview (การค้นหาซ้ำตามที่อธิบายไว้ใน คำตอบอื่น ๆ ) ระบบจ่ายอัตโนมัติและอาจมีปัจจัยอื่น ๆ ที่เป็นไปได้
จอนนี่

48

ใน iOS7 beta 5 UITableViewWrapperViewคือsuperview ของไฟล์UITableViewCell. นอกจากนี้ยังUITableViewเป็น superview ของไฟล์UITableViewWrapperView.

ดังนั้นสำหรับ iOS 7 วิธีแก้ปัญหาคือ

UITableView *tableView = (UITableView *)cell.superview.superview;

ดังนั้นสำหรับ iOSes จนถึง iOS 6 วิธีแก้ปัญหาคือ

UITableView *tableView = (UITableView *)cell.superview;


5
โอ้มนุษย์นี่คือการเปลี่ยนแปลง API ที่โหดร้าย แนวทางปฏิบัติที่ดีที่สุดของ Apple ในการแตกกิ่งก้านคืออะไรหากคุณรองรับทั้ง 6 และ 7
Ryan Romanchuk

@RyanRomanchuk นี่คือคำแนะนำที่ดีอย่างหนึ่ง: devforums.apple.com/message/865550#865550 - สร้างตัวชี้ที่อ่อนแอไปยัง tableView ที่เกี่ยวข้องของคุณเมื่อเซลล์ถูกสร้างขึ้น อีกวิธีหนึ่งคือสร้างUITableViewCellหมวดหมู่ด้วยวิธีการใหม่relatedTableViewซึ่งจะตรวจสอบเวอร์ชัน iOS และส่งคืน SuperView ที่เหมาะสม
memmons

3
เขียนหมวดหมู่แบบวนซ้ำใน UIView สำหรับสิ่งนี้ คุณไม่จำเป็นต้องตรวจสอบเวอร์ชัน เพียงโทรหา superview จนกว่าคุณจะพบเซลล์มุมมองตารางหรือด้านบนสุดของสแต็กมุมมอง นี่คือสิ่งที่ฉันใช้ใน iOS 6 และมันทำงานได้โดยไม่ต้องดัดแปลงใน iOS 7 และควรใช้งานไซต์ใน iOS 8
Steven Fisher

ขอประทานโทษ; ฉันหมายถึงจนกว่าคุณจะพบมุมมองตาราง :)
Steven Fisher

39

ส่วนขยาย Swift 5

วนซ้ำ

extension UIView {
    func parentView<T: UIView>(of type: T.Type) -> T? {
        guard let view = superview else {
            return nil
        } 
        return (view as? T) ?? view.parentView(of: T.self)
    }
}

extension UITableViewCell {
    var tableView: UITableView? {
        return parentView(of: UITableView.self)
    }
}

ใช้ลูป

extension UITableViewCell {
    var tableView: UITableView? {
        var view = superview
        while let v = view, v.isKind(of: UITableView.self) == false {
            view = v.superview
        }
        return view as? UITableView
    }
}

กฎทั่วไปคือห้ามใช้กำลังในการแกะ
thibaut noah

1
@thibautnoah มีการview != nilตรวจสอบก่อนแกะกล่อง อัปเดตเป็นรหัสที่สะอาดกว่า
Orkhan Alikhanov

25

ก่อน iOS7 ซูเปอร์วิวของเซลล์คือสิ่งUITableViewที่มีอยู่ ในฐานะของ iOS7 จีเอ็ม (เพื่อให้สันนิษฐานว่าจะเป็นในรุ่นประชาชนเช่นกัน) SuperView ของเซลล์เป็นUITableViewWrapperViewกับ SuperView UITableViewของการเป็น วิธีแก้ปัญหามีสองวิธี

โซลูชัน # 1: สร้างUITableViewCellหมวดหมู่

@implementation UITableViewCell (RelatedTable)

- (UITableView *)relatedTable
{
    if ([self.superview isKindOfClass:[UITableView class]])
        return (UITableView *)self.superview;
    else if ([self.superview.superview isKindOfClass:[UITableView class]])
        return (UITableView *)self.superview.superview;
    else
    {
        NSAssert(NO, @"UITableView shall always be found.");
        return nil;
    }

}
@end

นี่คือการแทนที่แบบดรอปอินที่ดีในการใช้cell.superviewทำให้ง่ายต่อการ refactor โค้ดที่มีอยู่ของคุณเพียงแค่ค้นหาและแทนที่ด้วย[cell relatedTable]และยืนยันเพื่อให้แน่ใจว่าหากลำดับชั้นของการดูเปลี่ยนแปลงหรือย้อนกลับในอนาคตจะแสดงขึ้นทันที ในการทดสอบของคุณ

โซลูชัน # 2: เพิ่มการUITableViewอ้างอิงที่อ่อนแอลงในUITableViewCell

@interface SOUITableViewCell

   @property (weak, nonatomic) UITableView *tableView;

@end

นี่เป็นการออกแบบที่ดีกว่ามากแม้ว่าจะต้องใช้การปรับโครงสร้างโค้ดอีกเล็กน้อยเพื่อใช้ในโครงการที่มีอยู่ ในการtableView:cellForRowAtIndexPathใช้ SOUITableViewCell ของคุณเป็นคลาสเซลล์ของคุณหรือตรวจสอบให้แน่ใจว่าคลาสเซลล์ที่กำหนดเองของคุณเป็นคลาสย่อยจากSOUITableViewCellและกำหนด tableView ให้กับคุณสมบัติ tableView ของเซลล์ ภายในเซลล์แล้วคุณสามารถดูที่ tableview self.tableViewที่มีการใช้


ฉันไม่เห็นด้วยที่โซลูชัน 2 เป็นการออกแบบที่ดีกว่า มีจุดล้มเหลวมากขึ้นและต้องการการแทรกแซงด้วยตนเองที่มีวินัย โซลูชันแรกค่อนข้างน่าเชื่อถือแม้ว่าฉันจะใช้มันตามที่ @idris แนะนำในคำตอบของเขาเพื่อการป้องกันในอนาคตอีกเล็กน้อย
devios1

1
การทดสอบ[self.superview.superview isKindOfClass:[UITableView class]]ควรเป็นครั้งแรกเนื่องจาก iOS 7 มีมากขึ้นเรื่อย ๆ
CopperCash

7

หากมองเห็นได้แสดงว่ามี superview และ ... แปลกใจ ... superview เป็นวัตถุ UITableView

อย่างไรก็ตามการมีซูเปอร์วิวไม่สามารถรับประกันได้ว่าจะปรากฏบนหน้าจอ แต่ UITableView มีวิธีการตรวจสอบว่าเซลล์ใดสามารถมองเห็นได้

และไม่ไม่มีการอ้างอิงเฉพาะจากเซลล์ไปยังตาราง แต่เมื่อคุณ subclass UITableViewCell คุณอาจแนะนำหนึ่งและตั้งค่าเมื่อสร้าง (ฉันทำแบบนั้นมามากก่อนที่ฉันจะคิดถึงลำดับชั้นมุมมองย่อย)

อัปเดตสำหรับ iOS7: Apple ได้เปลี่ยนลำดับชั้นมุมมองย่อยที่นี่ ตามปกติเมื่อทำงานกับสิ่งที่ไม่มีการบันทึกรายละเอียดมีความเสี่ยงที่สิ่งต่างๆจะเปลี่ยนไป การ "รวบรวมข้อมูล" ตามลำดับชั้นของมุมมองนั้นประหยัดได้มากจนกว่าจะพบวัตถุ UITableView ในที่สุด


14
สิ่งนี้ไม่เป็นความจริงอีกต่อไปใน iOS7 ใน iOS7 beta5 UITableViewWrapperView เป็น superview ของ UITableViewCell ... ทำให้ฉันมีปัญหาในตอนนี้
Gabe

ขอบคุณสำหรับความคิดเห็น ฉันจะต้องตรวจสอบรหัสของฉันแล้ว
Hermann Klecker

7

โซลูชัน Swift 2.2

ส่วนขยายสำหรับ UIView ที่ค้นหามุมมองซ้ำด้วยประเภทเฉพาะ

import UIKit

extension UIView {
    func lookForSuperviewOfType<T: UIView>(type: T.Type) -> T? {
        guard let view = self.superview as? T else {
            return self.superview?.lookForSuperviewOfType(type)
        }
        return view
    }
}

หรือกะทัดรัดกว่า (ขอบคุณ kabiroberai):

import UIKit

extension UIView {
    func lookForSuperviewOfType<T: UIView>(type: T.Type) -> T? {
        return superview as? T ?? superview?.superviewOfType(type)
    }
}

ในเซลล์ของคุณคุณเรียกมันว่า:

let tableView = self.lookForSuperviewOfType(UITableView)
// Here we go

โปรดทราบว่า UITableViewCell จะถูกเพิ่มใน UITableView หลังจากการเรียกใช้ cellForRowAtIndexPath เท่านั้น


คุณสามารถกระชับlookForSuperviewOfType:เนื้อหาวิธีการทั้งหมดให้เป็นบรรทัดเดียวทำให้รวดเร็วยิ่งขึ้น:return superview as? T ?? superview?.superviewOfType(type)
kabiroberai

@kabiroberai ขอบคุณค่ะ. ฉันเพิ่มคำแนะนำของคุณในคำตอบ
Mehdzor

ชื่อที่ใช้โดยการเรียกซ้ำต้องตรงกัน สิ่งนี้ต้องอ่าน: ... ?? superview? .lookForSuperviewOfType (ชนิด)
เบิร์

7

ไม่ว่าคุณจะจัดการกับสิ่งใดโดยการโทรหา super view หรือผ่านเครือข่ายการตอบกลับนั้นจะเปราะบางมาก วิธีที่ดีที่สุดในการทำเช่นนี้หากเซลล์ต้องการทราบบางสิ่งคือส่งวัตถุไปยังเซลล์ที่ตอบสนองต่อวิธีการบางอย่างที่ตอบคำถามที่เซลล์ต้องการถามและให้ผู้ควบคุมใช้ตรรกะในการกำหนดสิ่งที่จะตอบ (จากคำถามของคุณฉันเดาว่าเซลล์ต้องการทราบว่ามีบางสิ่งที่มองเห็นได้หรือไม่)

สร้างโปรโตคอลผู้รับมอบสิทธิ์ในเซลล์ตั้งค่าผู้รับมอบสิทธิ์ของเซลล์ tableViewController และย้ายตรรกะ "การควบคุม" ของ ui ทั้งหมดใน tableViewCotroller

เซลล์มุมมองตารางควรเป็นมุมมองจำลองที่จะแสดงเฉพาะข้อมูล


อาจจะดีกว่าผ่านตัวแทน ฉันใช้การอ้างอิงที่ส่งผ่านไปยังตารางชั่วคราวเนื่องจากผู้ปกครองแจ้งปัญหาให้ฉัน
Cristi Băluță

1
สิ่งที่ฉันอธิบายโดยทั่วไปคือการใช้รูปแบบตัวแทน :)
Javier Soto

นี่ควรเป็นคำตอบที่ได้รับการยอมรับผู้คนเห็นคำถามนี้ดูคำตอบการโหวตมากกว่า 150 รายการและพวกเขาคิดว่าเป็นเรื่องดีที่จะได้รับ tableView จากเซลล์และยิ่งไปกว่านั้นอาจมีการอ้างอิงที่ชัดเจน
Alex Terente

6

ฉันสร้างหมวดหมู่บน UITableViewCell เพื่อรับ tableView หลัก:

@implementation UITableViewCell (ParentTableView)


- (UITableView *)parentTableView {
    UITableView *tableView = nil;
    UIView *view = self;
    while(view != nil) {
        if([view isKindOfClass:[UITableView class]]) {
            tableView = (UITableView *)view;
            break;
        }
        view = [view superview];
    }
    return tableView;
}


@end

ดีที่สุด


3

นี่คือเวอร์ชันSwiftตามคำตอบข้างต้น ฉันได้สรุปไว้ExtendedCellสำหรับการใช้งานในภายหลัง

import Foundation
import UIKit

class ExtendedCell: UITableViewCell {

    weak var _tableView: UITableView!

    func rowIndex() -> Int {
        if _tableView == nil {
            _tableView = tableView()
        }

        return _tableView.indexPathForSelectedRow!.row
    }

    func tableView() -> UITableView! {
        if _tableView != nil {
            return _tableView
        }

        var view = self.superview
        while view != nil && !(view?.isKindOfClass(UITableView))! {
            view = view?.superview
        }

        self._tableView = view as! UITableView
        return _tableView
    }
}

หวังว่านี่จะช่วยได้ :)


2

ฉันใช้วิธีแก้ปัญหานี้ตามคำแนะนำของ Gabe ที่ว่าUITableViewWrapperViewวัตถุนั้นเป็นUITableViewCellsuperview ของ วัตถุใน iOS7 beta5

คลาสย่อยUITableviewCell:

- (UITableView *)superTableView
{
    return (UITableView *)[self findTableView:self];
}

- (UIView *)findTableView:(UIView *)view
{
    if (view.superview && [view.superview isKindOfClass:[UITableView class]]) {
        return view.superview;
    }
    return [self findTableView:view.superview];
}

2

ฉันยืมและแก้ไขเล็กน้อยจากคำตอบด้านบนและมาพร้อมกับตัวอย่างต่อไปนี้

- (id)recursivelyFindSuperViewWithClass:(Class)clazz fromView:(id)containedView {
    id containingView = [containedView superview];
    while (containingView && ![containingView isKindOfClass:[clazz class]]) {
        containingView = [containingView superview];
    }
    return containingView;
}

การผ่านในชั้นเรียนมีความยืดหยุ่นในการข้ามผ่านและรับมุมมองอื่นที่ไม่ใช่ UITableView ในบางโอกาส


2

วิธีแก้ปัญหานี้ของฉันค่อนข้างคล้ายกับวิธีแก้ปัญหาอื่น ๆ แต่ใช้ for-loop ที่สวยงามและสั้น ควรเป็นหลักฐานในอนาคต:

- (UITableView *)tableView
{
    UIView *view;
    for (view = self.superview; ![view isKindOfClass:UITableView.class]; view = view.superview);
    return (UITableView *)view;
}

สิ่งนี้จะวนซ้ำตลอดไปหากเซลล์ยังไม่อยู่ในมุมมองตารางเมื่อถูกเรียก ในการแก้ไขให้เพิ่มการตรวจสอบสำหรับศูนย์: for (view = self.superview; view && ![view isKindOfClass:UITableView.class]; view = view.superview);
Antonio Nunes


1

แทนที่จะใช้ superview ให้ลองใช้ ["UItableViewvariable" visibleCells]

ฉันใช้สิ่งนั้นใน foreach ลูปเพื่อวนรอบเซลล์ที่แอปเห็นและใช้งานได้

for (UITableView *v in [orderItemTableView visibleCells])//visibleCell is the fix.
{
  @try{
    [orderItemTableView reloadData];
    if ([v isKindOfClass:[UIView class]]) {
        ReviewOrderTableViewCell *cell = (ReviewOrderTableViewCell *)v;
        if (([[cell deleteRecord] intValue] == 1) || ([[[cell editQuantityText] text] intValue] == 0))
            //code here 
    }
  }
}

ใช้งานได้เหมือนมีเสน่ห์


1

ทดสอบเพียงเล็กน้อย แต่ตัวอย่าง Swift 3 ที่ไม่ใช่ทั่วไปนี้ดูเหมือนจะใช้งานได้:

extension UITableViewCell {
    func tableView() -> UITableView? {
        var currentView: UIView = self
        while let superView = currentView.superview {
            if superView is UITableView {
                return (superView as! UITableView)
            }
            currentView = superView
        }
        return nil
    }
}

0

รหัสนี้`UITableView *tblView=[cell superview];จะให้ตัวอย่างของ UItableview ซึ่งมีเซลล์มุมมอง tabe


สิ่งนี้จะไม่ได้ผลเนื่องจาก Superview ในทันทีของ UITableViewCell ไม่ใช่ UITableView สำหรับ iOS 7 Superview UITableViewCell เป็น UITableViewWrapperView ดูคำตอบอื่น ๆ ได้ที่นี่สำหรับแนวทางที่เปราะบางและเชื่อถือได้มากขึ้น
JaredH

0

ฉันขอแนะนำให้คุณสำรวจลำดับชั้นมุมมองด้วยวิธีนี้เพื่อค้นหา UITableView หลัก:

- (UITableView *) findParentTableView:(UITableViewCell *) cell
{
    UIView *view = cell;
    while ( view && ![view isKindOfClass:[UITableView class]] )
    {
#ifdef DEBUG
        NSLog( @"%@", [[view  class ] description] );
#endif
        view = [view superview];
    }

    return ( (UITableView *) view );
}

มิฉะนั้นรหัสของคุณจะทำลายเมื่อแอปเปิ้ลมีการเปลี่ยนแปลงลำดับชั้นดูอีกครั้ง

อีกคำตอบหนึ่งที่ข้ามลำดับชั้นคือการเรียกซ้ำ


0
UITableViewCell Internal View Hierarchy Change in iOS 7

Using iOS 6.1 SDK

    <UITableViewCell>
       | <UITableViewCellContentView>
       |    | <UILabel>

Using iOS 7 SDK

    <UITableViewCell>
       | <UITableViewCellScrollView>
       |    | <UIButton>
       |    |    | <UIImageView>
       |    | <UITableViewCellContentView>
       |    |    | <UILabel>


The new private UITableViewCellScrollView class is a subclass of UIScrollView and is what allows this interaction:


![enter image description here][1]


  [1]: http://i.stack.imgur.com/C2uJa.gif

http://www.curiousfind.com/blog/646 ขอบคุณ



0
extension UIView {
    func parentTableView() -> UITableView? {
        var viewOrNil: UIView? = self
        while let view = viewOrNil {
            if let tableView = view as? UITableView {
                return tableView
            }
            viewOrNil = view.superview
        }
        return nil
    }
}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.