webentwicklung-frage-antwort-db.com.de

Wie kann ich bis zum Ende der UITableView blättern?

Ich habe eine UITableView, die mit Zellen mit dynamischer Höhe gefüllt ist. Ich möchte, dass die Tabelle nach unten gescrollt wird, wenn der View-Controller vom View-Controller verschoben wird.

Ich habe es mit contentOffset und tableView scrollToRowAtIndexPath versucht, aber ich bekomme immer noch nicht die perfekte Lösung für genau das, was ich will.

Kann mir bitte jemand helfen, dieses Problem zu beheben?

Hier ist mein Code zum Scrollen:

let indexPath = NSIndexPath(forRow: commentArray.count-1, inSection: 0)
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
31
Padmaja

Für Swift 3.0

Schreibe eine Funktion:

func scrollToBottom(){
    DispatchQueue.main.async {
        let indexPath = IndexPath(row: self.dataArray.count-1, section: 0)
        self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
    }
}

und rufen Sie es auf, nachdem Sie die Tableview-Daten neu geladen haben

tableView.reloadData()
scrollToBottom()
65
Amit Verma

Ich würde dazu einen allgemeineren Ansatz verwenden:

Swift4

extension UITableView {

    func scrollToBottom(){

        DispatchQueue.main.async {
            let indexPath = IndexPath(
                row: self.numberOfRows(inSection:  self.numberOfSections -1) - 1, 
                section: self.numberOfSections - 1)
            self.scrollToRow(at: indexPath, at: .bottom, animated: true)
        }
    }

    func scrollToTop() {

        DispatchQueue.main.async {
            let indexPath = IndexPath(row: 0, section: 0)
            self.scrollToRow(at: indexPath, at: .top, animated: false)
        }
    }
}
17
Umair Afzal

Ich habe den Ansatz von Umair ausprobiert. In UITableViews kann es jedoch einen Abschnitt mit 0 Zeilen geben. In diesem Fall verweist der Code auf einen ungültigen Indexpfad (Zeile 0 eines leeren Abschnitts ist keine Zeile). 

Das blinde Minimieren von 1 von der Anzahl der Zeilen/Abschnitte kann ein weiterer Schwachpunkt sein, da wiederum die Zeile/der Abschnitt 0 Elemente enthalten kann.

Hier meine Lösung, um zur untersten Zelle zu scrollen und sicherzustellen, dass der Indexpfad gültig ist:

extension UITableView {
    func scrollToBottomRow() {
        DispatchQueue.main.async {
            guard self.numberOfSections > 0 else { return }

            // Make an attempt to use the bottom-most section with at least one row
            var section = max(self.numberOfSections - 1, 0)
            var row = max(self.numberOfRows(inSection: section) - 1, 0)
            var indexPath = IndexPath(row: row, section: section)

            // Ensure the index path is valid, otherwise use the section above (sections can
            // contain 0 rows which leads to an invalid index path)
            while !self.indexPathIsValid(indexPath) {
                section = max(section - 1, 0)
                row = max(self.numberOfRows(inSection: section) - 1, 0)
                indexPath = IndexPath(row: row, section: section)

                // If we're down to the last section, attempt to use the first row
                if indexPath.section == 0 {
                    indexPath = IndexPath(row: 0, section: 0)
                    break
                }
            }

            // In the case that [0, 0] is valid (perhaps no data source?), ensure we don't encounter an
            // exception here
            guard self.indexPathIsValid(indexPath) else { return }

            self.scrollToRow(at: indexPath, at: .bottom, animated: true)
        }
    }

    func indexPathIsValid(_ indexPath: IndexPath) -> Bool {
        let section = indexPath.section
        let row = indexPath.row
        return section < self.numberOfSections && row < self.numberOfRows(inSection: section)
    }
}
10
John Rogers

Wenn Sie den Viewcontroller mit der TableView betätigen, sollten Sie nur dann zum angegebenen IndexPath scrollen, nachdem das TableView vollständig geladen wurde.

yourTableview.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
    let indexPath = NSIndexPath(forRow: commentArray.count-1, inSection: 0)
  tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)

})

Der Grund für das Einfügen der Methode in dispatch_async besteht darin, dass nach dem Ausführen von reloadData die nächste Zeile sofort ausgeführt wird. Anschließend erfolgt ein erneutes Laden im Haupt-Thread. Um zu wissen, wann die Tabellenansicht beendet ist (nachdem alle cellforrowindex beendet ist), verwenden wir hier GCD. Grundsätzlich gibt es keinen Stellvertreter in tableview, der mitteilt, dass der Tableview vollständig geladen wurde.

