Programming

보기가 나타나기 전에 iPhone에서 UITableView의 맨 아래로 스크롤하는 방법

procodes 2020. 7. 1. 22:10
반응형

보기가 나타나기 전에 iPhone에서 UITableView의 맨 아래로 스크롤하는 방법


나는이 UITableView변수 높이의 세포로 채워된다. 뷰를 볼 때 테이블을 맨 아래로 스크롤하고 싶습니다.

나는 현재 다음과 같은 기능을 가지고 있습니다

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[log count]-1 inSection:0];
[self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];

log는 각 셀의 내용을 구성하는 객체를 포함하는 가변 배열입니다.

위의 코드는 잘 작동 viewDidAppear하지만 뷰가 처음 나타날 때 테이블 상단을 표시 한 다음 맨 아래로 점프하면 불행한 부작용이 있습니다. table view가 나타나기 전에 하단으로 스크롤 할 수 있다면 선호합니다 .

내가 스크롤을 시도 viewWillAppear하고 viewDidLoad있지만, 두 경우 모두 데이터가 테이블에로드 아직 예외를 던져 모두되지 않았습니다.

내가 가진 것이 가능한 전부라고 말한 경우에도 모든 지침은 대단히 감사하겠습니다.


나는 부름 [table setContentOffset:CGPointMake(0, CGFLOAT_MAX)]이 당신이 원하는 것을 할 것이라고 믿습니다 .


에서 야곱의 대답 이 코드는 다음과 같습니다

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    if (self.messagesTableView.contentSize.height > self.messagesTableView.frame.size.height) 
    {
        CGPoint offset = CGPointMake(0, self.messagesTableView.contentSize.height - self.messagesTableView.frame.size.height);
        [self.messagesTableView setContentOffset:offset animated:YES];
    }
}

가장 쉬운 방법은 다음과 같습니다.

if (self.messages.count > 0)
{
    [self.tableView 
        scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.messages.count-1 
        inSection:0] 
        atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

스위프트 3 버전 :

if messages.count > 0 {
    userDefinedOptionsTableView.scrollToRow(at: IndexPath(item:messages.count-1, section: 0), at: .bottom, animated: true)
}

컨텐츠의 정확한 끝으로 스크롤해야하는 경우 다음과 같이 수행 할 수 있습니다.

- (void)scrollToBottom
{
    CGFloat yOffset = 0;

    if (self.tableView.contentSize.height > self.tableView.bounds.size.height) {
        yOffset = self.tableView.contentSize.height - self.tableView.bounds.size.height;
    }

    [self.tableView setContentOffset:CGPointMake(0, yOffset) animated:NO];
}

자동 레이아웃을 사용하고 있으며 아무런 대답도 없습니다. 마침내 작동 한 내 솔루션은 다음과 같습니다.

@property (nonatomic, assign) BOOL shouldScrollToLastRow;


- (void)viewDidLoad {
    [super viewDidLoad];

    _shouldScrollToLastRow = YES;
}


- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    // Scroll table view to the last row
    if (_shouldScrollToLastRow)
    {
        _shouldScrollToLastRow = NO;
        [self.tableView setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
    }
}

@JacobRelkin승인 한 솔루션은 자동 레이아웃을 사용하는 iOS 7.0에서 작동하지 않았습니다.

사용자 정의 하위 클래스가 UIViewController있고 _tableView의 하위 뷰로 인스턴스 변수 추가 했습니다 view. 나는 위치 _tableView자동 레이아웃을 사용하여. 나는이 메소드를 마지막 viewDidLoad끝에서 호출하려고 시도 했다 viewWillAppear:. 둘 다 일하지 않았다.

그래서의 사용자 정의 하위 클래스에 다음 메소드를 추가했습니다 UIViewController.

- (void)tableViewScrollToBottomAnimated:(BOOL)animated {
    NSInteger numberOfRows = [_tableView numberOfRowsInSection:0];
    if (numberOfRows) {
        [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:numberOfRows-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated];
    }
}

작품 [self tableViewScrollToBottomAnimated:NO]의 끝에서 전화 viewDidLoad. 불행히도 tableView:heightForRowAtIndexPath:모든 셀에 대해 세 번 호출됩니다.


다음은 Swift 2.0에서 구현 한 확장입니다. 이 함수는 tableview로드 된 후에 호출해야합니다 .

import UIKit

extension UITableView {
    func setOffsetToBottom(animated: Bool) {
        self.setContentOffset(CGPointMake(0, self.contentSize.height - self.frame.size.height), animated: true)
    }

    func scrollToLastRow(animated: Bool) {
        if self.numberOfRowsInSection(0) > 0 {
            self.scrollToRowAtIndexPath(NSIndexPath(forRow: self.numberOfRowsInSection(0) - 1, inSection: 0), atScrollPosition: .Bottom, animated: animated)
        }
    }
}

세부

  • Xcode 8.3.2, 신속한 3.1
  • Xcode 10.2 (10E125), 스위프트 5

암호

import UIKit

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

용법

tableView.scrollToBottom(animated: true)

전체 샘플

솔루션 코드를 붙여 넣는 것을 잊지 마십시오!

import UIKit

class ViewController: UIViewController {

    private weak var tableView: UITableView?
    private lazy var cellReuseIdentifier = "CellReuseIdentifier"

    override func viewDidLoad() {
        super.viewDidLoad()
        let tableView = UITableView(frame: view.frame)
        view.addSubview(tableView)
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
        self.tableView = tableView
        tableView.dataSource = self
        tableView.performBatchUpdates(nil) { [weak self] result in
            if result { self?.tableView?.scrollToBottom(animated: true) }
        }
    }
}

extension ViewController: UITableViewDataSource {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath )
        cell.textLabel?.text = "\(indexPath)"
        return cell
    }
}

