Làm cách nào để lấy UITableView từ UITableViewCell?


100

Tôi có một ô UITableViewCellđược liên kết với một đối tượng và tôi cần biết ô đó có hiển thị hay không. Từ nghiên cứu mà tôi đã thực hiện, điều này có nghĩa là tôi cần phải truy cập bằng cách nào UITableViewđó có chứa nó (từ đó, có một số cách để kiểm tra xem nó có hiển thị không). Vì vậy, tôi tự hỏi nếu UITableViewCellcó một con trỏ đến UITableView, hoặc nếu có bất kỳ cách nào khác để lấy một con trỏ từ ô?


2
Mục đích của việc này là gì?
max_

[cell superView]có lẽ?
Chris Loonam

6
Điều đáng giải thích là tại sao bạn nghĩ bạn cần điều này - vì đây có thể là một dấu hiệu của thiết kế xấu vì tôi thực sự không thể nghĩ ra nhiều lý do chính đáng để một tế bào biết nó có trên màn hình hay không.
Paul.s

@ Paul.s Chúng tôi có một trình nhận dạng cử chỉ trên một hình ảnh trong một ô và khi ô được chạm vào, nó sẽ mở ra một chế độ xem lớp phủ khác, hãy nghĩ đến kiểu cửa sổ bật lên, sẽ phủ nhiều ô nếu cần để hiển thị đúng. Để điều này hoạt động, nó cần TableView hoặc chế độ xem khác được cung cấp cho nó để hiển thị. Không thực sự hài lòng với các giải pháp nhưng để đạt được hiệu quả mong muốn, việc sử dụng UITableView của UITableViewCell là điều tốt nhất mà chúng tôi đã đưa ra.
chadbag

1
@chadbag đừng lo lắng, hy vọng tôi đã đưa ra ý tưởng cho người khác có cùng vấn đề.
PJ_Finnegan

Câu trả lời:


153

Để tránh kiểm tra phiên bản iOS, hãy lặp đi lặp lại các chế độ xem siêu tốc từ chế độ xem của ô cho đến khi tìm thấy UITableView:

id view = [tableViewCellInstance superview];

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

UITableView *tableView = (UITableView *)view;

1
Cảm ơn. Có vẻ như điều này đã thay đổi một lần nữa trong iOS 8 và điều này sẽ chăm sóc cho tất cả các phiên bản một cách độc đáo.
djskinner

2
Tôi khuyên bạn nên thêm một tham chiếu yếu đến chế độ xem bảng vào ô để tránh các vấn đề tương thích trong các bản cập nhật trong tương lai.
Cenny

1
Đúng hơn là một cách yếu ớt. Nó sẽ không hoạt động nếu thứ bậc của chế độ xem thay đổi trong tương lai.
RomanN

2
Sẽ tốt hơn nếu bạn chỉ cần tạo một thuộc tính yếu thực sự giữ một con trỏ đến tableview. Trong lớp con @property (weak, nonatomic) UITableView *tableView;tableView:cellForRowAtIndexPath:chỉ trong tập hợp cell.tableView = tableView;.
Alejandro Iván

Kinh nghiệm hiện tại là sau khi xếp thứ tự một ô, chế độ xem bảng không xuất hiện dưới dạng chế độ xem siêu cấp của ô ngay lập tức - nếu tôi cuộn ô ra khỏi chế độ xem và quay lại lần nữa, tôi thấy chế độ xem bảng dưới dạng siêu xem (tìm kiếm đệ quy như được mô tả trong câu trả lời khác). Autolayout và có thể là các yếu tố khác có thể là lý do.
Jonny

48

Trong iOS7 beta 5 UITableViewWrapperViewlà bản tổng hợp của a UITableViewCell. Cũng UITableViewlà superview của a UITableViewWrapperView.

Vì vậy, đối với iOS 7, giải pháp là

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

Vì vậy, đối với iOS cho đến iOS 6, giải pháp là

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


5
Ôi trời, đây là một sự thay đổi API tàn bạo. Cách tốt nhất của táo để phân nhánh là gì nếu bạn hỗ trợ cả 6 và 7?
Ryan Romanchuk

@RyanRomanchuk Đây là một gợi ý hay: devforums.apple.com/message/865550#865550 - tạo một con trỏ yếu đến tableView liên quan của bạn khi ô được tạo. Ngoài ra, hãy tạo một UITableViewCelldanh mục bằng một phương thức mới, phương thức relatedTableViewnày sẽ kiểm tra phiên bản iOS và trả về superView thích hợp.
memmons 14/09/13

3
Viết một danh mục đệ quy trên UIView cho điều này. Bạn không cần phải kiểm tra phiên bản; chỉ cần gọi superview cho đến khi bạn tìm thấy ô xem bảng hoặc ô trên cùng của ngăn xếp chế độ xem. Đây là những gì tôi đã sử dụng trong iOS 6, và nó làm việc mà không sửa đổi trong iOS 7. Và nên làm việc sitll trong iOS 8.
Steven Fisher