6
iPrabu

Dies funktioniert in Swift 3.0

let pointsFromTop = CGPoint(x: 0, y: CGFloat.greatestFiniteMagnitude)
tableView.setContentOffset(pointsFromTop, animated: true)
2
silly_cone

Funktioniert in Swift 4+:

   self.tableView.reloadData()
    let indexPath = NSIndexPath(row: self.yourDataArray.count-1, section: 0)
    self.tableView.scrollToRow(at: indexPath as IndexPath, at: .bottom, animated: true)

Für ein perfektes Scrollen zur untersten Lösung verwenden Sie tableView contentOffset

func scrollToBottom()  {
        let point = CGPoint(x: 0, y: self.tableView.contentSize.height + self.tableView.contentInset.bottom - self.tableView.frame.height)
        if point.y >= 0{
            self.tableView.setContentOffset(point, animated: animate)
        }
    }

Ich benutze lieber self.view.layoutIfNeeded(), nachdem ich meine Daten in tableView eingegeben habe, und rufe dann meine Methode scrollToBottom() auf. Das funktioniert gut für mich.

1
Jayraj Vala

[Swift 3, iOS 10]

Ich habe am Ende eine Art von hacky-Lösung verwendet, aber sie hängt nicht von Zeilenindexpfaden ab (was manchmal zu Abstürzen führt), der dynamischen Höhe der Zellen oder dem Nachladen von Tabellen, daher scheint sie ziemlich universell und funktioniert in der Praxis zuverlässiger als andere habe ich gefunden. 

  • verwenden Sie KVO, um das contentOffset der Tabelle zu verfolgen

  • feuerrollereignis im KVO-Beobachter

  • zum Aufrufen mit verzögertem Timer können Sie mehrere Bilder filtern
    Beobachter triggert

Der Code in einigen ViewController:

private var scrollTimer: Timer?
private var ObserveContext: Int = 0

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    table.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.new, context: &ObserveContext)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    table.removeObserver(self, forKeyPath: "contentSize")
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if (context == &ObserveContext) {
        self.scheduleScrollToBottom()
    }
}

func scheduleScrollToBottom() {

    if (self.scrollTimer == nil) {
        self.scrollTimer = Timer(timeInterval: 0.5, repeats: false, block: { [weak self] (timer) in
            let table = self!.table

            let bottomOffset = table.contentSize.height - table.bounds.size.height
            if !table.isDragging && bottomOffset > 0 {
                let point: CGPoint = CGPoint(x: 0, y: bottomOffset)
                table.setContentOffset(point, animated: true)
            }

            timer.invalidate()
            self?.scrollTimer = nil
        })
        self.scrollTimer?.fire()
    }
}
0
Mitya

Ein kleines Update der @Umair-Antwort für den Fall, dass Ihre tableView leer ist

func scrollToBottom(animated: Bool = true, delay: Double = 0.0) {
    let numberOfRows = tableView.numberOfRows(inSection: tableView.numberOfSections - 1) - 1
    guard numberOfRows > 0 else { return }

    DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [unowned self] in

        let indexPath = IndexPath(
            row: numberOfRows,
            section: self.tableView.numberOfSections - 1)
        self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: animated)
    }
}
0
mattyU

Wenn Sie UINavigationBar mit etwas Höhe und UITableView in der UIView verwendet haben, versuchen Sie, die UINavigationBar-Höhe von der Frame-Höhe von UITableView abzuziehen. Verursachen Sie, dass UITableView die Variable top mit UINavigationBarbottom übereinstimmt, so dass sich dies auf Ihre UITableView-Elemente bottom auswirkt.

Swift 3

simpleTableView.frame = CGRect.init(x: 0, y: navigationBarHeight, width: Int(view.frame.width), height: Int(view.frame.height)-navigationBarHeight)
0
elia

Dies ist eine Funktion, die die Vervollständigung näher enthält:

func reloadAndScrollToTop(completion: @escaping () -> Void) {
    self.tableView.reload()
    completion()
}

Und benutze:

self.reloadAndScrollToTop(completion: {
     tableView.scrollToRow(at: indexPath, at: .top, animated: true))
})

Das tableView.scrollTo ... Zeile wird ausgeführt, nachdem alle Tabellenzellen sicher geladen wurden.

0
Eido 9oya