Hapus semua subview?

316

Ketika aplikasi saya kembali ke controller tampilan root, dalam viewDidAppear:metode ini saya harus menghapus semua subview.

Bagaimana saya bisa melakukan ini?

Ian Vink
sumber

Jawaban:

569

Sunting: Dengan terima kasih kepada cocoafan : Situasi ini kacau oleh fakta bahwa NSViewdan UIViewmenangani berbagai hal secara berbeda. Untuk NSView(pengembangan Mac desktop), Anda cukup menggunakan yang berikut ini:

[someNSView setSubviews:[NSArray array]];

Untuk UIView(pengembangan iOS saja), Anda dapat menggunakan dengan aman makeObjectsPerformSelector:karena subviewsproperti akan mengembalikan salinan array subview:

[[someUIView subviews]
 makeObjectsPerformSelector:@selector(removeFromSuperview)];

Terima kasih kepada Tommy untuk menunjukkan yang makeObjectsPerformSelector:tampaknya mengubah subviewsarray saat sedang dihitung (memang untuk apa NSView, tetapi tidak untuk UIView).

Silakan lihat pertanyaan SO ini untuk lebih jelasnya.

Catatan: Menggunakan salah satu dari kedua metode ini akan menghapus setiap tampilan yang berisi tampilan utama Anda dan melepaskannya , jika tidak disimpan di tempat lain. Dari dokumentasi Apple tentang removeFromSuperview :

Jika superview penerima tidak nol, metode ini melepaskan penerima. Jika Anda berencana untuk menggunakan kembali tampilan, pastikan untuk menyimpannya sebelum memanggil metode ini dan pastikan untuk melepaskannya sesuai ketika Anda selesai dengan itu atau setelah menambahkannya ke hierarki tampilan lain.

e.James
sumber
9
Apakah Anda yakin ini aman? Itu bermutasi daftar saat iterasi, dan saya tidak dapat menemukan pernyataan definitif dalam dokumentasi Apple.
Tommy
8
@Tommy: Itu poin bagus. Beberapa Googling muncul jawabannya: UIViewkembali sebuah salinan dari subviewsarray bisa berubah, jadi kode ini hanya bekerja. Cerita yang sama sekali berbeda di desktop, di mana kode yang sama akan memberikan pengecualian. Lihat stackoverflow.com/questions/4665179/…
e.James
3
UIView tidak menanggapi setSubviews:, bukan?
cocoafan
4
cara Xamarin: someUIView.Subviews.All (p => p.RemoveFromSuperview);
Benoit Jadinon
3
@ BenenoJadinon - tidak akan dikompilasi - Anda tampaknya bermaksud menyalahgunakan Semua untuk melakukan ForEach, jadi someUIView.Subviews.All( v => { v.RemoveFromSuperview(); return true; } );. IMHO cleaner untuk mengatakan apa yang Anda maksud: someUIView.Subviews.ToList().ForEach( v => v.RemoveFromSuperview() );.
ToolmakerSteve
173

Dapatkan semua subview dari pengontrol root Anda dan kirim masing-masing removeFromSuperview:

NSArray *viewsToRemove = [self.view subviews];
for (UIView *v in viewsToRemove) {
    [v removeFromSuperview];
}
Matthew McGoogan
sumber
+1 dan terima kasih. Seharusnya saya juga menggunakan self.viewseperti yang Anda miliki.
e.James
2
kenapa tidak!? for (UIView *v in [self.view subviews])lebih mudah
Frade
4
@ Teman Jauh lebih jelas dan lebih jelas cara dia melakukannya. Verbose dan mudah dibaca> hemat penekanan tombol
taylorcressy
33
@taylorcressy Anda seharusnya mengatakan "keterbacaan lebih penting daripada menyimpan penekanan tombol" daripada "keterbacaan> menyimpan penekanan tombol" dan kemudian komentar Anda akan lebih mudah dibaca. :-)
arlomedia
1
Jangan lupa tentang fakta bahwa jika [tinjau ulang self.view] melakukan perhitungan apa pun di bawah tenda, menempatkannya langsung di dalam for loop dapat menyebabkan perhitungan tersebut dilakukan berulang-ulang. Mendeklarasikannya sebelum loop memastikan mereka hanya dilakukan sekali.
Brian Sachetta
116

Di Swift Anda dapat menggunakan pendekatan fungsional seperti ini:

view.subviews.forEach { $0.removeFromSuperview() }

