Как изменить размер tableHeaderView UITableView?

103

У меня проблемы с изменением размера tableHeaderView. Это просто не работает.

1) Создайте UITableView и UIView (100 x 320 пикселей);

2) Установите UIView как tableHeaderView для UITableView;

3) Строй и работай. Все в порядке.

Теперь я хочу изменить размер tableHeaderView, поэтому я добавляю этот код в viewDidLoad:

self.tableView.autoresizesSubviews = YES;

self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;

Высота tableHeaderView должна отображаться как 200, но отображается как 100.

Если я напишу:

self.tableView.autoresizesSubviews = YES;


CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;


self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

Потом начинается с 200 высоты, как я хочу. Но я хочу иметь возможность изменять его во время выполнения.

Я тоже пробовал это, но безуспешно:

self.tableView.autoresizesSubviews = YES;

self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;

[self.tableView.tableHeaderView setNeedsLayout];
[self.tableView.tableHeaderView setNeedsDisplay];
[self.tableView setNeedsLayout];
[self.tableView setNeedsDisplay];

Дело здесь в следующем: как изменить размер tableHeaderView во время выполнения ???

Кто-нибудь может это сделать?

Спасибо

iMe

iMe
источник

Ответы:

180

К вашему сведению: я заставил это работать, изменив tableHeaderView и переустановив его. В этом случае я регулирую размер tableHeaderView, когда подвид UIWebView завершил загрузку.

[webView sizeToFit];
CGRect newFrame = headerView.frame;
newFrame.size.height = newFrame.size.height + webView.frame.size.height;
headerView.frame = newFrame;
[self.tableView setTableHeaderView:headerView];
Куби
источник
13
+1 У меня это сработало. Вызов setTableHeaderView после того, как ваше подпредставление изменило размер, является ключевым. Проблема в том, что мое подпредставление меняет размер за секунду как анимация. Теперь я пытаюсь понять, как с его помощью анимировать tableHeaderView.
Эндрю
1
Отлично, спасибо большое. Для меня это пример одного из менее желательных качеств свойств в Objective-C. У нас нет способа узнать (и нет причин, по которым мы должны знать), что установка заголовка имеет побочный эффект в виде пересчета высоты. Он должен либо делать это автоматически, когда мы обновляем высоту заголовка, либо нам нужно будет вызывать что-то подобное [tableView recalculateHeaderHeight]каждый раз.
jakeboxer 01
48
@Andrew, я знаю, что это уже почти на год поздно, но лучше поздно, чем никогда: я смог анимировать изменение высоты представления заголовка таблицы, setTableHeaderView:[tableview beginUpdates][tableview endUpdates]
заключив
2
@jasongregori удобный комментарий, который вы добавили - я вижу, что тот же метод (beginUpdates + endUpdates) не анимирует изменение высоты для tableFooterView, как это делает tableHeaderView. Вы придумали хороший способ анимировать и tableFooterView?
Крис
1
Впечатляет, это событие работает, когда выполняется внутри блока анимации UIView, хотя я не думаю, что в этом случае это очень эффективно.
Кан
12

Этот ответ старый и, по-видимому, не работает на iOS 7 и выше.

Я столкнулся с той же проблемой, и я также хотел, чтобы изменения были анимированы, поэтому я создал подкласс UIView для своего представления заголовка и добавил следующие методы:

- (void)adjustTableHeaderHeight:(NSUInteger)newHeight{
    NSUInteger oldHeight = self.frame.size.height;
    NSInteger originChange = oldHeight - newHeight;

    [UIView beginAnimations:nil context:nil];

    [UIView setAnimationDuration:1.0f];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    self.frame = CGRectMake(self.frame.origin.x, 
                        self.frame.origin.y, 
                        self.frame.size.width, 
                        newHeight);

    for (UIView *view in [(UITableView *)self.superview subviews]) {
        if ([view isKindOfClass:[self class]]) {
            continue;
        }
        view.frame = CGRectMake(view.frame.origin.x, 
                            view.frame.origin.y - originChange, 
                            view.frame.size.width, 
                            view.frame.size.height);
    }

    [UIView commitAnimations];
}

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
    [(UITableView *)self.superview setTableHeaderView:self];
}

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

