UIScrollView прокрутить вниз программно

298

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

Nico
источник

Ответы:

626

Вы можете использовать setContentOffset:animated:функцию UIScrollView для прокрутки к любой части представления содержимого. Вот некоторый код, который будет прокручиваться вниз, при условии, что ваш scrollView self.scrollView:

CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];

Надеюсь, это поможет!

Бен Готоу
источник
25
Вы, вероятно, хотите выполнить следующее для прокрутки вниз, а не из вида прокрутки: CGPoint bottomOffset = CGPointMake (0, [self.scrollView contentSize] .height - self.scrollView.frame.size.height);
Grantland Chew
70
Вы не принимаете во внимание вставки. Я думаю, что вы должны предпочесть этот bottomOffset:CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
Мартин
1
это не работает в ландшафтном режиме! не полностью прокрутить до дна
Мо Фарханд
1
Он не будет работать должным образом, если contentSizeниже, чем bounds. Так должно быть так:scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
Bartłomiej Semańczyk
1
Это обрабатывает нижнюю вставку и выходит за пределы 0:let bottomOffsetY = max(collectionView.contentSize.height - collectionView.bounds.height + collectionView.contentInset.bottom, 0)
Чувственный
136

Быстрая версия принятого ответа для легкого копирования копии:

let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height)
scrollView.setContentOffset(bottomOffset, animated: true)
Esqarrouth
источник
3
bottomOffset должен быть разрешен, а не var в вашем примере.
Гоббс Тайг
правда, изменил это. SWIFT2 материал :)
Esqarrouth
9
вам нужно проверить, если scrollView.contentSize.height - scrollView.bounds.size.height> 0, иначе вы получите странные результаты
iluvatar_GR
2
Это решение не идеально, когда у вас есть новая панель навигации.
Стремительный Кролик
@Josh, все еще жду дня, когда ТАК узнает об этом
Александр Г
53

Самое простое решение:

[scrollview scrollRectToVisible:CGRectMake(scrollview.contentSize.width - 1,scrollview.contentSize.height - 1, 1, 1) animated:YES];
Хай хв
источник
Спасибо. Я забыл, что изменил свой вид прокрутки на UITableView, и этот код работал и для UITableView, FYI.
LevinsonTechnologies
1
Почему бы просто: [scrollview scrollRectToVisible: CGRectMake (0, scrollview.contentSize.height, 1, 1) animated: YES];
Йоханнес
29

Просто улучшение существующего ответа.

CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];

Он также заботится о нижней вставке (если вы используете ее для настройки вида прокрутки, когда клавиатура видна)

vivek241
источник
Это должен быть правильный ответ ... а не другие, которые сломаются, если у них когда-нибудь появится клавиатура.
Бен Гильдия
20

Быстрая реализация:

extension UIScrollView {
   func scrollToBottom(animated: Bool) {
     if self.contentSize.height < self.bounds.size.height { return }
     let bottomOffset = CGPoint(x: 0, y: self.contentSize.height - self.bounds.size.height)
     self.setContentOffset(bottomOffset, animated: animated)
  }
}

используй это:

yourScrollview.scrollToBottom(animated: true)
дуань
источник
19

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

Правильное решение состоит в том, чтобы прокрутить нижнюю часть содержимого до нижней части представления прокрутки, например, так ( svэто UIScrollView):

CGSize csz = sv.contentSize;
CGSize bsz = sv.bounds.size;
if (sv.contentOffset.y + bsz.height > csz.height) {
    [sv setContentOffset:CGPointMake(sv.contentOffset.x, 
                                     csz.height - bsz.height) 
                animated:YES];
}
матовый
источник
1
Не должно ли это быть if (sv.contentOffset.y + csz.height > bsz.height) {?
i_am_jorf
@jeffamaphone - Спасибо, что спросили. На самом деле на данный момент я даже не уверен, какой цели условие должно было служить! Это setContentOffset:команда, которая важна.
Мэтт
Я полагаю, чтобы избежать вызова setContentOffset, если он ничего не собирается делать?
i_am_jorf
@jeffamaphone - Раньше считалось, что после поворота устройства представление прокрутки может заканчиваться так, чтобы его нижнее содержимое находилось выше нижней части кадра (потому что если мы поворачиваем представление прокрутки, его смещение содержимого остается постоянным, но высота представления прокрутки может увеличиться из-за для авторазмера). Условие гласит: если это произойдет, поместите содержимое внизу внизу кадра. Но было глупо с моей стороны включить условие, когда я вставил код. Условием является правильно тестирование для того, что он испытывал к, хотя.
Мэтт
15

Решение Swift 2.2 с contentInsetучетом

let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)

Это должно быть в расширении

extension UIScrollView {

  func scrollToBottom() {
    let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height + contentInset.bottom)
    setContentOffset(bottomOffset, animated: true)
  }
}

Обратите внимание, что вы можете проверить, если bottomOffset.y > 0перед прокруткой

onmyway133
источник
14

Что если contentSizeниже чемbounds ?

Для Свифта это:

scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
Бартломей Семанчик
источник
13

Пролистать наверх

