Згрупований UITableview видалити зовнішній роздільник


77

У мене є згрупований UITableview, який створюється програмно. Також у мене є комірка з файлом xib, заповнена в tableview також програмно. Все йде нормально. Але я хочу видалити лише зовнішню роздільну лінію. Я використав код нижче, але цього разу видалив усі роздільники.

self.tableView.separatorColor = [UIColor clearColor];

це невдалий варіант для моєї ситуації. Ось скріншот, що я хочу зробити;

введіть тут опис зображення


2
Варіант перший: Видаліть усі роздільники за замовчуванням і додайте UIViews у клітинку як підроблені роздільники. Сподіваємось, є кращий варіант.
Lord Zsolt

Це той самий підхід вище. Як я вже говорив раніше, я видаляю лише зовнішній засіб верхній і нижній рядки, а не внутрішній. Якщо я видалю всі роздільники в viewview та додаю новий рядок у зовнішньому вигляді таблиці customview, все ще залишається незмінним.
serhat sezer

4
Рішення полягає у використанні TableView як Plain tableview, а не у згрупованому. У групованому форматі він розміщує роздільники на колонтитули.
іфонічний

@iphonic, ваше рішення працює, дякую :), але звичайне табличне подання все одно показує роздільник, навіть якщо в ньому немає нічого.
li2

21
Це ваш серйозний, @iphonic ?! Кому це повинно допомогти? li2? Коли хтось використовує згрупований tableView, це, швидше за все, є вагомою причиною. Ви коли-небудь бачили, що сепаратори - це не єдина різниця між згрупованим та звичайним tableView?
Джуліан Ф. Вайнерт

Відповіді:


15

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

Ось мій код:

по-перше, ви повинні зробити сепаратор очищеним

tableView.separatorColor = UIColor.clear

По-друге, у cellForRowAtфункції:

let bottomBorder = CALayer()

bottomBorder.frame = CGRect(x: 0.0, y: 43.0, width: cell.contentView.frame.size.width, height: 1.0)
bottomBorder.backgroundColor = UIColor(white: 0.8, alpha: 1.0).cgColor
cell.contentView.layer.addSublayer(bottomBorder)

тут ви побачите такий інтерфейс:

введіть тут опис зображення


22

Ви можете видалити роздільники навіть у згрупованому UITableView зі статичною коміркою:

class CustomCell: UITableViewCell {

override func layoutSubviews() {
    super.layoutSubviews()
    for view in subviews where view != contentView {
        view.removeFromSuperview()
    }
}

Але в iOS10 також буде видалено лінію між клітинками в одному розділі.
leo.tan

21

Ось моє рішення:

self.tableView.separatorColor = self.tableView.backgroundColor

@xxtesaxx, у вас повинен бути справжній тест


9
Він видаляє всі сепаратори
Тихонов Олександр

5
@Brook Збирається видалити всі роздільники. Якщо ви прочитали моє запитання, я сказав, що хочу лише видалити "зовнішні" рядки.
serhat sezer

2
@serhatsezer Так, я прочитав ваше запитання, це трюк, він робить зовнішні рядки "зникаючими", а роздільники залишаються видимими.
Брук

@Brook Але проблема виникне, якщо колір фону tableView відрізняється від кольору розділювача або Колір фону TableView має такий самий колір, як у клітинок .. скажімо, білий ..
Кауця Кану

13

Google навіть у 2018 році пропонує цю сторінку найкращим результатом цього запитання. Мені не пощастило в iOS 11 з жодною з наданих відповідей, тож ось що я придумав:

extension UITableViewCell {
    func removeSectionSeparators() {
        for subview in subviews {
            if subview != contentView && subview.frame.width == frame.width {
                subview.removeFromSuperview()
            }
        }
    }
}

Тепер виклик .removeSectionSeparators () на будь-якому екземплярі UITableViewCell вирішить проблему. Принаймні в моєму випадку розділювачі розділів є єдиними з такою ж шириною, як і сама комірка (оскільки всі інші мають відступи).

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

У підсумку я помістив це в метод cellForRowAtIndexPath безпосередньо перед тим, як повернути перезавантажену комірку:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "MyReusableIdentifier", for: indexPath)

    Timer.scheduledTimer(withTimeInterval: 0.15, repeats: false) { (timer) in
        cell.removeSectionSeparators()
    }

    return cell
}