Гаррет
источник
работает отлично, убрал анимацию, потому что она мне не нужна.
Джихо Канг
Работал идеально, пока не случилось iOS7 ... добавил мое решение ниже
avishic
1
Какого черта? Вы так много возитесь с внутренним устройством UITableView, что вас действительно не должно удивлять, что он не работает в новых версиях iOS ...;)
Даниэль Ринсер
10

Если вы хотите условно анимировать изменения, вы можете сделать следующее:

- (void) showHeader:(BOOL)show animated:(BOOL)animated{

    CGRect closedFrame = CGRectMake(0, 0, self.view.frame.size.width, 0);
    CGRect newFrame = show?self.initialFrame:closedFrame;

    if(animated){
        // The UIView animation block handles the animation of our header view
        [UIView beginAnimations:nil context:nil];
        [UIView setAnimationDuration:0.3];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        // beginUpdates and endUpdates trigger the animation of our cells
        [self.tableView beginUpdates];
    }

    self.headerView.frame = newFrame;
    [self.tableView setTableHeaderView:self.headerView];

    if(animated){
        [self.tableView endUpdates];
        [UIView commitAnimations];
    }
}

Обратите внимание, что анимация состоит из двух частей:

  1. Анимация ячеек под tableHeaderView. Это делается с помощью beginUpdatesиendUpdates
  2. Анимация фактического вида заголовка. Это делается с помощью UIViewблока анимации.

Чтобы синхронизировать эти две анимации animationCurve, необходимо установить значение UIViewAnimationCurveEaseInOutи продолжительность.0.3 , которые, похоже, использует UITableView для своей анимации.

Обновить

Я создал проект Xcode на gihub, который делает это. Ознакомьтесь с проектом ResizeTableHeaderViewAnimatedв besi / ios-quickies

Скриншот

Беси
источник
2
Этот метод работает, но не работает для представлений таблиц с заголовками разделов. Когда вы используете начальные / конечные обновления, это мешает блоку анимации, оставляя повторяющиеся заголовки разделов.
BlueFish
9

Я думаю, это должно сработать, если вы просто установите высоту myHeaderView следующим образом:

CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;

self.tableView.tableHeaderView = myHeaderView;
Грег Мартин
источник
Это действительно работает, но только если используется в viewDidLayoutSubviews
KoCMoHaBTa
6

Использовалось решение @garrettmoon выше до iOS 7.
Вот обновленное решение, основанное на @garrettmoon:

- (void)adjustTableHeaderHeight:(NSUInteger)newHeight animated:(BOOL)animated {

    [UIView beginAnimations:nil context:nil];

    [UIView setAnimationDuration:[CATransaction animationDuration]];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    self.frame = CGRectMake(self.frame.origin.x,
                        self.frame.origin.y,
                        self.frame.size.width,
                        newHeight);

    [(UITableView *)self.superview setTableHeaderView:self];

    [UIView commitAnimations];
}

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
    [(UITableView *)self.superview setTableHeaderView:self];
}
авишик
источник
5

У меня это сработало на iOS 7 и 8. Этот код работает на контроллере табличного представления.

[UIView animateWithDuration:0.3 animations:^{
    CGRect oldFrame = self.headerView.frame;
    self.headerView.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, oldFrame.size.width, newHeight);
    [self.tableView setTableHeaderView:self.headerView];
}];
Дарси Рейнер
источник
4

Это потому, что установщик tableHeaderView.

Вы должны установить высоту UIView перед установкой tableHeaderView. (Было бы намного проще, если бы Apple открыла исходный код этого фреймворка ...)

Томас Деко
источник
4