- CGPoint topOffset = CGPointMake(0, 0);
- [scrollView setContentOffset:topOffset animated:YES];

Прокрутить до низа

- CGPoint bottomOffset = CGPointMake(0, scrollView.contentSize.height - self.scrollView.bounds.size.height);
 - [scrollView setContentOffset:bottomOffset animated:YES];
Тахир Васим
источник
7

Я также нашел другой полезный способ сделать это в случае, если вы используете UITableview (который является подклассом UIScrollView):

[(UITableView *)self.view scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
Nico
источник
К вашему сведению, это только возможность UITableView
OhadM
7

Использование setContentOffset:animated:функции UIScrollView для прокрутки вниз в Swift.

let bottomOffset : CGPoint = CGPointMake(0, scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)
Ким
источник
5

Похоже, что все ответы здесь не приняли во внимание безопасную область. Начиная с iOS 11, в iPhone X появилась безопасная зона. Это может повлиять на scrollViewcontentInset .

Для iOS 11 и выше, чтобы правильно прокрутить до конца с включенным содержимым. Вы должны использовать adjustedContentInsetвместо contentInset. Проверьте этот код:

  • Swift:
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.adjustedContentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)
  • Objective-C
CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.adjustedContentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];
  • Быстрое расширение (это сохраняет оригинал contentOffset.x):
extension UIScrollView {
    func scrollsToBottom(animated: Bool) {
        let bottomOffset = CGPoint(x: contentOffset.x,
                                   y: contentSize.height - bounds.height + adjustedContentInset.bottom)
        setContentOffset(bottomOffset, animated: animated)
    }
}

Ссылки:

Хунхао Чжан
источник
5

С (необязательно) footerViewи contentInset, решение:

CGPoint bottomOffset = CGPointMake(0, _tableView.contentSize.height - tableView.frame.size.height + _tableView.contentInset.bottom);
if (bottomOffset.y > 0) [_tableView setContentOffset: bottomOffset animated: YES];
Pieter
источник
4

Swift:

Вы можете использовать расширение как это:

extension UIScrollView {
    func scrollsToBottom(animated: Bool) {
        let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height)
        setContentOffset(bottomOffset, animated: animated)
    }
}

Использование:

scrollView.scrollsToBottom(animated: true)
Мауро Гарсия
источник
3

Валдыр, надеюсь, это тебе поможет

CGPoint bottomOffset = CGPointMake(0, [textView contentSize].height - textView.frame.size.height);

if (bottomOffset.y > 0)
 [textView setContentOffset: bottomOffset animated: YES];
Миша
источник
2

Категория на помощь!

Добавьте это к общему заголовку утилиты где-нибудь:

@interface UIScrollView (ScrollToBottom)
- (void)scrollToBottomAnimated:(BOOL)animated;
@end

А затем к этой реализации утилиты:

@implementation UIScrollView(ScrollToBottom)
- (void)scrollToBottomAnimated:(BOOL)animated
{
     CGPoint bottomOffset = CGPointMake(0, self.contentSize.height - self.bounds.size.height);
     [self setContentOffset:bottomOffset animated:animated];
}
@end

Затем реализуйте его, где хотите, например:

[[myWebView scrollView] scrollToBottomAnimated:YES];
BadPirate
источник
Всегда, всегда, всегда, всегда используйте категории! Идеальный :)
Толстяк
2

Для горизонтального ScrollView

Если я вам нравлюсь, у вас есть Horizontal ScrollView и вы хотите прокрутить его до конца (в моем случае до самого правого), вам нужно изменить некоторые части принятого ответа:

Objective-C

CGPoint rightOffset = CGPointMake(self.scrollView.contentSize.width - self.scrollView.bounds.size.width + self.scrollView.contentInset.right, 0 );
[self.scrollView setContentOffset:rightOffset animated:YES];

стриж

let rightOffset: CGPoint = CGPoint(x: self.scrollView.contentSize.width - self.scrollView.bounds.size.width + self.scrollView.contentInset.right, y: 0)
self.scrollView.setContentOffset(rightOffset, animated: true)
Esmaeil
источник
2

Если вы как-то измените scrollView contentSize (например, добавьте что-то в stackView, которое находится внутри scrollView), вы должны вызватьscrollView.layoutIfNeeded() перед прокруткой, в противном случае он ничего не делает.

Пример:

scrollView.layoutIfNeeded()
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
if(bottomOffset.y > 0) {
    scrollView.setContentOffset(bottomOffset, animated: true)
}
Роберт Коваль
источник
1

Хороший способ обеспечить видимость нижней части вашего контента - использовать формулу:

contentOffsetY = MIN(0, contentHeight - boundsHeight)

Это гарантирует, что нижний край вашего контента всегда находится у нижнего края представления или над ним. MIN(0, ...)Требуется потому , что UITableView(и , вероятно UIScrollView) обеспечивает , contentOffsetY >= 0когда пользователь пытается прокручивать заметно щелкать contentOffsetY = 0. Это выглядит довольно странно для пользователя.

Код для реализации этого:

UIScrollView scrollView = ...;
CGSize contentSize = scrollView.contentSize;
CGSize boundsSize = scrollView.bounds.size;
if (contentSize.height > boundsSize.height)
{
    CGPoint contentOffset = scrollView.contentOffset;
    contentOffset.y = contentSize.height - boundsSize.height;
    [scrollView setContentOffset:contentOffset animated:YES];
}
jjrscott
источник
1

Если вам не нужна анимация, это работает:

[self.scrollView setContentOffset:CGPointMake(0, CGFLOAT_MAX) animated:NO];
Крыс Юрговски
источник
1

В то время как Mattрешение кажется мне правильным, необходимо учитывать также вкладку представления коллекции, если она была настроена.

Адаптированный код будет:

CGSize csz = sv.contentSize;
CGSize bsz = sv.bounds.size;
NSInteger bottomInset = sv.contentInset.bottom;
if (sv.contentOffset.y + bsz.height + bottomInset > csz.height) {
    [sv setContentOffset:CGPointMake(sv.contentOffset.x, 
                                     csz.height - bsz.height + bottomInset) 
                animated:YES];
}
tiguero
источник
1

В скором времени:

   if self.mainScroll.contentSize.height > self.mainScroll.bounds.size.height {
        let bottomOffset = CGPointMake(0, self.mainScroll.contentSize.height - self.mainScroll.bounds.size.height);
        self.mainScroll.setContentOffset(bottomOffset, animated: true)
    }
Ponja
источник
1

Решение прокрутить до последнего элемента таблицы.

Свифт 3:

if self.items.count > 0 {
        self.tableView.scrollToRow(at:  IndexPath.init(row: self.items.count - 1, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
}
MarionFlex
источник
0

Не работает для меня, когда я пытался использовать его в UITableViewControllerна self.tableView (iOS 4.1), после добавленияfooterView . Он прокручивается за границы, показывая черный экран.

Альтернативное решение:

 CGFloat height = self.tableView.contentSize.height; 

 [self.tableView setTableFooterView: myFooterView];
 [self.tableView reloadData];

 CGFloat delta = self.tableView.contentSize.height - height;
 CGPoint offset = [self.tableView contentOffset];
 offset.y += delta;

 [self.tableView setContentOffset: offset animated: YES];
valdyr
источник
0
CGFloat yOffset = scrollView.contentOffset.y;

CGFloat height = scrollView.frame.size.height;

CGFloat contentHeight = scrollView.contentSize.height;

CGFloat distance = (contentHeight  - height) - yOffset;

if(distance < 0)
{
    return ;
}

CGPoint offset = scrollView.contentOffset;

offset.y += distance;

[scrollView setContentOffset:offset animated:YES];
8suhas
источник
0

Я обнаружил, что contentSizeэто на самом деле не отражает фактический размер текста, поэтому при попытке прокрутки вниз он будет немного отключен. Лучший способ для определения фактического размера контента на самом деле использовать в NSLayoutManager«S usedRectForTextContainer:метода:

UITextView *textView;
CGSize textSize = [textView.layoutManager usedRectForTextContainer:textView.textContainer].size;

Чтобы определить, сколько текста на самом деле отображается в UITextView, вы можете рассчитать его, вычтя вставки текстового контейнера из высоты рамки.

UITextView *textView;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textViewHeight = textView.frame.size.height - textInsets.top - textInsets.bottom;

Тогда становится легко прокручиваться:

// if you want scroll animation, use contentOffset
UITextView *textView;
textView.contentOffset = CGPointMake(textView.contentOffset.x, textSize - textViewHeight);

// if you don't want scroll animation
CGRect scrollBounds = textView.bounds;
scrollBounds.origin = CGPointMake(textView.contentOffset.x, textSize - textViewHeight);
textView.bounds = scrollBounds;

Некоторые цифры для справки о том, что разные размеры представляют для пустого UITextView.

textView.frame.size = (width=246, height=50)
textSize = (width=10, height=16.701999999999998)
textView.contentSize = (width=246, height=33)
textView.textContainerInset = (top=8, left=0, bottom=8, right=0)
mikeho
источник
0

Расширьте UIScrollView, чтобы добавить метод scrollToBottom:

extension UIScrollView {
    func scrollToBottom(animated:Bool) {
        let offset = self.contentSize.height - self.visibleSize.height
        if offset > self.contentOffset.y {
            self.setContentOffset(CGPoint(x: 0, y: offset), animated: animated)
        }
    }
}
Jeune-Loup
источник
Что это добавляет к существующим ответам?
седобородый
-1

Xamarin.iOS версия принятого ответа

var bottomOffset = new CGPoint (0,
     scrollView.ContentSize.Height - scrollView.Frame.Size.Height
     + scrollView.ContentInset.Bottom);

scrollView.SetContentOffset (bottomOffset, false);
Абдур
источник
-1

Версия Xamarin.iOS для UICollectionViewпринятого ответа для простоты копирования и вставки

var bottomOffset = new CGPoint (0, CollectionView.ContentSize.Height - CollectionView.Frame.Size.Height + CollectionView.ContentInset.Bottom);          
CollectionView.SetContentOffset (bottomOffset, false);
Абдур
источник