Как получить UITableView от UITableViewCell?


100

У меня есть объект, UITableViewCellсвязанный с объектом, и мне нужно сказать, видна ли ячейка. Из проведенного мной исследования это означает, что мне нужно каким-то образом получить доступ к тому, UITableViewчто его содержит (оттуда есть несколько способов проверить, виден ли он). Поэтому мне интересно, UITableViewCellесть ли указатель UITableViewна ячейку или есть ли другой способ получить указатель из ячейки?


2
Какая у этого цель?
max_

[cell superView]может быть?
Крис Лунам

6
Стоит объяснить, почему вы думаете, что вам это нужно - так как это может быть признаком плохого дизайна, поскольку я не могу придумать многих законных причин, по которым ячейка узнает, отображается ли она на экране или нет.
Paul.

@ 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
Я бы рекомендовал добавить слабую ссылку на табличное представление в ячейку, чтобы избежать проблем совместимости в будущих обновлениях.
Cenny

1
Скорее слабовато. Это не сработает, если иерархия представления изменится в будущем.
RomanN

2
Было бы лучше просто создать слабое свойство, которое фактически содержит указатель на представление таблицы. В подклассе @property (weak, nonatomic) UITableView *tableView;и tableView:cellForRowAtIndexPath:только что установленном cell.tableView = tableView;.
Алехандро Иван

Текущий опыт заключается в том, что после удаления ячейки из очереди табличное представление не отображается как супервизор ячейки сразу - если я прокручиваю ячейку из поля зрения и обратно, я нахожу табличное представление как супервизор (рекурсивный поиск, как описано в другие ответы). Вероятными причинами могут быть автопрокладка и, возможно, другие факторы.
Джонни

48

В iOS7 beta 5 UITableViewWrapperView- это супервизор UITableViewCell. Также UITableViewесть надзор над UITableViewWrapperView.

Итак, для iOS 7 решение

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

Итак, для iOS до iOS 6 решение

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


5
О боже, это резкое изменение API. Какая лучшая практика Apple для ветвления, если вы поддерживаете и 6, и 7?
Райан Романчук

@RyanRomanchuk Вот одно хорошее предложение: devforums.apple.com/message/865550#865550 - создайте слабый указатель на связанный с ним tableView при создании ячейки. В качестве альтернативы создайте UITableViewCellкатегорию с новым методом, relatedTableViewкоторый проверяет версию iOS и возвращает соответствующий superView.
memmons

3
Напишите для этого рекурсивную категорию в UIView. Версию проверять не нужно; просто вызывайте superview, пока не найдете ячейку представления таблицы или верх стека представления. Это то, что я использовал в iOS 6, и он работал без изменений в iOS 7. И должен работать также и в iOS 8.
Стивен Фишер

Просить прощения; Я имел в виду, пока вы не найдете вид таблицы. :)
Стивен Фишер

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

Эмпирическое правило - не использовать принудительное разворачивание
тибо Ноа

1
@thibautnoah Перед развёрткой стоит view != nilчек. Обновлен до более чистого кода
Орхан Алиханов

25

До iOS7 супервизор ячейки UITableViewсодержал ее. Что касается iOS7 GM (так что, предположительно, также будет в общедоступном выпуске) супервизор ячейки - это UITableViewWrapperViewс его супервизором 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, упрощает рефакторинг существующего кода - просто выполните поиск и замените с помощью [cell relatedTable]и вставьте утверждение, чтобы гарантировать, что если иерархия представлений изменится или вернется в будущем, она появится немедленно. в ваших тестах.

Решение №2: Добавьте слабую UITableViewссылку наUITableViewCell

@interface SOUITableViewCell

   @property (weak, nonatomic) UITableView *tableView;

@end

Это намного лучший дизайн, хотя для его использования в существующих проектах потребуется немного больше рефакторинга кода. При tableView:cellForRowAtIndexPathиспользовании SOUITableViewCell в качестве класса ячейки или убедитесь, что ваш пользовательский класс ячейки является подклассом, SOUITableViewCellи назначьте tableView свойству tableView ячейки. Затем внутри ячейки вы можете обратиться к содержащемуся табличному представлению, используя self.tableView.


Я не согласен с тем, что решение 2 - лучший дизайн. У него больше точек отказа и требуется дисциплинированное ручное вмешательство. Первое решение на самом деле довольно надежно, хотя я бы реализовал его, как предложил @idris в своем ответе, для большей защиты от будущего.
devios1

1
Тест [self.superview.superview isKindOfClass:[UITableView class]]должен быть первым, ведь iOS 7 все больше и больше.
CopperCash

7

Если он виден, значит, у него есть супервизор. И ... сюрприз ... супервизор - это объект UITableView.

Однако наличие супервизора не гарантирует попадания на экран. Но UITableView предоставляет методы для определения видимых ячеек.

И нет, нет специальной ссылки из ячейки на таблицу. Но когда вы создаете подкласс UITableViewCell, вы можете ввести его и установить при создании. (Я делал это сам много раз, прежде чем подумал об иерархии подпредставлений.)

Обновление для iOS7: Apple изменила иерархию подпредставлений здесь. Как обычно, при работе с вещами, которые подробно не задокументированы, всегда есть риск, что что-то изменится. «Ползать вверх» по иерархии представлений до тех пор, пока не будет найден объект UITableView, гораздо проще.


14
Это больше не так в iOS7. В iOS7 beta5 UITableViewWrapperView является супервизором UITableViewCell ... вызывает у меня проблемы прямо сейчас,
Гейб

Спасибо за комментарий. Тогда мне придется проверить часть своего кода.
Герман Клекер,

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, спасибо. Я добавил ваш совет к ответу.
Мехдзор

Имя, используемое рекурсивным вызовом, должно совпадать. Итак, это нужно читать: ... ?? superview? .lookForSuperviewOfType (type)
Роберт

7

Независимо от того, что вам удастся сделать, вызвав супервизор или через цепочку ответчиков, будет очень хрупко. Лучший способ сделать это, если ячейки хотят что-то знать, - передать объект в ячейку, которая отвечает на какой-то метод, который отвечает на вопрос, который ячейка хочет задать, и пусть контроллер реализует логику определения того, что ответить. (из вашего вопроса, я думаю, ячейка хочет знать, видно что-то или нет).

Создайте протокол делегата в ячейке, установите делегата ячейки tableViewController и переместите всю «управляющую» логику пользовательского интерфейса в tableViewCotroller.

Ячейки табличного представления должны быть пустыми, отображать только информацию.


Может быть, даже лучше через делегатов. Я временно использую переданную ссылку на таблицу, так как родитель дал мне проблемы.
Cristi Băluță

1
То, что я описал, в основном использует шаблон делегата :)
Хавьер Сото,

Это должен быть принятый ответ, люди видят этот вопрос, видят ответ на 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

Я основал это решение на предположении Гейба о том, что UITableViewWrapperViewобъект является UITableViewCellсупервизором объекта в 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 и является коротким. Это также должно быть ориентировано на будущее:

- (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);
Антонио Нуньес,

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

1

Вместо супервизора попробуйте использовать ["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, который содержит ячейку представления табе


Это не сработает, поскольку непосредственный супервизор UITableViewCell не является UITableView. Начиная с iOS 7 супервизор 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 );
}

В противном случае ваш код сломается, когда Apple снова изменит иерархию представлений .

Другой ответ, который также проходит по иерархии, - рекурсивный.


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

0

из ответа @idris я написал расширение для UITableViewCell в 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
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.