Sebagai perbandingan, pendekatan imperatif akan terlihat seperti ini:

for subview in view.subviews {
    subview.removeFromSuperview()
}

Cuplikan kode ini hanya berfungsi di iOS / tvOS , ada sedikit perbedaan pada macOS.

Astaga
sumber
4
(subviews as [UIView]).map { $0.removeFromSuperview() }
DeFrenZ
8
itu tidak fungsional karena fungsi mengembalikan nilai dan ini hanya membuang hasil .map. ini adalah efek samping murni dan lebih baik ditangani seperti ini:view.subviews.forEach() { $0.removeFromSuperview() }
Martin Algesten
1
Anda benar, Martin, saya setuju dengan Anda. Saya hanya tidak tahu ada metode forEach () pada Array. Kapan ditambahkan atau apakah saya hanya mengawasinya? Saya telah memperbarui jawaban saya!
Jeehut
1
Saya sangat malas sehingga bahkan jika saya tahu cara menghapus subview, saya masih datang ke sini untuk menyalin / menempelkan cuplikan Anda dan meletakkan +1
Vilmir
13

Jika Anda ingin menghapus semua subview pada UIView Anda (di sini yourView), maka tulis kode ini di klik tombol Anda:

[[yourView subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
Mohd Rahib
sumber
12
Selamat Datang di Stack Overflow! Apakah Anda mempertimbangkan untuk menambahkan narasi untuk menjelaskan mengapa kode ini berfungsi, dan apa yang membuatnya menjadi jawaban untuk pertanyaan itu? Ini akan sangat membantu bagi orang yang mengajukan pertanyaan, dan siapa pun yang datang. Selain itu, jawaban yang sudah diterima termasuk kode yang pada dasarnya sama dengan ini.
Andrew Barber
5
Bagaimana ini bisa membantu lebih daripada jawaban yang diterima: Itu identik. Kenapa menulis ini?
Rambatino
8

Ini hanya berlaku untuk OSX karena di iOS salinan array disimpan

Saat menghapus semua subview, itu ide yang baik untuk mulai menghapus di akhir array dan terus menghapus sampai Anda mencapai awal. Ini dapat dicapai dengan dua baris kode ini:

for (int i=mySuperView.subviews.count-1; i>=0; i--)
        [[mySuperView.subviews objectAtIndex:i] removeFromSuperview];

SWIFT 1.2

for var i=mySuperView.subviews.count-1; i>=0; i-- {
    mySuperView.subviews[i].removeFromSuperview();
}

atau (kurang efisien, tetapi lebih mudah dibaca)

for subview in mySuperView.subviews.reverse() {
    subview.removeFromSuperview()
}

CATATAN

Anda TIDAK boleh menghapus subview dalam urutan normal, karena dapat menyebabkan crash jika instance UIView dihapus sebelum removeFromSuperviewpesan dikirim ke semua objek array. (Jelas, menghapus elemen terakhir tidak akan menyebabkan kerusakan)

Karena itu, kodenya

[[someUIView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];

seharusnya TIDAK digunakan.

Kutipan dari dokumentasi Apple tentang makeObjectsPerformSelector :

Mengirim ke setiap objek dalam array pesan yang diidentifikasi oleh pemilih yang diberikan, dimulai dengan objek pertama dan melanjutkan melalui array ke objek terakhir.

(yang akan menjadi arah yang salah untuk tujuan ini)

Daniel
sumber
Bisakah Anda membuat contoh tentang apa yang Anda maksud? Tidak tahu apa yang Anda sebut sebagai "elemen" Dan bagaimana elemen ini akan dihapus sebelum memanggil removeFromSuperView?
Pendeta
Tetapi bagaimana sebuah instance dari UIView dapat dihapus saat memanggil metode ini? Apakah maksud Anda dihapus dari array subview?
Pendeta
Ketika removeFromSuperviewselesai, UIView akan dihapus dari array, dan jika tidak ada instance hidup lainnya yang memiliki hubungan kuat dengan UIView, UIView juga akan dihapus. Ini dapat menyebabkan pengecualian di luar batas.
Daniel
1
Kena kau! Terima kasih. Saya pikir Anda mendapatkan salinan array subviews di iOS. Bagaimanapun, itu adalah ide yang baik untuk membuat salinan sendiri jika Anda ingin menghapus subview.
Pendeta
@simpleBob - apakah Anda membaca komentar yang ditulis pada 2011 tentang jawaban yang diterima Menurut komentar tersebut, di iOS, [yourView subviews]mengembalikan COPY dari array, oleh karena itu aman. (Perhatikan bahwa pada OSX, apa yang Anda katakan benar.)
ToolmakerSteve
4

Coba cara ini cepat 2.0

view.subviews.forEach { $0.removeFromSuperview() }
William Hu
sumber
2
Tidakkah kamu melihat tanggal jawabanku sebelumnya? Mengapa tidak menempelkan tautan jawaban saya ke jawaban itu?
William Hu
1
Benar ... jawabannya telah diposting sebelum Anda tetapi forEachsolusi berbasis ditambahkan setelah Anda, saya melewatkan itu. Permintaan maaf.
Cristik
4

Gunakan kode berikut untuk menghapus semua subview.

for (UIView *view in [self.view subviews]) 
{
 [view removeFromSuperview];
}
Shahzaib Maqbool
sumber
2

Menggunakan UIViewekstensi Swift :

extension UIView {
    func removeAllSubviews() {
        for subview in subviews {
            subview.removeFromSuperview()
        }
    }
}
mixel
sumber
2

Dalam objektif-C, maju dan buat metode kategori dari kelas UIView.

- (void)removeAllSubviews
{
    for (UIView *subview in self.subviews)
        [subview removeFromSuperview];
}
Rickster
sumber
2
view.subviews.forEach { $0.removeFromSuperview() }
MAGiGO
sumber
2
Sementara kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang bagaimana dan / atau mengapa memecahkan masalah akan meningkatkan nilai jangka panjang jawaban.
Nic3500
1

Untuk menghapus semua sintaks subview:

- (void)makeObjectsPerformSelector:(SEL)aSelector;

Penggunaan:

[self.View.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];

Metode ini hadir dalam file NSArray.h dan menggunakan antarmuka NSArray (NSExtendedArray)

Jayprakash Dubey
sumber
1

Jika Anda menggunakan Swift, sesederhana:

subviews.map { $0.removeFromSuperview }

Ini mirip dalam filosofi dengan makeObjectsPerformSelectorpendekatan, namun dengan sedikit lebih aman.

lms
sumber
Ini secara semantik salah, mapseharusnya tidak menghasilkan efek samping. Plus, hasil yang sama dapat dicapai melalui forEach.
Cristik
0

Untuk ios6 menggunakan autolayout saya harus menambahkan sedikit kode untuk menghilangkan kendala juga.

NSMutableArray * constraints_to_remove = [ @[] mutableCopy] ;
for( NSLayoutConstraint * constraint in tagview.constraints) {
    if( [tagview.subviews containsObject:constraint.firstItem] ||
       [tagview.subviews containsObject:constraint.secondItem] ) {
        [constraints_to_remove addObject:constraint];
    }
}
[tagview removeConstraints:constraints_to_remove];

[ [tagview subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];

Saya yakin ada cara yang lebih rapi untuk melakukan ini, tetapi itu berhasil bagi saya. Dalam kasus saya, saya tidak bisa menggunakan direct [tagview removeConstraints:tagview.constraints]karena ada batasan yang ditetapkan dalam XCode yang sedang dibersihkan.

Michael Anderson
sumber
0

Di monotouch / xamarin.ios ini berfungsi untuk saya:

SomeParentUiView.Subviews.All(x => x.RemoveFromSuperview);
Daniele D.
sumber
-10

Untuk menghapus semua subview dari superviews:

NSArray *oSubView = [self subviews];
for(int iCount = 0; iCount < [oSubView count]; iCount++)
{
    id object = [oSubView objectAtIndex:iCount];
    [object removeFromSuperview];
    iCount--;
}
Pravin
sumber
Beberapa kesalahan besar di sini @Pravin. Pertama, Anda perlu 'objek' untuk didefinisikan sebagai UIView * jika tidak, Anda akan mendapatkan kesalahan kompiler dengan [objek removeFromSuperview]. Kedua, for for Anda sudah mengurangi iCount sehingga Anda melewatkan satu lagi dengan garis iCount - Anda. Dan akhirnya, ada dua pendekatan yang berfungsi dan benar di atas dan Anda tidak lebih elegan atau lebih cepat.
amergin
5
setiap iterasi yang Anda lakukan iCount++dan iCount--, biarkan indeksnya sama, sehingga akan menjadi infinite loop jika [oSubView count]>0. Ini jelas buggy dan BUKAN kode yang bisa digunakan.
Daniel