Xin lỗi; Ý tôi là cho đến khi bạn tìm thấy bảng xem. :)
Steven Fisher

39

Swift 5 phần mở rộng

Đệ quy

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)
    }
}

Sử dụng vòng lặp

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
    }
}

Một nguyên tắc nhỏ là để không sử dụng vũ lực unwrapping
Thibaut noah

1
@thibautnoah Có một view != nilkiểm tra trước khi mở gói. Cập nhật mã sạch hơn mặc dù
Orkhan Alikhanov

25

Trước iOS7, superview của ô là thứ UITableViewchứa nó. Đối với iOS7 GM (có lẽ cũng sẽ được phát hành công khai), superview của tế bào là một UITableViewWrapperViewvới superview của nó là UITableView. Có hai giải pháp cho vấn đề.

Giải pháp số 1: Tạo một UITableViewCelldanh mục

@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

Đây là một sự thay thế tốt để sử dụng cell.superview, giúp bạn dễ dàng cấu trúc lại mã hiện tại của mình - chỉ cần tìm kiếm và thay thế bằng [cell relatedTable]và đưa ra một xác nhận để đảm bảo rằng nếu hệ thống phân cấp chế độ xem thay đổi hoặc hoàn nguyên trong tương lai, nó sẽ hiển thị ngay lập tức trong các bài kiểm tra của bạn.

Giải pháp # 2: Thêm UITableViewtham chiếu yếu vàoUITableViewCell

@interface SOUITableViewCell

   @property (weak, nonatomic) UITableView *tableView;

@end

Đây là một thiết kế tốt hơn nhiều, mặc dù nó sẽ yêu cầu cấu trúc lại mã nhiều hơn một chút để sử dụng trong các dự án hiện có. Khi tableView:cellForRowAtIndexPathsử dụng SOUITableViewCell làm lớp ô của bạn hoặc đảm bảo rằng lớp ô tùy chỉnh của bạn được phân lớp từ đó SOUITableViewCellvà gán tableView cho thuộc tính tableView của ô. Sau đó, bên trong ô, bạn có thể tham chiếu đến chế độ xem bảng chứa bằng cách sử dụng self.tableView.


Tôi không đồng ý rằng giải pháp 2 là một thiết kế tốt hơn. Nó có nhiều điểm thất bại hơn và yêu cầu can thiệp thủ công có kỷ luật. Giải pháp đầu tiên thực sự khá đáng tin cậy, mặc dù tôi sẽ triển khai nó như @idris đã đề xuất trong câu trả lời của anh ấy để cải thiện một chút trong tương lai.
defos1

1
Kiểm tra [self.superview.superview isKindOfClass:[UITableView class]]nên là đầu tiên, vì iOS 7 ngày càng nhiều hơn.
ĐồngCash

7

Nếu nó có thể nhìn thấy thì nó có một cái nhìn siêu việt. Và ... thật bất ngờ ... superview là một đối tượng UITableView.

Tuy nhiên, việc có một siêu thị không đảm bảo cho việc hiển thị trên màn hình. Nhưng UITableView cung cấp các phương pháp để xác định ô nào có thể nhìn thấy.

Và không, không có tham chiếu dành riêng từ một ô đến một bảng. Nhưng khi bạn phân lớp UITableViewCell, bạn có thể giới thiệu một lớp và đặt nó khi tạo. (Bản thân tôi đã làm điều đó rất nhiều trước khi nghĩ đến hệ thống phân cấp chế độ xem phụ.)

Cập nhật cho iOS7: Apple đã thay đổi phân cấp chế độ xem phụ ở đây. Như thường lệ khi làm việc với những thứ không được ghi chép lại, luôn có nguy cơ mọi thứ thay đổi. Việc "thu thập dữ liệu" hệ thống phân cấp chế độ xem cho đến khi tìm thấy đối tượng UITableView là một điều tiết kiệm hơn nhiều.


14
Điều này không còn đúng trong iOS7 nữa, Trong iOS7 beta5, UITableViewWrapperView là siêu chế độ xem của UITableViewCell ... khiến tôi gặp sự cố ngay bây giờ
Gabe

Cảm ơn đã nhận xét. Sau đó, tôi sẽ phải kiểm tra một số mã của mình.
Hermann Klecker

7

Giải pháp Swift 2.2.

Một phần mở rộng cho UIView tìm kiếm đệ quy một chế độ xem với một loại cụ thể.

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
    }
}

hoặc nhỏ gọn hơn (nhờ kabiroberai):

import UIKit

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

Trong ô của bạn, bạn chỉ cần gọi nó là:

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

Lưu ý rằng UITableViewCell chỉ được thêm vào UITableView sau khi thực thi cellForRowAtIndexPath.


Bạn có thể nhỏ gọn toàn bộ lookForSuperviewOfType:cơ thể phương pháp vào một dòng, làm cho nó thậm chí còn nhanh chóng-ey:return superview as? T ?? superview?.superviewOfType(type)
kabiroberai

@kabiroberai, cảm ơn bạn. Tôi đã thêm lời khuyên của bạn vào câu trả lời.
Mehdzor

