Perilaku uitableview aneh di iOS11. Sel gulir ke atas dengan animasi dorong navigasi

116

Saya baru saja memigrasi beberapa kode ke SDK iOS 11 beta 5 yang baru.

Sekarang saya mendapatkan perilaku yang sangat membingungkan dari UITableView. Tableview itu sendiri tidak terlalu mewah. Saya memiliki sel khusus tetapi sebagian besar hanya untuk tinggi mereka.

Ketika saya mendorong pengontrol tampilan saya dengan tampilan tabel, saya mendapatkan animasi tambahan di mana sel "gulir ke atas" (atau mungkin seluruh bingkai tampilan tabel diubah) dan ke bawah di sepanjang animasi navigasi push / pop. Silakan lihat gif:

tableview bergelombang

Aku manual membuat tableviewdiloadView metode dan tata letak auto pengaturan kendala untuk menjadi sama dengan memimpin, mengikuti, atas, bawah SuperView tableview ini. Superview adalah tampilan root dari pengontrol tampilan.

Lihat pengontrol yang mendorong kode sangat standar: self.navigationController?.pushViewController(notifVC, animated: true)

Kode yang sama memberikan perilaku normal di iOS 10.

Bisakah Anda menunjukkan kepada saya apa yang salah?

EDIT: Saya telah membuat pengontrol tableview yang sangat sederhana dan saya dapat mereproduksi perilaku yang sama di sana. Kode:

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: Saya dapat mempersempit masalah ke kustomisasi UINavigationBar saya. Saya memiliki kustomisasi seperti ini:

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

dimana createFilledImage membuat gambar persegi dengan ukuran dan warna tertentu.

Jika saya mengomentari baris ini saya mendapatkan kembali perilaku normal.

Saya sangat menghargai pemikiran apapun tentang masalah ini.

iur
sumber
Mungkin tidak ada masalah dengan kustomisasi bilah navigasi. Saya mengalami masalah yang sama (jawaban yang diterima menyelesaikan ini) tanpa penyesuaian apa pun. Saya pikir ini mungkin menjadi masalah dengan cara iOS menangani tableview ketika dibuat secara manual sebagai subview, daripada menggunakan UITableViewController.
Mark Leonard
2
Saya melihat perilaku ini hanya ketika saya mengatur navigationBar.isTranslucentuntuk false, jika tidak bekerja dengan baik.
b_ray
5
Sepertinya ini adalah bug di iOS11 GM, harap menipu laporan bug itu sehingga masalah ini mendapat perhatian dari Apple: openradar.appspot.com/34465226
b_ray
1
Masalah ini tampaknya telah diperbaiki di iOS 11.2 beta. Saya tidak akan mengatur contentInsetAdjustmentBehavior menjadi tidak pernah karena merusak tampilan gulir iPhone X dengan tidak memberikan padding di bagian bawah layar. Bagian bawah tampilan konten Anda tetap berada di bawah "tombol" beranda iPhone X.
batu

Jawaban:

150

Ini karena properti baru UIScrollView's (UITableView adalah subclass dari UIScrollview)contentInsetAdjustmentBehavior , yang disetel ke .automaticdefault.

Anda dapat mengganti perilaku ini dengan cuplikan berikut di viewDidLoad dari pengontrol yang terpengaruh:

    tableView.contentInsetAdjustmentBehavior = .never

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

