Seltsames Verhalten in iOS11. Zellen scrollen mit Navigations-Push-Animation nach oben

116

Ich habe kürzlich Code auf das neue iOS 11 Beta 5 SDK migriert.

Ich bekomme jetzt ein sehr verwirrendes Verhalten von UITableView. Die Tabellenansicht selbst ist nicht so schick. Ich habe benutzerdefinierte Zellen, aber zum größten Teil ist es nur für ihre Höhe.

Wenn ich meinen Ansichts-Controller mit Tabellenansicht drücke, erhalte ich eine zusätzliche Animation, in der Zellen entlang der Push / Pop-Navigationsanimation "nach oben scrollen" (oder möglicherweise der gesamte Rahmen der Tabellenansicht geändert wird) und nach unten. Bitte siehe gif:

wellige Tabellenansicht

Ich erstelle manuell tableviewin loadViewMethode und richte Einschränkungen für das automatische Layout so ein, dass sie der führenden, nachfolgenden, oberen und unteren Seite der Übersicht der Tabellenansicht entsprechen. Die Übersicht ist die Stammansicht des View Controllers.

Der Push-Code des View Controllers ist Standard: self.navigationController?.pushViewController(notifVC, animated: true)

Der gleiche Code bietet unter iOS 10 ein normales Verhalten.

Könnten Sie mich bitte darauf hinweisen, was falsch ist?

EDIT: Ich habe einen sehr einfachen Tableview-Controller erstellt und kann dort das gleiche Verhalten reproduzieren. Code:

class VerySimpleTableViewController : UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    }


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

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


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = String(indexPath.row)
        cell.accessoryType = .disclosureIndicator

        return cell
    }


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)

        let vc = VerySimpleTableViewController.init(style: .grouped)

        self.navigationController?.pushViewController(vc, animated: true)
    }
}

EDIT 2: Ich konnte das Problem auf meine Anpassung von UINavigationBar eingrenzen. Ich habe eine Anpassung wie diese:

rootNavController.navigationBar.setBackgroundImage(createFilledImage(withColor: .white, size: 1), for: .default)

Dabei createFilledImagewird ein quadratisches Bild mit der angegebenen Größe und Farbe erstellt.

Wenn ich diese Zeile auskommentiere, bekomme ich wieder normales Verhalten.

Ich würde mich über Gedanken zu diesem Thema freuen.

iur
quelle
Möglicherweise liegt kein Problem bei der Anpassung der Navigationsleiste vor. Ich hatte das gleiche Problem (die akzeptierte Antwort löste dies) ohne Anpassung. Ich denke, es könnte ein Problem mit der Art und Weise sein, wie iOS die Tabellenansicht behandelt, wenn sie manuell als Unteransicht erstellt wird, anstatt UITableViewController zu verwenden.
Mark Leonard
2
Ich sehe dieses Verhalten nur, wenn ich es eingestellt navigationBar.isTranslucenthabe false, sonst funktioniert es einwandfrei.
b_ray
5
Dies scheint ein Fehler in iOS11 GM zu sein. Bitte täuschen Sie diesen Fehlerbericht vor, damit Apple auf dieses Problem aufmerksam wird: openradar.appspot.com/34465226
b_ray
1
Dieses Problem scheint in der Beta-Version von iOS 11.2 behoben zu sein. Ich würde contentInsetAdjustmentBehavior nicht auf nie setzen, da es die iPhone X-Bildlaufansichten unterbricht, indem am unteren Bildschirmrand keine Auffüllung angezeigt wird. Das Ende Ihrer Inhaltsansicht befindet sich unter der Home-Schaltfläche des iPhone X.
Batu

Antworten:

149

Dies liegt an UIScrollView's der neuen contentInsetAdjustmentBehaviorEigenschaft (UITableView ist eine Unterklasse von UIScrollview) , die .automaticstandardmäßig festgelegt ist.

Sie können dieses Verhalten mit dem folgenden Snippet in viewDidLoad aller betroffenen Controller überschreiben:

    tableView.contentInsetAdjustmentBehavior = .never