Це не настільки елегантно, але я ще не стикався з жодними проблемами.

РЕДАГУВАТИ: Схоже, нам це теж потрібно (для повторно використаних комірок):

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    cell.removeSectionSeparators()
}

Ось до / після на скріншотах з цим кодом:

Раніше Перед видаленням сепаратора

Після Після видалення сепаратора


1
Працює як шарм, дякую. Працює ще краще, якщо ви викликаєте це в
підзаголовках

1
Це єдине, що я міг знайти, що працювало б для iOS 11 і далі. Дякую.
jstr

Я намагаюся взяти цей дзвінок willDisplayабо cellForRowAt, обидва не працювали на моїй стороні. Я використовую iOS 14 і Swift5.
ChuckZHB

9

Після перевірки ієрархії подань, схоже, кожна UITableViewCell має лише три підпрогляди: подання вмісту ( UITableViewCellContentView) та два подання роздільника ( _UITableViewCellSeparatorView). Я не прихильник динамічного створення класу від NSStrings (як і Apple 😉). Однак, оскільки contentViewa UITableViewCellдоступний без використання приватних API, рішення виявляється досить простим.

Ідея полягає в тому, щоб просто переглядати ваші підзаписи UITableViewCellта видаляти будь-які подання, які не є contentView:

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    let subviews = cell.subviews
    if subviews.count >= 3 {
        for subview in subviews {
            if subview != cell.contentView {
                subview.removeFromSuperview()
                break
            }
        }
    }
}

tableView(:willDisplayCell:forRowAtIndexPath:)викликається кілька разів під час рендерінгу подання таблиці, тому вищевказане відстежує стан, перевіряючи, скільки підпоглядів має комірка. Якщо він має три підпрограми, обидва роздільники все ще недоторкані. Він також видаляє лише один із сепараторів, але ви можете видалити обидва, видаливши break. Ви також можете вказати, чи слід видаляти верхній або нижній роздільник, перевіряючи рамку підпрогляду. Якщо початок осі у кадру дорівнює 0, це верхній роздільник. Якщо ні 0, то це дно.

Сподіваюся, це допомагає!

Swift 4, Swift 4.2 Оновлення:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    let subviews = cell.subviews
    guard subviews.count >= 3 else {
        return
    }

    for subview in subviews where NSStringFromClass(subview.classForCoder) == "_UITableViewCellSeparatorView" {
        subview.removeFromSuperview()
    }
}

1
Дякую, але такий підхід може бути проблемою для майбутніх версій iOS?
serhat sezer

2
Швидше за все, ні, якщо Apple не змінить внутрішню структуру переглядів таблиць, щоб мати кілька подань вмісту. Якщо ви хочете бути ще більш обережним, ви могли б використовувати в якості альтернативи NSStringFromClassі порівняти ці значення з "_UITableViewCellSeparatorView"і "UITableViewCellContentView"потім видалити.
Ден Ловенгерц

2
Apple змінила ієрархію подання tableViewCell з iOS 7 на iOS 8, це насправді спалило мене в проекті, оскільки я використовував такий код, тому я б рекомендував не робити цього таким чином.
Роберт Бентлі,

Якщо вас турбують застарілість або внутрішні зміни, я рекомендую використовувати #availableатрибут із порожнім тілом для будь-якої версії iOS, більшої за поточну.
Ден Ловенгерц,

8

Для зняття верхньої та нижньої частини лінії роздільника для кожної секції. Додайте це у свою статичну комірку.

override func layoutSubviews() {
    super.layoutSubviews()

    //Get the width of tableview
    let width = subviews[0].frame.width

    for view in subviews where view != contentView {
        //for top and bottom separator will be same width with the tableview width
        //so we check at here and remove accordingly
        if view.frame.width == width {
            view.removeFromSuperview()
        }
    }
}

Результат, як показано на малюнку нижче

введіть тут опис зображення


Ідеальна відповідь, але чи можемо ми досягти цього для нестандартних комірок?
Vishwas Singh

1
Це було єдине рішення, яке працювало для мене. Дякую!
Метт

6

Це справді давнє запитання, але все-таки це один із перших записів у Google при пошуку, як видалити верхній і нижній роздільники для кожного розділу.