Maggy Hillen
sumber
2
komentar ini tentang definisi UIScrollViewContentInsetAdjustmentBehavior.automatic mengatakan: "... untuk kompatibilitas mundur juga akan menyesuaikan top & bottom contentInset saat tampilan gulir dimiliki oleh pengontrol tampilan dengan secara otomatisAdjustsScrollViewInsets = YES di dalam pengontrol navigasi, terlepas dari apakah scroll tampilan dapat digulir ". Teori saya adalah bahwa contentInset bilah navigasi dipengaruhi oleh pengaturan gambar latar belakang, yang kemudian menyesuaikan secara dinamis.
Maggy Hillen
8
Anda juga bisa melakukannya dengan storyboard. Size Inspector -> Content Insets -> Set 'Never'.
Woongbi Kim
4
Jika konten Anda meluas di belakang bilah tab, penonaktifan tableView.contentInsetAdjustmentBehaviorakan merusak insets.
kean
4
Juga, cukup menonaktifkan ini akan menempatkan indikator gulir di belakang (alat atas) pada iPhone X dalam lanskap. Inti dari perilaku ini adalah untuk menyesuaikan area konten tampilan gulir sehingga terlihat di layar yang tidak berbentuk persegi panjang. Saya pikir kita hanya bisa melihat ini saat ini di iPhone X Sim.
PhoneyDeveloper
8
Masalah ini disebabkan oleh bug di iOS 11 di mana safeAreaInsets dari tampilan pengontrol tampilan tidak disetel dengan benar selama transisi navigasi, yang seharusnya diperbaiki di iOS 11.2. Mengatur contentInsetAdjustmentBehaviorke .neverbukanlah solusi yang bagus karena kemungkinan besar akan memiliki efek samping lain yang tidak diinginkan. Jika Anda menggunakan solusi, Anda harus memastikan untuk menghapusnya untuk versi iOS> = 11.2.
smileyborg
23

Selain jawaban maggy

TUJUAN-C

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

Masalah ini disebabkan oleh bug di iOS 11 di mana safeAreaInsetstampilan pengontrol tampilan tidak disetel dengan benar selama transisi navigasi, yang seharusnya diperbaiki di iOS 11.2. Menyetel contentInsetAdjustmentBehaviorke.never bukanlah solusi yang bagus karena kemungkinan akan memiliki efek samping lain yang tidak diinginkan. Jika Anda menggunakan solusi, Anda harus memastikan untuk menghapusnya untuk versi iOS> = 11.2

-disebut oleh smileyborg (Insinyur Perangkat Lunak di Apple)

Lal Krishna
sumber
6

Anda dapat mengedit perilaku ini sekaligus di seluruh aplikasi dengan menggunakan NSProxy di misalnya didFinishLaunchingWithOptions:

