UITableview: як відключити вибір для деяких рядків, але не для інших


298

Я tableviewпоказую вміст групи, розібраний з XML. Я хочу відключити подію клацання по ній (я не можу взагалі натискати на неї) Таблиця містить дві групи. Я хочу відключити вибір лише для першої групи, але не для другої. Клацнувши перший рядок другої групи navigatesдо моєї трубки player view.

Як я можу зробити вибір лише певних груп чи рядків?

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
    if(indexPath.section!=0)
    if(indexPath.row==0)    

    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:tubeUrl]];   
}

Дякую.

Відповіді:


499

Вам просто потрібно ввести цей код cellForRowAtIndexPath

Щоб вимкнути властивість вибору комірки: (натискаючи клітинку)

cell.selectionStyle = UITableViewCellSelectionStyleNone;

Щоб увімкнути можливість вибору (торкніться) комірки: (торкнення клітинки)

// Default style
cell.selectionStyle = UITableViewCellSelectionStyleBlue;

// Gray style
cell.selectionStyle = UITableViewCellSelectionStyleGray;

Зауважте, що комірка з selectionStyle = UITableViewCellSelectionStyleNone;все одно викликатиме користувальницький інтерфейс, didSelectRowAtIndexPathколи вона торкнеться користувача. Щоб цього уникнути, виконайте, як запропоновано нижче, і встановіть.

cell.userInteractionEnabled = NO;

замість цього. Також зауважте, що ви, можливо, захочете встановити cell.textLabel.enabled = NO;елемент на сірий.


1
Цей метод створить помилку, якщо ви спробуєте провести пальцем, щоб видалити.
Vive

116
Це хак! Зробіть це, використовуйте відповідь нижче - використовуючи willSelectRowAtIndexPath з поверненням нуля!
Пітер Штайгер

4
Користувачеві взаємодіїEnabled речі не вірні, гак будеSelectRowAtIndexPath замість цього, як зазначають люди.
Джоні

23
На iOS 6.0 і пізніших версіях tableView:shouldHighlightRowAtIndexPath:- це новий UITableViewDelegateметод, який повертається на BOOLоснові того, чи indexPathслід виділяти передане чи ні . Якщо ви будуєте версію 6.0 і пізніше, я настійно рекомендую цей новий API.
cbowns

3
якщо ви робите це в Swift і у вас виникають проблеми, але опинилися тут, вам потрібно буде встановити cell!.selectionStyle = .None яка сила розгортає клітинку, дозволяючи вам отримати доступ до її властивостей.
Маркос

371

Якщо ви хочете зробити рядок (або підмножина рядків) , не вибирають , реалізувати UITableViewDelegateметод -tableView:willSelectRowAtIndexPath:(також згадується TechZen). Якщо значення indexPathне може бути обраним , поверніться nil, інакше поверніть його indexPath. Щоб отримати поведінку вибору за замовчуванням, ви просто повернете indexPathпереданий у свій delegateметод, але ви також можете змінити вибір рядків, повернувши іншийindexPath .

приклад:

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // rows in section 0 should not be selectable
    if ( indexPath.section == 0 ) return nil;

    // first 3 rows in any section should not be selectable
    if ( indexPath.row <= 2 ) return nil;

    // By default, allow row to be selected
    return indexPath;
}

2
Маленька деталь: ви мен <= 2, не = <2. Поганий смайлик! :)
Jonas Byström

23
Я також хотів би додати, що це правильна відповідь, але ви також повинні встановити selectStyle на UITableViewCellSelectionStyleNone у cellForRowAtIndexPath. Чому? тому що без цього стилю вибору клітинку неможливо вибрати, але вона все одно відображатиметься "як вибрана" при натисканні вниз (тому вона змінюється на вибраний колір, якщо є інший для інших комірок)
TooManyEduardos

4
це повинно мати більше результатів, ніж прийнята відповідь
user1244109