실제로 신속하게 수행하는 "Swifter"방법은 다음과 같습니다.

var lastIndex = NSIndexPath(forRow: self.messages.count - 1, inSection: 0)
self.messageTableView.scrollToRowAtIndexPath(lastIndex, atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)

나를 위해 완벽합니다.


프레임에 표시된 테이블의 끝으로 테이블을로드하고 싶었습니다. 나는

NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0];
[[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];

테이블 높이가 프레임 높이보다 작을 때 오류가 발생하여 작동하지 않았습니다. 내 테이블에는 섹션이 하나만 있습니다.

나를 위해 일한 솔루션은 viewWillAppear에서 다음 코드를 구현하는 것입니다.

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// on the initial cell load scroll to the last row (ie the latest Note)
if (initialLoad==TRUE) {
    initialLoad=FALSE; 
    NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0];
    [[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
        CGPoint offset = CGPointMake(0, (1000000.0));
        [self.tableView setContentOffset:offset animated:NO];
    }
}

BOOL ivar initialLoad는 viewDidLoad에서 TRUE로 설정됩니다.


스위프트

if tableView.contentSize.height > tableView.frame.size.height {
    let offset = CGPoint(x: 0, y: tableView.contentSize.height - tableView.frame.size.height)
    tableView.setContentOffset(offset, animated: false)
}

UITableViewScrollPositionBottom대신 사용해야 합니다.


스위프트 3 (Xcode 8.1)의 경우 :

override func viewDidAppear(_ animated: Bool) {
    let numberOfSections = self.tableView.numberOfSections
    let numberOfRows = self.tableView.numberOfRows(inSection: numberOfSections-1)

    let indexPath = IndexPath(row: numberOfRows-1 , section: numberOfSections-1)
    self.tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.middle, animated: true)
}

물론 버그입니다. 아마도 코드 어딘가에 있습니다 table.estimatedRowHeight = value(예 : 100). 이 값을 행 높이가 얻을 수있는 최대 값 ( 예 : 500)으로 바꾸십시오. 이렇게하면 다음 코드와 함께 문제가 해결됩니다.

//auto scroll down example
let delay = 0.1 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))

dispatch_after(time, dispatch_get_main_queue(), {
    self.table.scrollToRowAtIndexPath(NSIndexPath(forRow: self.Messages.count - 1, inSection: 0), atScrollPosition: UITableViewScrollPosition.Bottom, animated: false)
})

위의 솔루션을 사용하면 테이블 맨 아래로 스크롤됩니다 (테이블 내용이 먼저로드 된 경우에만).

//Scroll to bottom of table
CGSize tableSize = myTableView.contentSize;
[myTableView setContentOffset:CGPointMake(0, tableSize.height)];

After a lot of fiddling this is what worked for me:

var viewHasAppeared = false

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if !viewHasAppeared { goToBottom() }
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    viewHasAppeared = true
}

private func goToBottom() {
    guard data.count > 0 else { return }
    let indexPath = NSIndexPath(forRow: data.count - 1, inSection: 0)
    tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: false)
    tableView.layoutIfNeeded()
}

The key turned out to be not wrapping scrollToRowAtIndexPath inside of dispatch_async as some have suggested, but simply following it with a call to layoutIfNeeded.

My understanding of this is, calling the scroll method in the current thread guarantees that the scroll offset is set immediately, before the view is displayed. When I was dispatching to the main thread, the view was getting displayed for an instant before the scroll took effect.