Після деякого розслідування я з’ясував, що Apple не має можливості і просто не має наміру зробити це якось без глупо ускладнених хакерів ієрархії поглядів.

На щастя, існує абсолютно простий і легкий спосіб досягти такого вигляду:

введіть тут опис зображення

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

  1. Створіть UITableViewCellпідклас
  2. У комірці створіть @IBOutlet weak var separatorView: UIView!властивість
  3. У своїй Раскадровці виберіть комірку viewview таблиці та виберіть власну комірку як клас, який створює резервну копію комірки. Примітка : Вам не потрібно використовувати власний стиль. Ви все ще можете використовувати Basic, Subtitle тощо.
  4. Встановіть UITableViewS Separator Styleв Noneприховати роздільник за замовчуванням
  5. Перетягніть a UIViewна клітинку та змініть розмір, щоб він знаходився внизу (або вгорі) комірки. Використовуйте Size Inspectors, Autoresizingщоб закріпити його, щоб почати / закінчити / знизу і надати йому гнучку ширину (або авторозкладку, але в цьому випадку це трохи зверху)
  6. Підключіть подання до розетки вашого класу стільникового зв’язку
  7. У вашому UITableViewDataSources cellForRowAtIndexPath:встановіть isHiddenвластивість власного роздільника, виходячи з того, чи indexPath.rowє останній рядок (або перший, якщо ваш погляд знаходиться вгорі) у розділі

Ось приклад коду:

class MyCell: UITableViewCell {
    @IBOutlet weak var separatorView: UIView!
}

class ViewController: UITableViewController {


    override func numberOfSections(in tableView: UITableView) -> Int {
        return 3
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MyCell
        cell.separatorView.isHidden = indexPath.row == 2
        return cell
    }

}

І ось кілька скріншотів:

введіть тут опис зображення

введіть тут опис зображення

введіть тут опис зображення

введіть тут опис зображення

Так, це певна робота, але просто неможливо щось дістати безкоштовно під час кодування. Зрештою, ми програмісти, і кодування нам вирішувати нам. Завжди краще витратити 5-10 хвилин на це налаштування, ніж просто копіювати вставку якогось хакерського коду, який може не працювати в майбутньому, коли Apple вирішить змінити ієрархію подання.

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

Я сподіваюся, ви також відчуваєте скептицизм, коли бачите цикли for-ітерації, що перебирають підпідготовки комірок, щоб приховати або навіть видалити подання під час виконання з ієрархії подань, яку Apple надає вам, коли просто, за рогом є просте, універсальне, стабільне та надійне рішення . 7 маленьких кроків - це справді все, що вам потрібно, і їх легко зрозуміти.


Чи працює ваше рішення з видом аксесуарів або стандартними аксесуарами UITableViewCell (шеврони, інформаційні кнопки тощо)?
iur

Оскільки ви можете використовувати стилі за замовчуванням і не змушені використовувати власні стилі, я вважаю, що використання аксесуарів за замовчуванням повинно працювати без проблем.
xxtesaxx

Я спробував і не зміг змусити роздільник з’явитись під chevron у конструкторі інтерфейсів. Не намагався на пристрої. Причина цього - xcode дозволяє додавати подання лише до contentView комірки. Шеврон - це частина виду аксесуарів, яка відображається поруч із contentView.
iur

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

3

Ось що я нарешті придумав:

введіть тут опис зображення

Я не зміг знайти кращого шляху, ніж цей. Але це дуже працює для мене. Востаннє випробувано за допомогою Xcode версії 7.3.1 (7D1014). Ця процедура була виконана за допомогою розкадрування.

В основному я додаю UIView 0,5 pt Height на UITableViewCell, а потім встановлюю колір тла для цього UIView. Встановити батьківський розділювач UITableView як Ні.

Ось деталі:

Враховуючи, що ви вже встановили UITableViewCell, спеціальний або за замовчуванням. На першому етапі встановіть розділювач UITableView як None.

введіть тут опис зображення

Далі додайте UIView висотою 1 пт і встановіть фон як вам потрібно, у моєму випадку це червоний.

введіть тут опис зображення

введіть тут опис зображення

