Як отримати UITableView від UITableViewCell?


100

У мене є об'єкт, UITableViewCellякий пов'язаний з об'єктом, і мені потрібно сказати, чи відображається комірка. З проведених нами досліджень це означає, що мені потрібно якось отримати доступ до того, UITableViewщо його містить (звідти є кілька способів перевірити, чи воно видно). Тож мені цікаво, чи UITableViewCellє вказівник на UITableViewабо, чи був інший спосіб отримати вказівник з комірки?


2
Яка мета цього?
макс_

[cell superView]може бути?
Chris Loonam

6
Варто пояснити, чому ви думаєте, що вам це потрібно - оскільки це може бути ознакою поганого дизайну, оскільки я не можу реально придумати багато законних причин, щоб клітина знала, чи є вона на екрані чи ні.
Пол.с

@ Paul.s У нас є розпізнавач жестів на зображенні в комірці, і коли торкається комірки, вона відкриває інший вид перекриття, думаю, стиль переходу, який повинен накладати стільки комірок, скільки потрібно для належного відображення. Для цього для відображення потрібен TableView або інший вид, який йому надається. Не дуже задоволений рішеннями, але для отримання бажаного ефекту отримання UITableView UITableViewCell - це найкраще, що ми придумали.
чадбаг

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
Я рекомендую додавати слабке посилання на перегляд таблиці до комірки, щоб уникнути проблем сумісності у майбутніх оновленнях.
Сенні

1
Досить слабкий шлях. Він не працюватиме, якщо в майбутньому зміниться ієрархія перегляду.
RomanN

2
Було б краще просто створити слабку властивість, яка насправді містить покажчик на подання таблиці. У підкласі @property (weak, nonatomic) UITableView *tableView;та в tableView:cellForRowAtIndexPath:просто наборі cell.tableView = tableView;.
Алехандро Іван

Поточний досвід полягає в тому, що після виведення з комірки огляд таблиці не з’являється відразу як огляд комірки - якщо я прокручую комірку з виду і знову знову, я знаходжу подання таблиці як нагляд (рекурсивний пошук, як описано в інші відповіді). Автомобільний розклад та, можливо, інші фактори є ймовірними причинами.
Джоні

48

В iOS7 бета-5 UITableViewWrapperViewє наглядом a UITableViewCell. Також UITableViewє нагляд за UITableViewWrapperView.

Тож для iOS 7 рішення є

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

Тож для iOSes до iOS 6 рішення є

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


5
О людино, це жорстока зміна API. Яка найкраща практика для розгалуження яблук, якщо ви підтримуєте і 6, і 7?
Райан Романчук

@RyanRomanchuk Ось одна гарна пропозиція: devforums.apple.com/message/865550#865550 - створіть слабкий вказівник на ваш пов’язаний tableView при створенні комірки. Крім того, створіть UITableViewCellкатегорію за допомогою нового методу, relatedTableViewякий робить перевірку на версію iOS та повертає відповідний SuperView.
пам’ятки

3
Напишіть для цього рекурсивну категорію на UIView. Вам не потрібно перевіряти версію; просто зателефонуйте до нагляду, поки не знайдете клітинку подання таблиці чи верхню частину стеку перегляду. Це те, що я використовував в 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 iOS7 - це огляд UITableViewCell ... викликає у мене проблеми зараз
Gabe

Дякуємо за коментар Тоді мені доведеться перевірити частину свого коду.
Герман Клекер

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, дякую Я додав ваші поради до відповіді.
Мехдзор

Ім’я, яке використовується рекурсивним викликом, має відповідати. Отже, це потрібно прочитати: ... ?? Superview? .lookForSuperviewOfType (type)
robert

7

Що б вам не вдалося зробити, зателефонувавши в режимі суперпрогляду або через ланцюжок відповідей, буде дуже крихким. Найкращий спосіб зробити це, якщо клітини хочуть щось знати, - це передати об’єкт до комірки, який відповідає на якийсь метод, який відповідає на питання, яке комірка хоче задати, і дозволити контролеру реалізувати логіку визначення відповіді. (з вашого запитання, я думаю, клітина хоче знати, чи щось видно чи ні).

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

У клітинках подання таблиці повинен бути перегляд дум, який відображатиме лише інформацію.


Можливо, ще краще через делегатів. Я тимчасово використовую передане посилання на таблицю, оскільки батько створив мені проблеми.
Крісті Балуша

1
Те, що я описав, в основному використовує модель делегата :)
Хав'єр Сото

Це має бути прийнятою відповіддю, люди бачать це запитання, бачать відповідь 150+, і вони вважають, що це добре, щоб отримати tableView з комірки і навіть більше, можливо, зберегти чітку посилання на неї, на жаль.
Alex

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

Моє рішення цієї проблеми дещо схоже з іншими рішеннями, але використовує елегантний для-цикл і короткий. Це також повинно бути надійним:

- (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" видимими клітинками].

Я використав це в циклі 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.