https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior

Maggy Hillen
quelle
2
In diesem Kommentar zur Definition von UIScrollViewContentInsetAdjustmentBehavior.automatic heißt es: "... aus Gründen der Abwärtskompatibilität wird auch das obere und untere contentInset angepasst, wenn die Bildlaufansicht einem Ansichtscontroller mit automatischAdjustsScrollViewInsets = YES in einem Navigationscontroller gehört, unabhängig davon, ob der Bildlauf ausgeführt wird Ansicht ist scrollbar ". Meine Theorie ist, dass das contentInset der Navigationsleiste durch das Festlegen des Hintergrundbilds beeinflusst wird, das dann dynamisch angepasst wird.
Maggy Hillen
8
Sie können dies auch mit dem Storyboard tun. Größeninspektor -> Inhaltseinfügungen -> Setzen Sie 'Nie'.
Woongbi Kim
4
Wenn sich Ihr Inhalt hinter die Registerkartenleiste erstreckt, werden die Einfügungen durch Deaktivieren tableView.contentInsetAdjustmentBehaviorbeschädigt.
Kean
4
Durch einfaches Deaktivieren wird die Bildlaufanzeige hinter dem (oberen Gizmo) auf dem iPhone X im Querformat platziert. Der springende Punkt dieses Verhaltens ist, den Inhaltsbereich von Bildlaufansichten so anzupassen, dass sie auf Bildschirmen sichtbar sind, die nicht rechteckig sind. Ich denke, wir können dies derzeit nur auf dem iPhone X Sim sehen.
PhoneyDeveloper
8
Dieses Problem wurde durch einen Fehler in iOS 11 verursacht, bei dem die safeAreaInsets der Ansicht des Ansichtscontrollers während des Navigationsübergangs falsch eingestellt wurden, was in iOS 11.2 behoben werden sollte. Das Einstellen von contentInsetAdjustmentBehaviorauf .neverist keine gute Problemumgehung, da es wahrscheinlich andere unerwünschte Nebenwirkungen hat. Wenn Sie eine Problemumgehung verwenden, sollten Sie diese für iOS-Versionen> = 11.2 entfernen.
Smileyborg
23

Neben Maggys Antwort

ZIEL C

if (@available(iOS 11.0, *)) {
    scrollViewForView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}

Dieses Problem wurde durch einen Fehler in iOS 11 verursacht, bei dem safeAreaInsetsdie Ansicht des Ansichtscontrollers während des Navigationsübergangs falsch eingestellt wurde, was in iOS 11.2 behoben werden sollte. Das Einstellen von contentInsetAdjustmentBehaviorauf .neverist keine gute Problemumgehung, da es wahrscheinlich andere unerwünschte Nebenwirkungen hat. Wenn Sie eine Problemumgehung verwenden, sollten Sie diese für iOS-Versionen> = 11.2 entfernen

- erwähnt von smileyborg (Software Engineer bei Apple)

Lal Krishna
quelle
6

Sie können dieses Verhalten in der gesamten Anwendung sofort bearbeiten, indem Sie NSProxy verwenden, z. B. didFinishLaunchingWithOptions:

if (@available(iOS 11.0, *)) {
      [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} 
BigDanceMouse
quelle
1
Dies wird den Controller des Systems beschädigen, wie UIImagePickerController
Galway
6

So konnte ich dieses Problem beheben, während iOS 11 weiterhin automatisch Einfügungen festlegen konnte . Ich benutze UITableViewController.

  • Wählen Sie in Ihrem Ansichts-Controller im Storyboard (oder programmgesteuert ) "Kanten unter oberen Balken erweitern" und "Kanten unter undurchsichtigen Balken erweitern" . Die Sicherheitsbereiche verhindern, dass Ihre Sicht unter die obere Leiste fällt.
  • Aktivieren Sie die Schaltfläche "Insets to Safe Area" in Ihrer Tabellenansicht in Ihrem Storyboard. (oder tableView.insetsContentViewsToSafeArea = true) - Das ist vielleicht nicht nötig, aber ich habe es getan.
  • Stellen Sie das Anpassungsverhalten für den Inhaltseinsatz auf "Bildlaufachsen" (oder tableView.contentInsetAdjustmentBehavior = .scrollableAxes) - .alwaysfunktioniert möglicherweise auch, aber ich habe es nicht getestet.

Eine andere Sache zu versuchen, wenn alles andere fehlschlägt:

Überschreiben Sie die viewSafeAreaInsetsDidChange UIViewControllerMethode, um die Tabellenansicht zu zwingen, das Setzen der Bildlaufansicht-Einfügungen auf die Sicherheitsbereichseinfügungen zu erzwingen. Dies steht in Verbindung mit der Einstellung "Nie" in Maggys Antwort.

- (void)viewSafeAreaInsetsDidChange {
    [super viewSafeAreaInsetsDidChange];
    self.tableView.contentInset = self.view.safeAreaInsets;
}

Hinweis: self.tableViewund self.viewsollte das gleiche sein fürUITableViewController

Weihnachtsmann
quelle
Versuchte fast alles an diesem Thread, und das hat bei mir funktioniert. TableViewVC in TabBarVC eingebettet. Danke dir!
Josh Wolff
3

Dies scheint eher ein Fehler als ein beabsichtigtes Verhalten zu sein. Dies geschieht, wenn die Navigationsleiste nicht durchscheinend ist oder wenn das Hintergrundbild eingestellt ist.

Wenn Sie nur contentInsetAdjustmentBehavior auf .never setzen, werden Inhaltseinfügungen auf dem iPhone X nicht richtig eingestellt, z. B. wird der Inhalt unter den Bildlaufleisten in den unteren Bereich verschoben.

Es müssen zwei Dinge getan werden:
1. Verhindern, dass scrollView bei Push / Pop animiert wird.
2. Automatisches Verhalten beibehalten, da es für das iPhone X benötigt wird. Ohne dies, z. B. im Hochformat, wird der Inhalt unter die untere Bildlaufleiste verschoben.

Neue einfache Lösung: In XIB: Fügen Sie einfach eine neue UIView über Ihrer Hauptansicht hinzu, wobei oben, oben und unten die Übersicht und die Höhe auf 0 gesetzt sind. Sie müssen sie nicht mit anderen Unteransichten oder Ähnlichem verbinden.

Alte Lösung:

Hinweis: Wenn Sie UIScrollView im Querformat verwenden, werden horizontale Einfügungen immer noch nicht richtig eingestellt (ein weiterer Fehler?). Daher müssen Sie die führenden / nachfolgenden Zeichenfolgen von scrollView an safeAreaInsets in IB anheften.

Hinweis 2: Die folgende Lösung hat auch das Problem, dass tableView, wenn es nach unten gescrollt wird und Sie den Controller drücken und zurückspringen, nicht mehr unten angezeigt wird.

override func viewDidLoad()
{
    super.viewDidLoad()

    // This parts gets rid of animation when pushing
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .never
    }
}

override func viewDidDisappear(_ animated: Bool)
{
    super.viewDidDisappear(animated)
    // This parts gets rid of animation when popping
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .never
    }
}