Почніть встановлювати обмеження. Проблема полягає в тому, щоб встановити висоту UIView 0,5 пт. Це єдине Проблемне питання для цього робочого циклу.

UIView з висотою 0,5 пт:

Спільний спосіб встановити 0,5 pt висоти UIView.

Спочатку (1) закріпіть вигляд, а потім (2) встановіть висоту 0,5. Натисніть Enter.

введіть тут опис зображення

Нарешті, ваш UIView буде виглядати приблизно так, як показано нижче.

введіть тут опис зображення

Я не зміг встановити висоту як 0,5, крім цього способу.


2

Тут рішення. Це для статичних комірок. Якщо ви хочете динамічну, просто перепишіть "count". Сподіваюся, це допоможе.

extension NSObject {
   var theClassName: String {
       return    NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
   }
}

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.separatorStyle = .None
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    let count = tableView.numberOfRowsInSection(indexPath.section)
    if ( indexPath.row != count - 1 ) {
        for view in cell.subviews {
            if view.theClassName == "_UITableViewCellSeparatorView" {
                view.backgroundColor = UIColors.redColor()
            }
        }
    }
}

1
Це виглядає як жахливо складне рішення. Якщо у вас є статичний вигляд таблиці, чому б не встановити роздільник на none та додати деякі власні подання з висотою 1p до статичних комірок, де ви їх хочете?
xxtesaxx

Чесно кажучи, я не є великим шанувальником налаштування в рамках iOS, оскільки його можна змінити пізніше. Тож може виникнути проблема із випущеним додатком.
serhat sezer

2

iOS 14, Swift 5

У власному класі комірок:

override func layoutSubviews(){
    super.layoutSubviews()
    
    for subview in subviews where (subview != contentView && abs(subview.frame.width - frame.width) <= 0.1 && subview.frame.height < 2) {
        subview.removeFromSuperview()                           //option #1 -- remove the line completely
        //subview.frame = subview.frame.insetBy(dx: 16, dy: 0)  //option #2 -- modify the length
    }
}

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

  1. він видалив виділення за замовчуванням / вибраний фоновий вигляд, тому я додав додаткову перевірку висоти підпрогляду.
  2. Я вклав свій код layoutSubviews()і не мав жодної проблеми.
  3. Я застосував наближення між двома CGFloats замість того, щоб використовувати оператор рівності ==, що для мене звучить схильним до помилок.

Я додав "варіант №2" у код, оскільки це рішення, яке я особисто шукав (я хотів підтримувати роздільник, але хотів, щоб він був на тому ж рівні відступу, що і звичайні роздільники комірок, у моєму випадку значення 16).


Нарешті, я отримую відпрацьовану відповідь! Для iOS 14, Swift 5.
ChuckZHB

1

Випробував різні рішення на основі cell.separatorInset = UIEdgeInsetsMake()обхідного шляху , жодне з них не працює належним чином.

Для мого проекту UITableViewStyleGroupedна базі iOS11 це вдалося:

self.tableView.separatorColor = self.tableView.backgroundColor;

1

На основі відповіді кухаря, але без таймера:

override func didAddSubview(_ subview: UIView) {
    super.didAddSubview(subview)
    if NSStringFromClass(subview.classForCoder) != "UITableViewCellContentView" && subview.frame.width == frame.width {
        subview.removeFromSuperview()
    }
}

Просто додайте це в підклас комірки, і вам більше нічого не потрібно. Працює на iOS 12.


0

У мене була подібна проблема, коли у мене був згрупований UITableView із користувацькими комірками, усі розроблені за допомогою Interface Build як .xib-файли. Клітинки мали білий фон, видаляли роздільники за замовчуванням та додавали власні. У мене також були власні подання заголовків для кожного розділу. Я встановив висоту цих подань заголовка до 44 (може бути що завгодно ...). Між моїми секціями був вигляд висоти в 1 бал, що здавалося дивним. Очевидно, система додає деякий чіткий фоновий вигляд між розділами, навіть якщо ми вказали спеціальну висоту, скажімо, 44, а користувацький вид заголовка, який ми повертаємо, має білий (або якийсь конкретний колір фону). Колір, який ми бачимо за цим чітким видом, насправді є кольором фону табличного подання. У моєму випадку і вигляд таблиці, і комірки повинні бути білого кольору, і встановлення білого фону подання таблиці вирішило проблему (принаймні візуально, але це все одно я хотів). Другим рішенням було б зберегти стиль табличного подання як звичайний, але реалізувати метод делегування UITableView, щоб повернути 2 або більше розділів, а також створити власні заголовки, якщо вам потрібно. Але це призведе до того, що подання заголовків на деякий час (при прокрутці) прилипають до верху, поки подання заголовка наступного розділу не наблизиться до нього, а потім він теж не почне підніматися вгору (і це може бути не те, що ви насправді хочете, але є може бути простим способом виправити це, хоча не впевнений).