В iOS 9 и ниже tableHeaderViewне меняет макет после изменения размера. Эта проблема решена в iOS 10.

Чтобы решить эту проблему, просто сделайте это с помощью следующего кода:

self.tableView.tableHeaderView = self.tableView.tableHeaderView;
Клаудз
источник
2

В iOS 9.x это viewDidLoadотлично работает:

var frame = headerView.frame
frame.size.height = 11  // New size
headerView.frame = frame

headerViewобъявляется как @IBOutlet var headerView: UIView!и подключается к раскадровке, где он размещается в верхней части tableView, чтобы функционировать как tableHeaderView.

Энеко Алонсо
источник
2

Это только тогда, когда вы используете автоматический макет и устанавливаете translatesAutoresizingMaskIntoConstraints = false вид пользовательского заголовка.

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

  1. настроить макет пользовательского представления верхнего / нижнего колонтитула (подпредставления)
  2. вызывать invalidateIntrinsicContentSize()
  3. вызывать tableView.setNeedsLayout()иtableView.layoutIfNeeded()

Тогда UITableViewверхний / нижний колонтитул будет обновлен по вашему желанию. Нет необходимости устанавливать нулевой вид или сбрасывать.

Одна вещь действительно интересная для UITableView.tableHeaderViewor .tableFooterView- это то, что он UIStackViewтеряет способность управлять своим arrangedSubviews. Если вы хотите использовать в UIStackViewкачестве tableHeaderView или tableFooterView, вы должны вставлять в stackView в UIViewи переопределение UIView«S intrinsicContentSize.

Райан
источник
2

Для Swift 5 Протестированный код

override func viewDidLayoutSubviews() {
      super.viewDidLayoutSubviews()

        guard let headerView = self.tblProfile.tableHeaderView else {
            return
        }

        let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)

        if headerView.frame.size.height != size.height {
            headerView.frame.size.height = size.height
            self.tblProfile.tableHeaderView = headerView
            self.tblProfile.layoutIfNeeded()
        }
    }

Примечание: вам нужно указать все ограничения подвида сверху, снизу, впереди и в конце. Так он приобретет весь необходимый размер.

Ссылка взята из: https://useyourloaf.com/blog/variable-height-table-view-header/

Хардик Таккар
источник
1

Настройка высоты для просмотра заголовка собственности tableView.tableHeaderViewв , viewDidLoadкажется , не работа, заголовок высота вид еще не меняется , как и ожидалось.

После борьбы с этой проблемой много попыток. Я обнаружил, что вы можете изменить высоту, вызвав логику создания представления заголовка внутри - (void)didMoveToParentViewController:(UIViewController *)parentметода.

Таким образом, пример кода будет выглядеть так:

- (void)didMoveToParentViewController:(UIViewController *)parent {
    [super didMoveToParentViewController:parent];

    if ( _tableView.tableHeaderView == nil ) {
        UIView *header = [[[UINib nibWithNibName:@"your header view" bundle:nil] instantiateWithOwner:self options:nil] firstObject];

        header.frame = CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), HeaderViewHeight);

        [_tableView setTableHeaderView:header];
    }
}
Enix
источник
0

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

 - (id)initWithFrame:(CGRect)aRect {

    CGRect frame = [[UIScreen mainScreen] applicationFrame];

    if ((self = [super initWithFrame:CGRectZero])) {

        // Ugly initialization behavior - initWithFrame will not properly honor the frame we pass
        self.frame = CGRectMake(0, 0, frame.size.width, 200);

        // ...
    }
}

Преимущество этого в том, что он лучше инкапсулируется в код вашего представления.

Харальд Шуберт
источник
Получение кадра из UIScreen- плохая идея, как вы будете повторно использовать свое представление?
Зорайр
0

Я реализовал анимированное изменение высоты заголовка таблицы, чтобы при касании он расширялся до всего экрана. Однако код может помочь в других случаях:

// Swift
@IBAction func tapped(sender: UITapGestureRecognizer) {

    self.tableView.beginUpdates()       // Required to update cells. 

    // Collapse table header to original height
    if isHeaderExpandedToFullScreen {   

        UIView.animateWithDuration(0.5, animations: { () -> Void in
            self.scrollView.frame.size.height = 110   // original height in my case is 110
        })

    }
    // Expand table header to overall screen            
    else {      
        let screenSize = self.view.frame           // "screen" size

        UIView.animateWithDuration(0.5, animations: { () -> Void in
            self.scrollView.frame.size.height = screenSize.height
        })
    }

    self.tableView.endUpdates()  // Required to update cells. 

    isHeaderExpandedToFullScreen= !isHeaderExpandedToFullScreen  // Toggle
}
Александр Волков
источник
0

Заголовок изменения размера UITableView - UISearchBar с областью действия

Я хотел, чтобы UITableViewв UISearchBarкачестве заголовка таблицы у меня была иерархия, которая выглядит так

UITableView
  |
  |--> UIView
  |     |--> UISearchBar
  |
  |--> UITableViewCells

UISearchBarDelegate методы

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

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
    searchBar.showsScopeBar = YES;
    [UIView animateWithDuration:0.2f animations:^{
        [searchBar sizeToFit];
        CGFloat height = CGRectGetHeight(searchBar.frame);

        CGRect frame = self.tableView.tableHeaderView.frame;
        frame.size.height = height;
        self.tableHeaderView.frame = frame;
        self.tableView.tableHeaderView = self.tableHeaderView;
    }];

    [searchBar setShowsCancelButton:YES animated:YES];
    return YES;
}

- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
{
    searchBar.showsScopeBar = NO;
    [UIView animateWithDuration:0.f animations:^{
        [searchBar sizeToFit];

        CGFloat height = CGRectGetHeight(searchBar.frame);

        CGRect frame = self.tableView.tableHeaderView.frame;
        frame.size.height = height;
        self.tableHeaderView.frame = frame;
        self.tableView.tableHeaderView = self.tableHeaderView;
    }];

    [searchBar setShowsCancelButton:NO animated:YES];
    return YES;
}
Кэмерон Лоуэлл Палмер
источник
0

Если настраиваемый headerView разработан с использованием автоопределения, а headerView необходимо обновить после веб-выборки или аналогичной ленивой задачи. затем в iOS-Swift я сделал это и обновил свой headerView, используя следующий код:

//to reload your cell data
self.tableView.reloadData()
dispatch_async(dispatch_get_main_queue(),{
// this is needed to update a specific tableview's headerview layout on main queue otherwise it's won't update perfectly cause reloaddata() is called
  self.tableView.beginUpdates()
  self.tableView.endUpdates()
} 
Рафат тукир Рафсун
источник
0

Очевидно, к настоящему времени Apple должна была реализовать UITableViewAutomaticDimension для tableHeaderView и tableFooterView ...

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

CGSize   s  = [ self  systemLayoutSizeFittingSize : UILayoutFittingCompressedSize ];
CGRect   f  = [ self  frame ];

f.size   = s;

[ self  setFrame : f ];
digitaldaemon
источник
0

Если ваш tableHeaderView - это веб-просмотр с регулируемым содержанием, вы можете попробовать:

[self.webView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    self.webView.height = self.webView.scrollView.contentSize.height;
    self.tableView.tableHeaderView = self.webView;
}

Тестировал на iOS9 и iOS11, работал хорошо.

无 夜 之 星辰
источник
-3

Пробовали [self.tableView reloadData]после изменения высоты?

codelogic
источник
1
Речь идет о tableHeaderView, который представляет собой статическое представление. - [UITableView reloadData]предназначен только для динамических представлений (ячеек), а также для заголовков разделов, которые, как вы, очевидно, думали, имели в виду;)
Джулиан Ф. Вайнерт