Подтвердить что ты не робот

UITableView в UIScrollView с использованием автозапуска

В настоящий момент я использую UITableView вместе с другими представлениями, содержащимися в UIScrollView. Я хочу, чтобы UITableView имел свою высоту так же, как и ее высоту содержимого.

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

У меня сделана вставка/удаление, хотя на данный момент он не обновляет UIScrollView, который является его супервизором, так что размер содержимого UIScrollView пересчитывается, а UITableView вместе с другими представлениями в UIScrollView отображаются правильно.

Как я могу это реализовать, чтобы отрегулировать размер UIScrollView, и его содержимое было правильно изложено при изменении содержимого UITableView? В настоящее время я использую автомат.

4b9b3361

Ответ 1

Прежде всего, это те другие виды (сиблинги табличного представления) строго выше и ниже представления таблицы? Если да, считаете ли вы, что просмотр табличного представления прокручивается в обычном режиме, и помещение этих внешних представлений в представления заголовка и нижнего колонтитула таблицы? Тогда вам не потребуется прокрутка.

Во-вторых, вы можете прочитать Техническую ноту TN2154: UIScrollView и Autolayout, если вы еще этого не сделали.

В-третьих, учитывая информацию в этой технической заметке, я могу придумать несколько способов сделать то, что вы хотите. Самый чистый - это, вероятно, создание подкласса UITableView, реализующего метод intrinsicContentSize. Реализация тривиальна:

@implementation MyTableView

- (CGSize)intrinsicContentSize {
    [self layoutIfNeeded]; // force my contentSize to be updated immediately
    return CGSizeMake(UIViewNoIntrinsicMetric, self.contentSize.height);
}

@end

Затем просто дайте авто макету использовать внутренний размер содержимого таблицы. Создайте ограничения между подзонами представления прокрутки (включая представление таблицы), чтобы их выложить, и убедитесь, что есть ограничения для всех четырех ребер прокрутки.

Вероятно, вам нужно отправить invalidateIntrinsicContentSize в представление таблицы в соответствующие моменты времени (когда вы добавляете или удаляете строки или изменяете высоту строк). Возможно, вы могли бы просто переопределить соответствующие методы в MyTableView для этого. Например. do [self invalidateIntrinsicContentSize] в -endUpdates, -reloadData, - insertRowsAtIndexPaths:withRowAnimation: и т.д.

Вот результат моего тестирования:

представление таблицы с внутренним размером содержимого в прокрутке

Вид прокрутки имеет светло-синий фон. Красная верхняя метка и синяя нижняя метка являются братьями и сестрами вида таблицы внутри прокрутки.

Здесь полный исходный код для контроллера представления в моем тесте. Нет xib файла.

#import "ViewController.h"
#import "MyTableView.h"

@interface ViewController () <UITableViewDataSource, UITableViewDelegate>

@end

@implementation ViewController

- (void)loadView {
    UIView *view = [[UIView alloc] init];
    self.view = view;

    UIScrollView *scrollView = [[UIScrollView alloc] init];
    scrollView.translatesAutoresizingMaskIntoConstraints = NO;
    scrollView.backgroundColor = [UIColor cyanColor];
    [view addSubview:scrollView];

    UILabel *topLabel = [[UILabel alloc] init];
    topLabel.translatesAutoresizingMaskIntoConstraints = NO;
    topLabel.text = @"Top Label";
    topLabel.backgroundColor = [UIColor redColor];
    [scrollView addSubview:topLabel];

    UILabel *bottomLabel = [[UILabel alloc] init];
    bottomLabel.translatesAutoresizingMaskIntoConstraints = NO;
    bottomLabel.text = @"Bottom Label";
    bottomLabel.backgroundColor = [UIColor blueColor];
    [scrollView addSubview:bottomLabel];

    UITableView *tableView = [[MyTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
    tableView.translatesAutoresizingMaskIntoConstraints = NO;
    tableView.dataSource = self;
    tableView.delegate = self;
    [scrollView addSubview:tableView];

    UILabel *footer = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 30)];
    footer.backgroundColor = [UIColor greenColor];
    footer.text = @"Footer";
    tableView.tableFooterView = footer;

    NSDictionary *views = NSDictionaryOfVariableBindings(
        scrollView, topLabel, bottomLabel, tableView);
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:|[scrollView]|"
        options:0 metrics:nil views:views]];
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"H:|[scrollView]|"
        options:0 metrics:nil views:views]];
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:|[topLabel][tableView][bottomLabel]|"
        options:0 metrics:nil views:views]];
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"H:|[topLabel]|"
        options:0 metrics:nil views:views]];
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"H:|-8-[tableView]-8-|"
        options:0 metrics:nil views:views]];
    [view addConstraint:[NSLayoutConstraint
        constraintWithItem:tableView attribute:NSLayoutAttributeWidth
        relatedBy:NSLayoutRelationEqual
        toItem:view attribute:NSLayoutAttributeWidth
        multiplier:1 constant:-16]];
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"H:|[bottomLabel]|"
        options:0 metrics:nil views:views]];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 20;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    cell.textLabel.text = [NSString stringWithFormat:@"Row %d", indexPath.row];
    return cell;
}