Tên được sử dụng bởi cuộc gọi đệ quy cần phải khớp. Vì vậy, điều này cần phải đọc: ... ?? superview? .lookForSuperviewOfType (type)
robert

7

Bất cứ điều gì bạn có thể phải làm bằng cách gọi super view hoặc thông qua chuỗi phản hồi sẽ rất mong manh. Cách tốt nhất để làm điều này, nếu các ô muốn biết điều gì đó, là chuyển một đối tượng đến ô phản hồi với một số phương thức trả lời câu hỏi mà ô muốn hỏi và yêu cầu bộ điều khiển thực hiện logic xác định nội dung cần trả lời. (từ câu hỏi của bạn, tôi đoán rằng tế bào muốn biết nếu một cái gì đó có thể nhìn thấy được hay không).

Tạo một giao thức ủy quyền trong ô, đặt đại biểu của ô là tableViewController và di chuyển tất cả logic "điều khiển" ui trong tableViewCotroller.

Các ô của chế độ xem bảng phải là chế độ xem dum sẽ chỉ hiển thị thông tin.


Có thể tốt hơn nữa thông qua các đại biểu. Tôi đang tạm thời sử dụng một tham chiếu được chuyển đến bảng vì cha mẹ đã cho tôi vấn đề.
Cristi Băluță

1
Những gì tôi mô tả về cơ bản là sử dụng mẫu đại biểu :)
Javier Soto

Đây phải là câu trả lời được chấp nhận, mọi người nhìn thấy câu hỏi này, xem câu trả lời hơn 150 lượt ủng hộ và họ nghĩ rằng không sao để lấy tableView từ ô và thậm chí nhiều hơn có thể giữ một tham chiếu mạnh mẽ đến nó.
Alex Terente

6

Tôi đã tạo một danh mục trên UITableViewCell để lấy tableView mẹ của nó:

@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

Tốt,


3

Đây là phiên bản Swift dựa trên các câu trả lời trên. Tôi đã khái quát thành ExtendedCellđể sử dụng sau này.

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
    }
}

Hy vọng điều này giúp đỡ :)


2

Tôi dựa trên giải pháp này dựa trên gợi ý của Gabe rằng UITableViewWrapperViewđối tượng là superview của UITableViewCellđối tượng trong iOS7 beta5.

Lớp con 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

Tôi đã mượn và sửa đổi một chút từ câu trả lời trên và đưa ra đoạn mã sau.

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

Vượt qua trong lớp mang lại sự linh hoạt cho việc di chuyển và nhận các lượt xem khác với UITableView trong một số trường hợp khác.


2

Giải pháp của tôi cho vấn đề này hơi giống với các giải pháp khác, nhưng sử dụng vòng lặp for thanh lịch và ngắn gọn. Nó cũng phải được chứng minh trong tương lai:

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

Điều này sẽ lặp lại mãi mãi nếu ô chưa ở trong dạng xem bảng khi được gọi. Để sửa chữa, thêm một tấm séc trị nil: for (view = self.superview; view && ![view isKindOfClass:UITableView.class]; view = view.superview);
Antonio Nunes

2
UITableView *tv = (UITableView *) self.superview.superview;
BuyListController *vc = (BuyListController *) tv.dataSource;

1

Thay vì superview, hãy thử sử dụng ["UItableViewvariable" opensCells].

Tôi đã sử dụng nó trong một vòng lặp foreach để lặp qua các ô mà ứng dụng đã nhìn thấy và nó hoạt động.

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 
    }
  }
}

Hoạt động như một sự quyến rũ.


1

Đã được kiểm tra tối thiểu nhưng ví dụ Swift 3 không chung chung này dường như hoạt động:

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];này sẽ cung cấp cho bạn một phiên bản của UItableview chứa ô chế độ xem tabe


Điều này sẽ không hoạt động, vì superview ngay lập tức của UITableViewCell không phải là UITableView. Kể từ iOS 7, superview UITableViewCell là một UITableViewWrapperView. Xem các câu trả lời khác tại đây để biết các cách tiếp cận ít mong manh hơn, đáng tin cậy hơn.
JaredH

0

Tôi khuyên bạn nên duyệt qua phân cấp chế độ xem theo cách này để tìm UITableView chính:

- (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 );
}

Nếu không, mã của bạn sẽ bị hỏng khi Apple thay đổi lại hệ thống phân cấp chế độ xem .

Một câu trả lời khác cũng đi qua hệ thống phân cấp là đệ quy.


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 Cảm ơn bạn


0

Bạn có thể lấy nó bằng một dòng mã.

UITableView *tableView = (UITableView *)[[cell superview] superview];

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
    }
}

0

từ câu trả lời @idris Tôi đã viết một bản mở rộng cho UITableViewCell trong Swift

extension UITableViewCell {
func relatedTableView() -> UITableView? {
    var view = self.superview
    while view != nil && !(view is UITableView) {
        view = view?.superview
    }

    guard let tableView = view as? UITableView else { return nil }
    return tableView
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.