override func viewDidAppear(_ animated: Bool)
{
    super.viewDidAppear(animated)
    // This parts sets correct behaviour(insets are correct on iPhone X)
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .automatic
    }
}
El Horrible
quelle
Was ist parentView?
PhoneyDeveloper
In der Hauptansicht Ihres View Controllers habe ich die Antwort aktualisiert. Vielen Dank.
El Horrible
Wenn ich UITableViewController verwende, ist tableView die Ansicht des View Controllers. Außerdem sollten die Einsätze unterschiedlich sein, wenn das Gerät gedreht wird. Ich möchte die Anpassung. Ich mag die Animation einfach nicht, wenn die Tabellenansicht zum ersten Mal angezeigt wird.
PhoneyDeveloper
In Ihrem Fall sollte UITableView, da es sich um die Ansicht des Controllers handelt, safeAreaInsets.bottom = 34 (Hochformat) haben, sodass Sie einfach tableView.contentInset = tableView.safeAreaInsets festlegen können.
El Horrible
Das funktioniert bei mir nicht. In viewDidLoad sind alle Einfügungen Null. Wenn ich versuche, diesen Code in viewWillLayoutSubviews zu verwenden, wird die Bildlaufanzeige zwar eingefügt, aber die tableView selbst kann horizontal scrollen. Wenn ich mir nur die Einfügungen in viewWillLayoutSubviews ansehe, ohne die Anpassung zu deaktivieren, wird der untere angepasste ContentInset 21, und das scheint sich zu ändern.
PhoneyDeveloper
3

Ich kann den Fehler für iOS 11.1 reproduzieren, aber es scheint, dass der Fehler seit iOS 11.2 behoben ist. Siehe http://openradar.appspot.com/34465226

Linda
quelle
Ja, behoben auf iOS 11.2
Richie Hyatt
2

Bitte stellen Sie sicher, dass Sie zusammen mit dem obigen Code zusätzlichen Code wie folgt hinzufügen. Es hat das Problem gelöst

override func viewDidLayoutSubviews() { 
     super.viewDidLayoutSubviews() 
     tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) // print(thisUISBTV.adjustedContentInset) 
}
Basavaraj Kalaghatagi
quelle
Dies allein hat mein Problem gelöst !! Danke dir. Verschwendete zu viele Stunden in dieser Angelegenheit!
Murat Yasar
2

Wenn Sie die Registerkartenleiste verwenden, ist der untere Inhalt der Sammlungsansicht Null. Fügen Sie dazu den folgenden Code in viewDidAppear ein:

if #available(iOS 11, *) {
    tableView.contentInset = self.collectionView.safeAreaInsets
}
Hasankose
quelle
2

In meinem Fall hat dies funktioniert (in viewDidLoad einfügen):

self.navigationController.navigationBar.translucent = YES;
nemissm
quelle
1

Entfernen von zusätzlichem Platz oben auf collectionViewodertableView

    if #available(iOS 11.0, *) {
        collectionView.contentInsetAdjustmentBehavior  = .never
        //tableView.contentInsetAdjustmentBehavior  = .never
    } else {
        automaticallyAdjustsScrollViewInsets = false
    }

Über dem Code collectionViewoder tableViewunter der Navigationsleiste.
Der folgende Code verhindert, dass die Sammlungsansicht unter der Navigation angezeigt wird

    self.edgesForExtendedLayout = UIRectEdge.bottom

Aber ich liebe es, unten Logik und Code für die UICollectionView zu verwenden

Kanteneinfügungswerte werden auf ein Rechteck angewendet, um den durch dieses Rechteck dargestellten Bereich zu verkleinern oder zu erweitern. In der Regel werden Kanteneinsätze während des Ansichtslayouts verwendet, um den Rahmen der Ansicht zu ändern. Positive Werte bewirken, dass der Rahmen um den angegebenen Betrag eingefügt (oder verkleinert) wird. Negative Werte bewirken, dass der Frame um den angegebenen Betrag versetzt (oder erweitert) wird.

collectionView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
//tableView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)

Der beste Weg für UICollectionView

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
}
Nazmul Hasan
quelle
0

Löschen Sie diese Code-Arbeit für mich

self.edgesForExtendedLayout = UIRectEdgeNone
weiminghuaa
quelle
0
  if #available(iOS 11, *) {
        self.edgesForExtendedLayout = UIRectEdge.bottom
  }

Ich habe UISearchController mit benutzerdefinierten resultsControllern mit Tabellenansicht verwendet. Durch Drücken des neuen Controllers auf den Ergebnis-Controller wurde die Tabellenansicht durchsucht.

Der oben aufgeführte Code hat das Problem vollständig behoben

Vitalii Shvetsov
quelle