(Also NB you need the viewHasAppeared flag because you don't want to goToBottom every time viewDidLayoutSubviews is called. It gets called for example whenever the orientation changes.)


In Swift 3.0

self.tableViewFeeds.setContentOffset(CGPoint(x: 0, y: CGFLOAT_MAX), animated: true)

If you have to load the data asynchronously prior to scrolling down, here's the possible solution:

tableView.alpha = 0 // We want animation!
lastMessageShown = false // This is ivar

viewModel.fetch { [unowned self] result in
    self.tableView.reloadData()

    if !self.lastMessageShown {
        dispatch_async(dispatch_get_main_queue()) { [unowned self] in
            if self.rowCount > 0 {
                self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: self.rowCount, inSection: 0), atScrollPosition: .Bottom, animated: false)
            }

            UIView.animateWithDuration(0.1) {
                self.tableView.alpha = 1
                self.lastMessageShown = true // Do it once
            }
        }
    }
}

Function on swift 3 scroll to bottom

 override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(false)
        //scroll down
        if lists.count > 2 {
            let numberOfSections = self.tableView.numberOfSections
            let numberOfRows = self.tableView.numberOfRows(inSection: numberOfSections-1)
            let indexPath = IndexPath(row: numberOfRows-1 , section: numberOfSections-1)
            self.tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.middle, animated: true)
        }
    }

func scrollToBottom() {

    let sections = self.chatTableView.numberOfSections

    if sections > 0 {

        let rows = self.chatTableView.numberOfRows(inSection: sections - 1)

        let last = IndexPath(row: rows - 1, section: sections - 1)

        DispatchQueue.main.async {

            self.chatTableView.scrollToRow(at: last, at: .bottom, animated: false)
        }
    }
}

you should add

DispatchQueue.main.async {
            self.chatTableView.scrollToRow(at: last, at: .bottom, animated: false)
        }

or it will not scroll to bottom.


Use this simple code to scroll tableView bottom

NSInteger rows = [tableName numberOfRowsInSection:0];
if(rows > 0) {
    [tableName scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:rows-1 inSection:0]
                     atScrollPosition:UITableViewScrollPositionBottom
                             animated:YES];
}

Thanks Jacob for the answer. really helpfull if anyone interesting with monotouch c# version

private void SetScrollPositionDown() {
    if (tblShoppingListItem.ContentSize.Height > tblShoppingListItem.Frame.Size.Height) {
        PointF offset = new PointF(0, tblShoppingListItem.ContentSize.Height - tblShoppingListItem.Frame.Size.Height);
        tblShoppingListItem.SetContentOffset(offset,true );
    }
}

In iOS this worked fine for me

CGFloat height = self.inputTableView.contentSize.height;
if (height > CGRectGetHeight(self.inputTableView.frame)) {
    height -= (CGRectGetHeight(self.inputTableView.frame) - CGRectGetHeight(self.navigationController.navigationBar.frame));
}
else {
    height = 0;
}
[self.inputTableView setContentOffset:CGPointMake(0, height) animated:animated];

It needs to be called from viewDidLayoutSubviews


[self.tableViewInfo scrollRectToVisible:CGRectMake(0, self.tableViewInfo.contentSize.height-self.tableViewInfo.height, self.tableViewInfo.width, self.tableViewInfo.height) animated:YES];


The accepted answer didn't work with my table (thousands of rows, dynamic loading) but the code below works:

- (void)scrollToBottom:(id)sender {
    if ([self.sections count] > 0) {
        NSInteger idx = [self.sections count] - 1;
        CGRect sectionRect = [self.tableView rectForSection:idx];
        sectionRect.size.height = self.tableView.frame.size.height;
        [self.tableView scrollRectToVisible:sectionRect animated:NO];
    }
}

No need for any scrolling you can just do it by using this code:

[YOURTABLEVIEWNAME setContentOffset:CGPointMake(0, CGFLOAT_MAX)];

If you are setting up frame for tableview programmatically, make sure you are setting frame correctly.


In Swift, you just need

self.tableView.scrollToNearestSelectedRowAtScrollPosition(UITableViewScrollPosition.Bottom, animated: true)

to make it automatically scroll to the buttom


In swift 3.0 If you want to go any particular Cell of tableview Change cell index Value like change "self.yourArr.count" value .

self.yourTable.reloadData()
self.scrollToBottom() 
func scrollToBottom(){
    DispatchQueue.global(qos: .background).async {
        let indexPath = IndexPath(row: self.yourArr.count-1, section: 0)
        self.tblComment.scrollToRow(at: indexPath, at: .bottom, animated: true)
    }
}

I believe old solutions do not work with swift3.

If you know number rows in table you can use :

tableView.scrollToRow(
    at: IndexPath(item: listCountInSection-1, section: sectionCount - 1 ), 
    at: .top, 
    animated: true)

참고URL : https://stackoverflow.com/questions/2770158/how-to-scroll-to-the-bottom-of-a-uitableview-on-the-iphone-before-the-view-appea

반응형