2
Я думаю, що головна причина, що це не прийнята відповідь, це тому, що прийняту відповідь простіше. Хоча це не найкращий спосіб зробити це, оскільки ваша таблиця все ще буде називати setSelected: у ваших клітинках вона все ще працює, візуально. Ця відповідь тут потребує більше роботи, але це правильний спосіб зробити це (ви також повинні поєднати пропозицію @TooManyEduardos), щоб уникнути непередбачуваних наслідків таблиці, що викликає setSelected: на ваші клітинки.
Брайан Сачетта

Мені подобається ця відповідь, але якщо ви не хочете дублювати свою логіку в cellForRowAtIndexPath і willSelectRowAtIndexPath, просто перевірте, чи Cell.selectionStyle == немає в willSelectRowAtIndexPath, щоб повернути нуль (див. Мою відповідь нижче)
Eric D'Souza

61

Починаючи з iOS 6, ви можете використовувати

-tableView:shouldHighlightRowAtIndexPath:

Якщо ти повернешся NO , він вимикає як виділення виділення, так і розгорнуту мовою розповідей, підключені до цієї комірки.

Метод називається тоді, коли дотик спадає на рядок. Повернення NOдо цього повідомлення зупиняє процес вибору і не призводить до того, що вибраний рядок втрачає обраний вигляд під час натискання.

Посилання на протокол UITableViewDelegate


3
Домовились. Для iOS 7 і вище це повинен бути кращим методом (адже на iOS 8 інші методи не працювали для мене)
race_carr

Це має бути головна відповідь.
Камерон Е

16

Жодна з наведених вище відповідей насправді не вирішує проблему правильно. Причина полягає в тому, що ми хочемо вимкнути вибір клітини, але не обов'язково підпогляди всередині комірки.

У моєму випадку я представляв UISwitch в середині рядка, і я хотів відключити вибір для решти рядка (який порожній), але не для перемикача! Правильний спосіб зробити це, отже, у способі

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

де виклад форми

[cell setSelectionStyle:UITableViewCellSelectionStyleNone];

вимикає вибір для конкретної комірки, в той же час дозволяє користувачеві керувати комутатором і, отже, використовувати відповідний селектор. Це неправда, якщо хтось відключає взаємодію з користувачем через

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

метод, який просто готує клітинку і не дозволяє взаємодія з UISwitch.

Більше того, використовуючи метод

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

щоб скасувати виділення клітинки із заявою форми

[tableView deselectRowAtIndexPath:indexPath animated:NO];

все ще показує вибраний рядок, поки користувач натискає на оригінальний contentView комірки.

Всього два мої копійки. Я впевнений, що багато хто вважатиме це корисним.


Погодьтеся. І повернення нуля willSelectRowAtIndexPathмає те саме питання, підзагляди не можна вибрати.
jeffjv

Також зауважте, що якщо у вас є статична таблиця, ви можете просто встановити Виділення на "Нічого" в Інструменті інтерфейсів для комірок, які не хочуть, щоб вибір відображався.
jeffjv

11

Ви відловлюєте вибрані ці методи джерела даних.

 tableView:willSelectRowAtIndexPath: 
 tableView:didSelectRowAtIndexPath: 
 tableView:willDeselectRowAtIndexPath: 
 tableView:didDeselectRowAtIndexPath:

У цих методах ви перевіряєте, чи вибраний рядок потрібний для вибору. Якщо це так, зробіть дії, якщо ні, не робіть нічого.

На жаль, ви не можете вимкнути вибір лише для одного розділу. Це весь стіл або нічого.

Однак можна встановити selectionStyleвластивість комірок таблиці UITableViewCellSelectionStyleNone. Я вважаю, що це зробить вибір невидимим. У поєднанні з вищезазначеними методами, які повинні зробити клітини виглядати абсолютно інертними з точки зору користувача.

Edit01:

Якщо у вас є таблиця, в якій вибираються лише деякі рядки, важливо, щоб комірки рядків, що вибираються, були візуально відмінні від рядків, що не вибираються. За замовчуванням спосіб це зробити кнопкою аксесуарів "шеврон".