0

Ви можете отримати доступ до подання за допомогою

override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {

    let header = view as! UITableViewHeaderFooterView 
    header.backgroundView . .....

0

Спробуйте це, це видалить верхній розділовий рядок.

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.row == 0 && indexPath.section == 0) {
        for (UIView *view in  cell.subviews) {
            if ([NSStringFromClass([view class]) containsString:@"CellSeparator"]) {
                if (view.frame.origin.y == 0 && CGRectGetHeight(view.frame) < 1.01 ) { //Hide first UITableViewCellSeparatorView
                    NSLog(@"%s Remove View From Cell[Section:0 Row:0] for top border [%@]:%@",__FUNCTION__,NSStringFromClass([view class]),view);
                    view.hidden = YES; //Hide
                }
            }
        }
    }
}


0

iOS 10 ~ 13 видаляє лише лінію стопи головки розділу.

-(void)layoutSubviews{
    [super layoutSubviews];
    //for iOS10~iOS13: only remove section head foot line
    for (UIView * v in self.subviews) {
        if ( v != self.contentView &&
            (v.frame.size.width == self.frame.size.width)){
            [v removeFromSuperview];
        }
    }
}

якщо хочете видалити весь рядок:

    for (UIView * v in self.subviews) {
        if (v != self.contentView){
            [v removeFromSuperview];
        }
    }

0
 override func layoutSubviews() {
    super.layoutSubviews()
    subviews.filter { $0 != contentView && $0.frame.width == frame.width }.first?.removeFromSuperview()
}

3
Будь ласка, не публікуйте лише код як відповідь, але також надайте пояснення, що робить ваш код і як він вирішує проблему питання. Відповіді з поясненнями, як правило, є більш корисними та якісними, і, швидше за все, залучають прихильників.
Matt D

-1

Ось рішення Swift, яке працює на iOS 8 та 9.

Визначте протокол із реалізацією за замовчуванням:

protocol CellSeparatorRemovable { }

extension CellSeparatorRemovable {
    func removeSeparatorLinesFromCell(cell: UITableViewCell, section: Int, row: Int, indexPath: NSIndexPath) {
        guard (section, row) == (indexPath.section, indexPath.row) else { return }

        for view in cell.subviews where view != cell.contentView {
            view.removeFromSuperview()
        }
    }
}

Потім, де б ви не хотіли його використовувати, дотримуйтесь CellSeparatorRemovableпротоколу та викликайте його метод із …willDisplayCell…:

class SomeVC: UITableViewController, CellSeparatorRemovable {
    override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        removeSeparatorLinesFromCell(cell, section: 1, row: 1, indexPath: indexPath)
    }
}

Це мінімальне рішення; Вам може знадобитися його рефакторинг, якщо ви маєте справу з багатьма комірками, щоб уникнути надмірної рекурсії та / або проблем із повторним використанням комірок.


Привіт @Aaron. Я спробував ваше рішення, але роздільник зникає лише після перемальовування комірки (коли ви прокручуєте екран, а він виходить з екрана, а потім прокручуєте назад), але при першому завантаженні екрана сепаратор є. Я спробував cell.setNeedsLayout () і cell.setNeedsDisplay (). Будь-яка ідея, як це вирішити?
ПМТ

1
Наприкінці мені довелося підклас UITableViewCell і замінити layoutSubviews ()
PMT

2
зламати ієрархію подання, мабуть, не є гарним рішенням. Це може змінитися в будь-який час. Також немає необхідності в спеціальному малюванні. Будь ласка, ознайомтесь з моєю відповіддю про те, як краще вирішити проблему операційних систем
xxtesaxx
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.