Skenario: Pengguna mengetuk tombol pada pengontrol tampilan. Pengontrol tampilan adalah yang paling atas (jelas) di tumpukan navigasi. Ketuk memanggil metode kelas utilitas yang dipanggil kelas lain. Hal buruk terjadi di sana dan saya ingin menampilkan peringatan di sana sebelum kontrol kembali ke pengontrol tampilan.
+ (void)myUtilityMethod {
// do stuff
// something bad happened, display an alert.
}
Ini dimungkinkan dengan UIAlertView
(tapi mungkin tidak cukup tepat).
Dalam hal ini, bagaimana Anda menyajikan UIAlertController
, di sana myUtilityMethod
?
sumber
Di WWDC, saya mampir di salah satu laboratorium dan bertanya pada Apple Engineer pertanyaan yang sama: "Apa praktik terbaik untuk menampilkan
UIAlertController
?" Dan dia mengatakan mereka telah banyak mendapatkan pertanyaan ini dan kami bercanda bahwa mereka seharusnya memiliki sesi tentang itu. Dia mengatakan bahwa secara internal Apple menciptakanUIWindow
dengan transparanUIViewController
dan kemudian menyajikannyaUIAlertController
. Pada dasarnya apa yang ada dalam jawaban Dylan Betterman.Tapi saya tidak ingin menggunakan subkelas
UIAlertController
karena itu akan mengharuskan saya mengubah kode saya di seluruh aplikasi saya. Jadi dengan bantuan objek terkait, saya membuat kategoriUIAlertController
yang menyediakanshow
metode di Objective-C.Ini kode yang relevan:
Berikut ini contoh penggunaannya:
Yang
UIWindow
dibuat akan dihancurkan ketikaUIAlertController
dealloced, karena itu adalah satu-satunya objek yang mempertahankanUIWindow
. Tetapi jika Anda menetapkanUIAlertController
ke properti atau menyebabkan jumlah tetapnya meningkat dengan mengakses lansiran di salah satu blok tindakan, yangUIWindow
akan tetap ada di layar, mengunci UI Anda. Lihat contoh kode penggunaan di atas untuk menghindari jika perlu mengaksesUITextField
.Saya membuat repo GitHub dengan proyek uji: FFGlobalAlertController
sumber
viewDidDisappear:
pada kategori tampak seperti Gagasan Buruk. Intinya, Anda bersaing dengan implementasi kerangka kerjaviewDidDisappear:
. Untuk saat ini mungkin baik-baik saja, tetapi jika Apple memutuskan untuk mengimplementasikan metode itu di masa depan, tidak ada cara bagi Anda untuk menyebutnya (yaitu tidak ada analoginyasuper
yang menunjuk pada implementasi utama dari metode dari implementasi kategori) .prefersStatusBarHidden
danpreferredStatusBarStyle
tanpa subkelas tambahan?Cepat
Objektif-C
sumber
Anda dapat melakukan hal berikut dengan Swift 2.2:
Dan Swift 3.0:
sumber
Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy!
.visibleViewController
properti kapan saja untuk melihat dari pengontrol mana Anda dapat menyampaikan peringatan. Lihat dokumenCukup generik
UIAlertController
extension
untuk semua kasusUINavigationController
dan / atauUITabBarController
. Juga berfungsi jika ada modal VC di layar saat ini.Pemakaian:
Ini ekstensi:
sumber
UI
kelas yang memegang (lemah!)currentVC
JenisUIViewController
.Saya memilikiBaseViewController
yang mewarisi dariUIViewController
dan setUI.currentVC
keself
atasviewDidAppear
lalu kenil
atasviewWillDisappear
. Semua pengendali tampilan saya di dalam aplikasi mewarisiBaseViewController
. Dengan begitu jika Anda memiliki sesuatuUI.currentVC
(bukannil
...) - pasti tidak di tengah-tengah animasi presentasi, dan Anda dapat memintanya untuk menyajikannyaUIAlertController
.else { if let presentedViewController = controller.presentedViewController { presentedViewController.presentViewController(self, animated: animated, completion: completion) } else { controller.presentViewController(self, animated: animated, completion: completion) } }
Memperbaiki jawaban agilityvision , Anda harus membuat jendela dengan pengontrol tampilan root transparan dan menyajikan tampilan lansiran dari sana.
Namun selama Anda memiliki tindakan di pengontrol peringatan Anda , Anda tidak perlu menyimpan referensi ke jendela . Sebagai langkah terakhir dari blok pengendali tindakan, Anda hanya perlu menyembunyikan jendela sebagai bagian dari tugas pembersihan. Dengan memiliki referensi ke jendela di blok handler, ini menciptakan referensi melingkar sementara yang akan rusak setelah pengontrol lansiran dibubarkan.
sumber
Solusi berikut ini tidak berfungsi meskipun terlihat cukup menjanjikan dengan semua versi. Solusi ini menghasilkan PERINGATAN .
Peringatan: Berusaha menampilkan pandangan siapa yang tidak ada dalam hierarki jendela!
https://stackoverflow.com/a/34487871/2369867 => Ini terlihat menjanjikan saat itu. Tapi itu tidak di
Swift 3
. Jadi saya menjawab ini di Swift 3 dan ini bukan contoh template.Ini adalah kode yang agak berfungsi penuh dengan sendirinya begitu Anda menempelkan di dalam fungsi apa pun.
Ini diuji dan kode kerjanya dalam Swift 3.
sumber
UIWindow
atau jendela itu akan dirilis dan akan segera hilang setelah keluar dari ruang lingkup.Berikut jawaban mitos mister sebagai ekstensi, diuji & bekerja di Swift 4:
Contoh penggunaan:
sumber
Ini berfungsi di Swift untuk pengontrol tampilan normal dan bahkan jika ada pengontrol navigasi di layar:
sumber
UIWindow
itu tidak responsif. Ada hubungannya dengan yangwindowLevel
mungkin. Bagaimana saya bisa membuatnya responsif?alertWindow
untuknil
ketika Anda selesai dengan itu.Menambahkan ke jawaban Zev (dan beralih kembali ke Objective-C), Anda bisa mengalami situasi di mana pengontrol tampilan root Anda menyajikan beberapa VC lain melalui segue atau sesuatu yang lain. Memanggil presentViewController pada root VC akan menangani hal ini:
Ini meluruskan masalah yang saya miliki di mana VC root telah dipisahkan ke VC lain, dan alih-alih menghadirkan pengontrol peringatan, peringatan seperti yang dilaporkan di atas dikeluarkan:
Saya belum mengujinya, tetapi ini mungkin juga perlu jika root VC Anda menjadi pengendali navigasi.
sumber
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController?.presentViewController(controller, animated: true, completion: nil)
Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x15cd4afe0>)
rootViewController.presentedViewController
jika tidak nihil, jika tidak gunakanrootViewController
. Untuk solusi yang sepenuhnya generik, mungkin perlu untuk menjalankan rantaipresentedViewController
s untuk mencapaitopmost
VC@ jawaban agilityvision diterjemahkan ke Swift4 / iOS11. Saya belum pernah menggunakan string yang dilokalkan, tetapi Anda dapat mengubahnya dengan mudah:
sumber
window.backgroundColor = UIColor.clear
memperbaikinya.viewController.view.backgroundColor = UIColor.clear
tampaknya tidak perlu.UIAlertController
subclassing:The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.
developer.apple.com/documentation/uikit/uialertcontrollerBuat Extension seperti pada jawaban Aviel Gross. Di sini Anda memiliki ekstensi Objective-C.
Di sini Anda memiliki file header * .h
Dan implementasi: * .m
Anda menggunakan ekstensi ini di file implementasi Anda seperti ini:
sumber
Cross post jawaban saya karena kedua utas ini tidak ditandai sebagai dupes ...
Sekarang itu
UIViewController
adalah bagian dari rantai responden, Anda dapat melakukan sesuatu seperti ini:sumber
Jawaban Zev Eisenberg sederhana dan langsung, tetapi tidak selalu berhasil, dan mungkin gagal dengan pesan peringatan ini:
Ini karena windows rootViewController tidak di bagian atas tampilan yang disajikan. Untuk memperbaikinya kita perlu menjalankan rantai presentasi, seperti yang ditunjukkan dalam kode ekstensi UIAlertController saya yang ditulis dalam Swift 3:
Pembaruan pada 15/15/2017:
Diuji dan dikonfirmasi bahwa logika di atas masih berfungsi dengan baik di seed iOS 11 GM yang baru tersedia. Metode pemilihan teratas oleh agilityvision, bagaimanapun, tidak: tampilan peringatan disajikan dalam yang baru dicetak
UIWindow
berada di bawah keyboard dan berpotensi mencegah pengguna mengetuk tombolnya. Ini karena di iOS 11 semua windowLevel lebih tinggi dari jendela keyboard diturunkan ke level di bawahnya.Salah satu artefak presentasi dari
keyWindow
adalah animasi keyboard geser ke bawah ketika peringatan disajikan, dan geser ke atas saat peringatan diberhentikan. Jika Anda ingin keyboard tetap berada di sana selama presentasi, Anda dapat mencoba menyajikan dari jendela atas itu sendiri, seperti yang ditunjukkan pada kode di bawah ini:Satu-satunya bagian yang tidak begitu bagus dari kode di atas adalah bahwa ia memeriksa nama kelas
UIRemoteKeyboardWindow
untuk memastikan kita juga bisa memasukkannya. Namun demikian kode di atas tidak berfungsi dengan baik di iOS 9, 10 dan 11 benih GM, dengan warna warna yang tepat dan tanpa artefak sliding keyboard.sumber
Swift 4+
Solusi Saya gunakan selama bertahun-tahun tanpa masalah sama sekali. Pertama-tama saya memperluas
UIWindow
untuk menemukan itu terlihatViewController. CATATAN : jika Anda menggunakan kelas koleksi kustom * (seperti menu samping) Anda harus menambahkan handler untuk kasus ini dalam ekstensi berikut. Setelah mendapatkan pengontrol tampilan paling atas, mudah untuk ditampilkanUIAlertController
seperti ituUIAlertView
.sumber
Untuk iOS 13, bangun jawaban dengan mitos pembuat kode dan bobbyreh :
Di iOS 13, jika Anda membuat jendela sendiri untuk menampilkan lansiran, Anda harus memegang referensi kuat ke jendela itu atau jika tidak lansiran Anda tidak akan ditampilkan karena jendela akan segera dibatalkan alokasinya ketika referensi keluar dari ruang lingkup.
Selanjutnya, Anda perlu mengatur referensi ke nil lagi setelah lansiran diberhentikan untuk menghapus jendela untuk terus memungkinkan interaksi pengguna pada jendela utama di bawahnya.
Anda dapat membuat
UIViewController
subkelas untuk merangkum logika manajemen memori jendela:Anda dapat menggunakan ini apa adanya, atau jika Anda menginginkan metode kenyamanan pada Anda
UIAlertController
, Anda dapat membuangnya dalam ekstensi:sumber
dismiss
WindowAlertPresentationController secara langsungalert.presentingViewController?.dismiss(animated: true, completion: nil)
Cara singkat untuk menyajikan peringatan di Objective-C:
Di mana objek
alertController
AndaUIAlertController
.CATATAN: Anda juga harus memastikan kelas pembantu Anda diperluas
UIViewController
sumber
Jika ada yang tertarik, saya membuat versi @agilityvision jawaban Swift 3. Kode:
sumber
Dengan ini, Anda dapat dengan mudah menampilkan peringatan seperti itu
Satu hal yang perlu diperhatikan adalah jika ada UIAlertController yang sedang ditampilkan,
UIApplication.topMostViewController
akan mengembalikan aUIAlertController
. Menyajikan di atasUIAlertController
memiliki perilaku aneh dan harus dihindari. Dengan demikian, Anda harus memeriksa secara manual!(UIApplication.topMostViewController is UIAlertController)
sebelum mempresentasikan, atau menambahkanelse if
case untuk mengembalikan nil jikaself is UIAlertController
sumber
Anda dapat mengirim tampilan atau pengontrol saat ini sebagai parameter:
sumber
Kevin Sliech memberikan solusi hebat.
Saya sekarang menggunakan kode di bawah ini di subclass UIViewController utama saya.
Satu perubahan kecil yang saya buat adalah memeriksa untuk melihat apakah pengontrol presentasi terbaik bukan UIViewController biasa. Jika tidak, pasti ada beberapa VC yang menghadirkan VC biasa. Jadi kami mengembalikan VC yang disajikan sebagai gantinya.
Tampaknya semua berhasil sejauh ini dalam pengujian saya.
Kevin terima kasih!
sumber
Selain jawaban yang besar diberikan ( agilityvision , adib , malhal ). Untuk mencapai perilaku antrian seperti di UIAlertViews lama yang baik (hindari tumpang tindih jendela peringatan), gunakan blok ini untuk mengamati ketersediaan tingkat jendela:
Contoh lengkap:
Ini akan memungkinkan Anda untuk menghindari jendela peringatan tumpang tindih. Metode yang sama dapat digunakan untuk memisahkan dan memasukkan pengontrol tampilan antrian untuk sejumlah lapisan jendela.
sumber
Saya mencoba semua yang disebutkan, tetapi tidak berhasil. Metode yang saya gunakan untuk Swift 3.0:
sumber
Beberapa jawaban ini hanya berfungsi sebagian untuk saya, menggabungkannya dalam metode kelas berikut di AppDelegate adalah solusi bagi saya. Ini berfungsi di iPad, dalam tampilan UITabBarController, di UINavigationController, dan ketika menampilkan modals. Diuji pada iOS 10 dan 13.
Pemakaian:
sumber
Dukungan adegan iOS13 (saat menggunakan UIWindowScene)
sumber
Anda dapat mencoba mengimplementasikan kategori
UIViewController
dengan mehtod seperti- (void)presentErrorMessage;
Dan dan di dalam metode itu Anda mengimplementasikan UIAlertController dan kemudian menampilkannyaself
. Dari pada kode klien Anda, Anda akan memiliki sesuatu seperti:[myViewController presentErrorMessage];
Dengan cara itu Anda akan menghindari parameter dan peringatan yang tidak perlu tentang tampilan yang tidak berada dalam hierarki jendela.
sumber
myViewController
kode di mana hal buruk terjadi. Itu dalam metode utilitas yang tidak tahu apa-apa tentang view controller yang memanggilnya.UIAlertView
membuat saya melanggar peraturan itu di beberapa tempat.Ada 2 pendekatan yang dapat Anda gunakan:
-Menggunakan
UIAlertView
atau 'UIActionSheet' sebagai gantinya (tidak disarankan, karena sudah usang di iOS 8 tetapi berfungsi sekarang)-Beberapa ingat pengontrol tampilan terakhir yang disajikan. Berikut ini contohnya.
Pemakaian:
sumber
Saya menggunakan kode ini dengan sedikit variasi pribadi di kelas AppDelegate saya
sumber
Tampaknya bekerja:
sumber
buat kelas pembantu AlertWindow dan gunakan sebagai
sumber