@end

Ответ 2

В дополнение к запросу rob есть быстрый пример самоизменяемого подкласса UITableView:

Swift 2.x

class IntrinsicTableView: UITableView {

    override var contentSize:CGSize {
        didSet {
            self.invalidateIntrinsicContentSize()
        }
    }


    override func intrinsicContentSize() -> CGSize {
        self.layoutIfNeeded()
        return CGSizeMake(UIViewNoIntrinsicMetric, contentSize.height)
    }

}

Swift 3.x или Swift 4.x

class IntrinsicTableView: UITableView {

    override var contentSize:CGSize {
        didSet {
            self.invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        self.layoutIfNeeded()
        return CGSize(width: UIViewNoIntrinsicMetric, height: contentSize.height)
    }

}

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

Ответ 3

Вот версия obj-C. Он основан на решении от пользователя @MuHAOS

@implementation SizedTableView

- (void)setContentSize:(CGSize)contentSize {
  [super setContentSize:contentSize];
  [self invalidateIntrinsicContentSize];
}

- (CGSize)intrinsicContentSize {
  [self layoutIfNeeded]; // force my contentSize to be updated immediately
  return CGSizeMake(UIViewNoIntrinsicMetric, self.contentSize.height);
}


@end

Ответ 4

@MuHAOS и код @klemen-zagar очень помогли мне, но фактически вызвали проблему с производительностью, вызвав бесконечный цикл компоновки, когда tableview содержится в представлении стека, который сам содержится в представлении прокрутки. См. Мое решение ниже.

@interface AutoSizingTableView ()
@property (nonatomic, assign) BOOL needsIntrinsicContentSizeUpdate;
@end

@implementation AutoSizingTableView

- (void)setContentSize:(CGSize)contentSize
{
    [super setContentSize:contentSize];

    self.needsIntrinsicContentSizeUpdate = YES;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if (!self.needsIntrinsicContentSizeUpdate) {
            return;
        }

        self.needsIntrinsicContentSizeUpdate = NO;
        [self layoutIfNeeded];
        [self invalidateIntrinsicContentSize];
    });
}

- (CGSize)intrinsicContentSize
{
    return CGSizeMake(UIViewNoIntrinsicMetric, self.contentSize.height);
}

@end

Ответ 5

вы можете добавить представление в виде headerview и footview просмотра tableview. потому что tableview является подпунктом scrollview. следуйте нижеприведенному примеру.

UILabel *topLabel = [[UILabel alloc] init];
topLabel.translatesAutoresizingMaskIntoConstraints = NO;
topLabel.text = @"Top Label";
topLabel.backgroundColor = [UIColor redColor];
tableView.tableFooterView = topLabel;

UILabel *footer = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 30)];
     footer.backgroundColor = [UIColor greenColor];
     footer.text = @"Footer";
     tableView.tableFooterView = footer;

а также вы можете добавить headerview и footerview tableview, используя простой вид перетаскивания в таблицу в раскадровке и взять IBOutlet из этих представлений.