Однак ви не хочете, щоб ваші користувачі намагалися вибирати рядки і думали, що додаток не працює, оскільки рядок нічого не робить.


розкажіть, будь ласка, як використати ці методи або наведіть кілька прикладних посилань на програму
Воїн

2
@Warrior: Якщо ви встановили SDK, ви можете відкрити Xcode, перейдіть до Help -> Developer documentation, а потім скопіюйте ці назви методів у поле пошуку, щоб побачити, як він працює. Документація розробника також містить зразок коду.
kennytm


9

Вам потрібно зробити щось на кшталт наступного, щоб відключити вибір клітини в межах cellForRowAtIndexPathметоду:

[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[cell setUserInteractionEnabled:NO];

Щоб показати клітку сірою, введіть у спосіб наступне tableView:WillDisplayCell:forRowAtIndexPath:

[cell setAlpha:0.5];

Один метод дозволяє контролювати інтерактивність, інший дозволяє контролювати зовнішній вигляд інтерфейсу.


5

Щоб зупинити вибір лише деяких комірок:

cell.userInteractionEnabled = NO;

Окрім запобігання вибору, це також зупиняє виклик tableView: didSelectRowAtIndexPath: для викликів для комірок, у яких його встановлено.


1
Дивна річ трапляється постійно: коли я встановив " userInteractionEnabledНІ" на одній комірці (ясніше, один indexPath), взаємодія користувачів деяких ІНШИХ комірок відключається. Я не маю поняття, чому це відбувається кожного разу, коли я використовую userInteractionEnabledв осередках подання таблиці. Це відома помилка чи я щось зробив не так?
Philip007

3
@ Philip007 Чи використовуються клітини "НІ" повторно? Якщо так, можливо, вам потрібно буде переконатися, що ви встановили його "ТАК", коли ви знімаєте комірок.
ДжозефH

1
Навіщо ставити його на "ТАК"? Я хочу, щоб бути NO (заборонити взаємодії з користувачем) .. Типовий прикладом є те , що я встановив взаємодія з користувачем першого рядка ( if [indexPath row] == 0) , щоб NOв tableView:cellForRowAtIndexPath:після того, як клітини звільнення пакета з черги. Зазвичай це спочатку добре. Потім після декількох викликів reloadDataраптом п'ятий ряд заборонено взаємодію, потім четвертий ряд .. Я не можу зрозуміти, коли це станеться. Вся справа виглядає випадково.
Philip007

6
@ Philip007 Якщо ви повторно клітини, вам потрібно буде скинути його в «YES» для осередків DO хочете бути інтерактивним. В іншому випадку, якщо клітинку "НІ" повторно використовувати в тому рядку, який ви хочете бути інтерактивним, вона все одно буде відключена. тобто:if [indexPath row] == 0) { set to NO } else { set to YES }
ЙосифH

5

Ви можете використовувати tableView:willDisplayCellметод, щоб виконати всі види налаштування для tableViewCell.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
     [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
     [cell setUserInteractionEnabled:NO];

     if (indexPath.section == 1 && indexPath.row == 0)
     {
         [cell setSelectionStyle:UITableViewCellSelectionStyleGray];
         [cell setUserInteractionEnabled:YES];
     }
} 

У цьому вище коді користувач може вибрати лише перший рядок у другому розділі tableView. Решта всіх рядків не можна вибрати. Спасибі! ~



5

Моє рішення на основі моделі даних:

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
    let rowDetails = viewModel.rowDetails(forIndexPath: indexPath)
    return rowDetails.enabled ? indexPath : nil
}

func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
    let rowDetails = viewModel.rowDetails(forIndexPath: indexPath)
    return rowDetails.enabled
}

5

Для швидкого 4.0 Це зробить трюк. Це вимкне клітинку в методі didSelectRowAtIndexPath, але збереже підзагляди під натисканням.

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
         if (indexPath.row == clickableIndex ) { 
            return indexPath
         }else{
            return nil
        }
    }