if (@available(iOS 11.0, *)) {
      [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} 
BigDanceMouse
sumber
1
Ini akan merusak pengontrol sistem, seperti UIImagePickerController
galway
6

Inilah cara saya berhasil memperbaiki masalah ini sambil tetap mengizinkan iOS 11 untuk mengatur insets secara otomatis . Saya menggunakan UITableViewController.

  • Pilih "Perpanjang tepi di bawah bilah atas" dan "Perpanjang tepi di bawah bilah buram" di pengontrol tampilan Anda di storyboard (atau secara terprogram ). Sisipan area aman akan mencegah pandangan Anda masuk ke bawah bilah atas.
  • Centang tombol "Sisipkan ke Area Aman" pada tampilan tabel di papan cerita Anda. (atautableView.insetsContentViewsToSafeArea = true ) - Ini mungkin tidak perlu tetapi itulah yang saya lakukan.
  • Setel perilaku penyesuaian inset konten ke "Scrollable Axes" (atau tableView.contentInsetAdjustmentBehavior = .scrollableAxes) - .alwaysmungkin juga berfungsi tetapi saya tidak menguji.

Satu hal lagi untuk dicoba jika semuanya gagal:

viewSafeAreaInsetsDidChange UIViewControllerMetode override untuk mendapatkan tampilan tabel untuk memaksa mengatur insets tampilan gulir ke insets area aman. Ini terkait dengan pengaturan 'Tidak Pernah' dalam jawaban Maggy.

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

Catatan: self.tableViewdan self.viewharus sama untukUITableViewController

Sinterklas
sumber
Mencoba hampir semua yang ada di utas ini, dan ini berhasil untuk saya. TableViewVC disematkan di TabBarVC. Terima kasih!
Josh Wolff
3

Ini tampaknya lebih seperti bug daripada perilaku yang dimaksudkan. Itu terjadi ketika bilah navigasi tidak tembus cahaya atau ketika gambar latar belakang disetel.

Jika Anda baru saja mengatur contentInsetAdjustmentBehavior ke .never, inset konten tidak akan diatur dengan benar di iPhone X, misalnya konten akan masuk ke area bawah, di bawah scrollbar.

Ada dua hal yang perlu dilakukan:
1. mencegah scrollView beranimasi pada push / pop
2. mempertahankan perilaku otomatis karena diperlukan untuk iPhone X. Tanpa ini misalnya dalam potret, konten akan berada di bawah scrollbar bawah.

Solusi sederhana baru: di XIB: Cukup tambahkan UIView baru di atas tampilan utama Anda dengan top, leading dan trailing to superview dan height set to 0. Anda tidak perlu menghubungkannya ke subview lain atau apa pun.

Solusi lama:

Catatan: Jika Anda menggunakan UIScrollView dalam mode lanskap, itu masih tidak mengatur insets horizontal dengan benar (bug lain?), Jadi Anda harus memasang pin scrollView's leading / trailing ke safeAreaInsets di IB.

Catatan 2: Solusi di bawah ini juga memiliki masalah bahwa jika tableView di-scroll ke bawah, dan Anda menekan controller dan pop back, itu tidak akan berada di bawah lagi.

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
sumber
Apa itu parentView?
PhoneyDeveloper
Tampilan utama pengontrol tampilan Anda, saya telah memperbarui jawabannya. Terima kasih.
El Horrible
Saat saya menggunakan UITableViewController, tableView adalah tampilan pengontrol tampilan. Selain itu, inset harus berbeda saat perangkat diputar. Saya ingin penyesuaian. Saya hanya tidak suka animasi saat tableView pertama kali muncul.
PhoneyDeveloper
Dalam kasus Anda karena UITableView adalah tampilan pengontrol, UITableView harus memiliki safeAreaInsets.bottom = 34 (potret), jadi Anda bisa menyetel tableView.contentInset = tableView.safeAreaInsets.
El Horrible
Itu tidak berhasil untuk saya. Di viewDidLoad semua sisipan adalah nol. Jika saya mencoba menggunakan kode itu di viewWillLayoutSubviews indikator gulir tidak mendapatkan inset tetapi tableView itu sendiri menjadi dapat menggulir secara horizontal. Jika saya hanya melihat insets di viewWillLayoutSubviews tanpa menonaktifkan penyesuaian, AdjustContentInset bawah menjadi 21 dan hanya itu yang tampaknya berubah.
PhoneyDeveloper
3

Saya dapat mereproduksi bug untuk iOS 11.1 tetapi tampaknya bug telah diperbaiki sejak iOS 11.2. Lihat http://openradar.appspot.com/34465226

Linda
sumber
ya, diperbaiki di iOS 11.2
Richie Hyatt
2

pastikan bersama kode di atas, tambahkan kode tambahan sebagai berikut. Itu memecahkan masalah

override func viewDidLayoutSubviews() { 
     super.viewDidLayoutSubviews() 
     tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) // print(thisUISBTV.adjustedContentInset) 
}
Basavaraj Kalaghatagi
sumber
Ini saja yang memecahkan masalah saya !! Terima kasih. Terlalu banyak waktu terbuang untuk masalah ini!
Murat Yasar
2

Juga jika Anda menggunakan bilah tab, inset konten bawah tampilan koleksi akan menjadi nol. Untuk ini, letakkan kode di bawah ini di viewDidAppear:

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

Dalam kasus saya ini berhasil (taruh di viewDidLoad):

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

Menghapus ruang ekstra di atas collectionViewatautableView

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

Di atas kode collectionViewatau di tableViewbawah bilah navigasi.
Kode di bawah ini mencegah tampilan koleksi untuk masuk ke bawah navigasi

    self.edgesForExtendedLayout = UIRectEdge.bottom

tapi saya suka menggunakan logika dan kode di bawah ini untuk UICollectionView

Nilai sisipan tepi diterapkan ke persegi panjang untuk memperkecil atau memperluas area yang diwakili oleh persegi tersebut. Biasanya, sisipan tepi digunakan selama tata letak tampilan untuk mengubah bingkai tampilan. Nilai positif menyebabkan frame menjadi inset (atau menyusut) dengan jumlah yang ditentukan. Nilai negatif menyebabkan frame diawali (atau diperluas) dengan jumlah yang ditentukan.

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

Cara terbaik untuk UICollectionView

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

Hapus kode ini berfungsi untuk saya

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

Saya menggunakan UISearchController dengan resultControllers khusus yang memiliki tampilan tabel. Mendorong pengontrol baru pada pengontrol hasil menyebabkan tampilan tabel berada di bawah penelusuran.

Kode yang tercantum di atas benar-benar memperbaiki masalah

Vitalii Shvetsov
sumber