Ich muss diese App machen, die eine seltsame Konfiguration hat.
Wie im nächsten Bild gezeigt, ist die Hauptansicht eine UIScrollView. Darin sollte sich dann eine UIPageView befinden, und jede Seite der PageView sollte eine UITableView haben.
Ich habe das alles bisher gemacht. Mein Problem ist jedoch, dass sich das Scrollen natürlich verhält .
Das nächste meine ich natürlich . Wenn ich derzeit in einer der UITableViews scrolle, wird die Tabellenansicht (nicht die Bildlaufansicht) gescrollt. Ich möchte jedoch, dass die Bildlaufansicht gescrollt wird, es sei denn, die Bildlaufansicht kann nicht gescrollt werden, da sie oben oder unten angezeigt wird (in diesem Fall möchte ich, dass die Tabellenansicht gescrollt wird).
Angenommen, meine Bildlaufansicht wird derzeit nach oben gescrollt. Dann lege ich meinen Finger über die Tabellenansicht (der aktuell angezeigten Seite) und beginne mit dem Scrollen nach unten . In diesem Fall möchte ich, dass die Bildlaufansicht gescrollt wird (nein, die Tabellenansicht). Wenn ich meine Bildlaufansicht weiter nach unten scrolle und sie den unteren Rand erreicht, wenn ich meinen Finger vom Display entferne und ihn wieder über die Tebleansicht lege und wieder nach unten scrolle, möchte ich, dass meine Tabellenansicht jetzt nach unten scrollt, da die Bildlaufansicht ihren unteren Rand erreicht hat und dies nicht der Fall ist in der Lage, weiter zu scrollen.
Habt ihr eine Idee, wie man dieses Scrollen implementiert?
Ich bin wirklich verloren damit. Jede Hilfe wird es sehr schätzen :(
Vielen Dank!
quelle
Antworten:
Die Lösung für die gleichzeitige Behandlung der Bildlaufansicht und der Tabellenansicht dreht sich um die
UIScrollViewDelegate
. Lassen Sie Ihren View Controller daher diesem Protokoll entsprechen:class ViewController: UIViewController, UIScrollViewDelegate {
Ich werde die Bildlaufansicht und die Tabellenansicht als Ausgänge darstellen:
@IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var tableView: UITableView!
Wir müssen auch die Höhe des Inhalts der Bildlaufansicht sowie die Bildschirmhöhe verfolgen. Du wirst später sehen warum.
let screenHeight = UIScreen.mainScreen().bounds.height let scrollViewContentHeight = 1200 as CGFloat
Eine kleine Konfiguration ist erforderlich in
viewDidLoad:
:override func viewDidLoad() { super.viewDidLoad() scrollView.contentSize = CGSizeMake(scrollViewContentWidth, scrollViewContentHeight) scrollView.delegate = self tableView.delegate = self scrollView.bounces = false tableView.bounces = false tableView.scrollEnabled = false }
wo ich das Hüpfen ausgeschaltet habe, um die Dinge einfach zu halten. Die Schlüsseleinstellungen sind die Delegaten für die Bildlaufansicht und die Tabellenansicht, und das Scrollen der Tabellenansicht wird zuerst deaktiviert.
Diese sind erforderlich, damit die
scrollViewDidScroll:
Delegatmethode den unteren Rand der Bildlaufansicht und den oberen Rand der Tabellenansicht erreichen kann. Hier ist diese Methode:func scrollViewDidScroll(scrollView: UIScrollView) { let yOffset = scrollView.contentOffset.y if scrollView == self.scrollView { if yOffset >= scrollViewContentHeight - screenHeight { scrollView.scrollEnabled = false tableView.scrollEnabled = true } } if scrollView == self.tableView { if yOffset <= 0 { self.scrollView.scrollEnabled = true self.tableView.scrollEnabled = false } } }
Die Delegate-Methode erkennt, wann die Bildlaufansicht ihren unteren Rand erreicht hat. In diesem Fall kann die Tabellenansicht gescrollt werden. Es wird auch erkannt, wann die Tabellenansicht den oberen Rand erreicht, an dem die Bildlaufansicht wieder aktiviert wird.
Ich habe ein GIF erstellt, um die Ergebnisse zu demonstrieren:
quelle
Daniels Antwort wurde geändert, um sie effizienter und fehlerfreier zu gestalten.
@IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var tableView: UITableView! @IBOutlet weak var tableHeight: NSLayoutConstraint! override func viewDidLoad() { super.viewDidLoad() //Set table height to cover entire view //if navigation bar is not translucent, reduce navigation bar height from view height tableHeight.constant = self.view.frame.height-64 self.tableView.isScrollEnabled = false //no need to write following if checked in storyboard self.scrollView.bounces = false self.tableView.bounces = true } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 20 } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let label = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 30)) label.text = "Section 1" label.textAlignment = .center label.backgroundColor = .yellow return label } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) cell.textLabel?.text = "Row: \(indexPath.row+1)" return cell } func scrollViewDidScroll(_ scrollView: UIScrollView) { if scrollView == self.scrollView { tableView.isScrollEnabled = (self.scrollView.contentOffset.y >= 200) } if scrollView == self.tableView { self.tableView.isScrollEnabled = (tableView.contentOffset.y > 0) } }
Das vollständige Projekt finden Sie hier: https://gitlab.com/vineetks/TableScroll.git
quelle
Nach vielen Versuchen und Irrtümern hat dies für mich am besten funktioniert. Die Lösung muss zwei Anforderungen lösen: 1) Bestimmen Sie, wer die Bildlaufeigenschaft verwenden soll. tableView oder scrollView? 2) Stellen Sie sicher, dass die tableView der scrollView keine Berechtigung erteilt, bis sie den oberen Rand ihrer Tabelle / ihres Inhalts erreicht hat.
Um zu sehen, ob die Bildlaufansicht zum Scrollen im Vergleich zur Tabellenansicht verwendet werden soll, habe ich überprüft, ob sich die UIView direkt über meiner Tabellenansicht innerhalb des Rahmens befindet. Wenn sich die UIView innerhalb des Frames befindet, kann man mit Sicherheit sagen, dass die scrollView die Berechtigung zum Scrollen haben sollte. Wenn sich die UIView nicht innerhalb des Frames befindet, bedeutet dies, dass die tableView das gesamte Fenster einnimmt und daher die Berechtigung zum Scrollen haben sollte.
func scrollViewDidScroll(_ scrollView: UIScrollView) { if scrollView.bounds.intersects(UIView.frame) == true { //the UIView is within frame, use the UIScrollView's scrolling. if tableView.contentOffset.y == 0 { //tableViews content is at the top of the tableView. tableView.isUserInteractionEnabled = false tableView.resignFirstResponder() print("using scrollView scroll") } else { //UIView is in frame, but the tableView still has more content to scroll before resigning its scrolling over to ScrollView. tableView.isUserInteractionEnabled = true scrollView.resignFirstResponder() print("using tableView scroll") } } else { //UIView is not in frame. Use tableViews scroll. tableView.isUserInteractionEnabled = true scrollView.resignFirstResponder() print("using tableView scroll") } }
hoffe das hilft jemandem!
quelle
Ich hatte auch mit diesem Problem zu kämpfen. Es gibt eine sehr einfache Lösung.
Im Interface Builder:
Jetzt kommt der lustige Teil: Stellen Sie für jede Seite, die Sie hinzufügen, sicher, dass die tableView einen Versatz von oben hat. Das ist es. Sie können dies beispielsweise mit diesem Code tun (vorausgesetzt, Sie verwenden UITableViewController als Seite):
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let tables = viewControllers.compactMap { $0 as? UITableViewController } tables.forEach { $0.tableView.contentInset = UIEdgeInsets(top: headerView.bounds.height, left: 0, bottom: 0, right: 0) $0.tableView.contentOffset = CGPoint(x: 0, y: -headerView.bounds.height) } }
Keine unordentliche Schriftrolle in der Tabellenansicht, keine Verwirrung mit Delegierten, keine doppelten Schriftrollen, vollkommen natürliches Verhalten. Wenn Sie den Header nicht sehen können, liegt dies wahrscheinlich an der Hintergrundfarbe von tableView. Sie müssen es auf clear setzen, damit der Header unter der tableView sichtbar ist.
quelle
Keine der Antworten hier hat perfekt für mich funktioniert. Jeder hatte sein eigenes nuanciertes Problem (er musste wiederholt wischen, wenn eine Bildlaufansicht den Boden erreichte oder die Bildlaufanzeige nicht richtig aussah usw.), also dachte ich mir, ich würde eine andere Antwort einwerfen.
Ole Begemann hat eine großartige Beschreibung dazu: https://oleb.net/blog/2014/05/scrollviews-inside-scrollviews/
Obwohl es sich um einen alten Beitrag handelt, gelten die Konzepte weiterhin für die aktuellen APIs. Darüber hinaus gibt es eine gepflegte (Xcode 9-kompatible) Objective-C-Implementierung seines Ansatzes https://github.com/eyeem/OLEContainerScrollView
quelle
Wenn Sie Probleme mit dem verschachtelten Bildlauf haben, ist dies die einfachste Lösung.
Überprüfen Sie, wie Sie das Abprallen beim Scrollen einer Bildlaufansicht deaktivieren
Überprüfen Sie, wie Sie das Abprallen beim Scrollen einer Tabellenansicht deaktivieren können
quelle
Ich denke, es gibt zwei Möglichkeiten.
Da Sie die Größe der Bildlaufansicht und der Hauptansicht kennen, können Sie nicht feststellen, ob die Bildlaufansicht den unteren Rand erreicht hat oder nicht.
if (scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height)) { // reach bottom }
Also, wenn es traf; Sie setzen im Grunde
[contentScrollView setScrollEnabled:NO];
und umgekehrt für Ihre tableView.
Die andere Sache, die meiner Meinung nach genauer ist, besteht darin, Ihren Ansichten eine Geste hinzuzufügen.
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondToTapGesture:)]; // Specify that the gesture must be a single tap tapRecognizer.numberOfTapsRequired = 1; // Add the tap gesture recognizer to the view [self.view addGestureRecognizer:tapRecognizer]; // Do any additional setup after loading the view, typically from a nib
Also , wenn Sie Gesture hinzufügen, können Sie einfach die aktive Ansicht steuern , indem
setScrollEnabled
in derrespondToTapGesture
.quelle
Ich habe eine tolle Bibliothek MXParallaxHeader gefunden
Stellen Sie im Storyboard einfach die
UIScrollView
Klasse auf "MXScrollView
Magie" ein.Ich habe diese Klasse verwendet, um meine
UIScrollView
beim Einbetten einerUIPageViewController
Containeransicht zu behandeln. Sie können sogar eine Parallaxen-Header-Ansicht einfügen, um weitere Details zu erhalten.Auch diese Bibliothek bietet
Cocoapods
undCarthage
Ich habe ein Bild angehängt, das die
UIView
Hierarchie darstellt. MXScrollView-Hierarchiequelle
Ich habe versucht, die als richtige Antwort gekennzeichnete Lösung zu finden, aber sie funktionierte nicht richtig. Der Benutzer muss zweimal auf die Tabellenansicht klicken, um einen Bildlauf durchzuführen, und danach konnte ich nicht mehr den gesamten Bildschirm scrollen. Also habe ich gerade den folgenden Code in viewDidLoad () angewendet:
tableView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(tableViewSwiped))) scrollView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(scrollViewSwiped)))
Und der folgende Code ist die Implementierung der Aktionen:
func tableViewSwiped(){ scrollView.isScrollEnabled = false tableView.isScrollEnabled = true } func scrollViewSwiped(){ scrollView.isScrollEnabled = true tableView.isScrollEnabled = false }
quelle
Vielleicht Brute-Force, aber es funktioniert perfekt: Ich verwende übrigens das automatische Layout.
Legen Sie für tableView (oder collectionView oder was auch immer) eine beliebige Höhe im Storyboard fest und stellen Sie einen Ausgang für die Klasse her. Stellen Sie gegebenenfalls (viewDidLoad () oder ...) die Höhe der tableView so groß ein, dass tableView nicht scrollen muss. (Die Anzahl der Zeilen muss im Voraus bekannt sein.) Dann wird nur die äußere scrollView gut scrollen.
quelle
Ein einfacher Trick, wenn Sie dies erreichen möchten, ist das Ersetzen der übergeordneten Bildlaufansicht durch die normale Containeransicht. Wenn Sie der Containeransicht eine Schwenkgeste hinzufügen, können Sie mit der obersten Einschränkung der ersten Ansicht spielen, um negative Werte zuzuweisen. Sie können die Herkunft der Seitenansicht überprüfen, wenn sie den oberen Rand erreicht. Sie können diesen Wert für den Inhaltsversatz der untergeordneten Ansicht der Seitenansicht zuweisen. Bis der Benutzer die Tabellenansicht in der Containeransicht in der obersten Ansicht erreicht, können Sie das Scrollen der Seitentabelle deaktivieren und das manuelle Scrollen zulassen, indem Sie den Inhaltsversatz festlegen. Daher wird die Höhe der Seitenansicht zunächst reduziert (oder beispielsweise außerhalb des Bildschirms) oder weniger unten. Später beim Scrollen nach unten wird es erweitert, um mehr Platz zu beanspruchen.
Die Geste reagiert automatisch nicht mehr, wenn außerhalb der Frames die Navigationsleiste oder eine andere Ansicht außerhalb der Containeransicht angezeigt wird.
Gesten sind ein Schlüssel zu interaktiven Benutzerübergängen, die in vielen Apps verwendet werden. Sie können damit das Scrollen für eine bestimmte Zeit nachahmen.
quelle
In meinem Fall verwende ich eine Einschränkung für die Höhe wie folgt:
self.heightTableViewConstraint.constant = self.tableView.contentSize.height self.scrollView.contentInset.bottom = self.tableView.contentSize.height
quelle
CGFloat tableHeight = 0.0f; YourArray =[response valueForKey:@"result"]; tableHeight = 0.0f; for (int i = 0; i < [YourArray count]; i ++) { tableHeight += [self tableView:self.aTableviewDoc heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]]; } self.aTableviewDoc.frame = CGRectMake(self.aTableviewDoc.frame.origin.x, self.aTableviewDoc.frame.origin.y, self.aTableviewDoc.frame.size.width, tableHeight);
quelle