Або коротко:, return indexPath.row == clickableIndex ? indexPath : nilтримає ваш код трохи чистішим ;-)
Позначте

1

Використовуйте це, щоб клітина виглядала так, що вона вимкнена і не вибирається:

cell.selectionStyle = UITableViewCellSelectionStyleNone;

Важливо: зауважте, що це лише властивість стилізації та фактично не вимикає клітинку. Для того , щоб зробити це, ви повинні перевірити selectionStyleу вашій didSelectRowAtIndexPath:реалізації делегата:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if(cell.selectionStyle == UITableViewCellSelectionStyleNone) {
        return;
    }

    // do your cell selection handling here
}

1

Мені подобається відповідь Брайана Чападоса вище. Однак це означає, що у вас може бути логіка, дубльована в обох cellForRowAtIndexPath, а потім у willSelectRowAtIndexPath, яка може легко вийти з синхронізації. Замість того, щоб дублювати логіку, просто перевірте selectStyle:

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if (cell.selectionStyle == UITableViewCellSelectionStyleNone)
        return nil;

    else
        return indexPath;
}

1

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

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if (cell.accessoryType != UITableViewCellAccessoryDisclosureIndicator) {
        return nil;
    }
    return indexPath;
}

1

на Xcode 7 без кодування ви можете просто зробити наступне:

У контурному вікні виберіть Стіл перегляду таблиці. (Осередок вкладений у "Сцена контролера перегляду таблиці"> "Контролер перегляду таблиці"> "Перегляд таблиці".

У інспекторі атрибутів знайдіть поле, позначене Виділення, і виберіть Ні. розподілити інспектора За допомогою цієї опції комірка не отримає візуальної підсвітки, коли користувач натискає її.


0

ПРОСТО

Просто використовуйте cell.userInteractionEnabled = YES;для комірки, якщо вона може переміщатися та cell.userInteractionEnabled = NO;іншим чином


1
Це також запобігає взаємодії з будь-яким видом аксесуара всередині комірки, що може бути не бажаною поведінкою.
Грег Браун

0

Реалізуйте лише метод tableView:willSelectRowAtIndexPath: у джерелі даних для вашої таблиці. Якщо ви хочете виділити рядок на шляху, поверніть заданий indexPath. Якщо цього не зробити, поверніть нуль.

Приклад з мого додатка:

- (NSIndexPath *)tableView:(UITableView *)tableView
    willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    MySelectableObj* obj = [self objectAtPath:indexPath];
    if(obj==nil) return nil;
    return indexPath;
}

Приємно в цьому те shouldPerformSegueWithIdentifier:sender:, що не буде викликано, якщо вищевказаний метод поверне нуль, хоча я повторюю тест вище лише для повноти.



0

Я згоден з відповіддю Брайана

якщо я

cell.isUserInteractionEnabled = false 

тоді підпрезентація всередині комірки не буде взаємодіяти з користувачем.

На іншому сайті налаштування

cell.selectionStyle = .none

запустить метод didSelect, незважаючи на не оновлення кольору вибору.

Використання willSelectRowAt - це спосіб вирішити свою проблему. Приклад:

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {

    if indexPath.section == 0{

        if indexPath.row == 0{
            return nil
        }

    }
    else if indexPath.section == 1{

        if indexPath.row == 0{
            return nil
        }

    }

    return indexPath

}

0

tableView.allowsMultipleSelection = trueПри виборі додатка від вас потрібно буде зняти решту клітинок розділу при виборі:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if indexPath.section does not support multiple selection {
        // deselect the rest of cells
        tableView
            .indexPathsForSelectedRows?.compactMap { $0 }
            .filter { $0.section == indexPath.section && $0.row != indexPath.row }
            .forEach { tableView.deselectRow(at: $0, animated: true) }
    }
}

-1

для swift 3.0 ви можете використовувати код нижче, щоб відключити взаємодію користувача з коміркою UITableView

 cell.isUserInteractionEnabled = false
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.