Mendapatkan referensi ke tampilan / jendela paling atas di aplikasi iOS

97

Saya membuat kerangka kerja yang dapat digunakan kembali untuk menampilkan pemberitahuan dalam aplikasi iOS. Saya ingin tampilan notifikasi ditambahkan di atas segalanya dalam aplikasi, semacam UIAlertView. Ketika saya memulai manajer yang mendengarkan acara NSNotification dan menambahkan tampilan sebagai tanggapan, saya perlu mendapatkan referensi ke tampilan paling atas dalam aplikasi. Inilah yang saya miliki saat ini:

_topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];

Apakah ini akan berfungsi untuk aplikasi iOS apa pun atau apakah ini cara yang lebih aman / lebih baik untuk mendapatkan tampilan teratas?

typeoneerror
sumber

Jawaban:

36

Biasanya itu akan memberi Anda tampilan teratas, tetapi tidak ada jaminan bahwa itu terlihat oleh pengguna. Bisa di luar layar, memiliki alpha 0,0, atau bisa berukuran 0x0 misalnya.

Bisa juga karena keyWindow tidak memiliki subview, jadi Anda mungkin harus mengujinya terlebih dahulu. Ini tidak biasa, tetapi bukan tidak mungkin.

UIWindow adalah subclass dari UIView, jadi jika Anda ingin memastikan notifikasi Anda terlihat oleh pengguna, Anda dapat menambahkannya langsung ke keyWindow menggunakan addSubview:dan itu akan langsung menjadi tampilan paling atas. Saya tidak yakin apakah ini yang ingin Anda lakukan. (Berdasarkan pertanyaan Anda, sepertinya Anda sudah mengetahui hal ini.)

Kris Markel
sumber
111

Setiap kali saya ingin menampilkan beberapa overlay di atas segalanya, saya hanya menambahkannya di atas Jendela Aplikasi secara langsung:

[[[UIApplication sharedApplication] keyWindow] addSubview:someView]
samvermette
sumber
9
Hanya bekerja dalam orientasi potret .. Anda harus memperhatikan rotasi dll seperti ini: stackoverflow.com/questions/2508630/…
hfossli
1
Saya mendapatkan (null)di aplikasi iOS-8 saya (masalah papan cerita?) Ada petunjuk? Terima kasih! (Catatan: (null)dikembalikan pada viewDidLoaddan viewWillAppear:waktu. viewDidAppear:Sudah terlambat.
Olie
apakah ini berfungsi saat kami menampilkan pengontrol tampilan ?. Akankah subview tambahan ditampilkan di atas pengontrol tampilan yang disajikan?
Pratyusha Terli
Ini tidak akan melampaui keyboard, lastObject melakukannya.
Ser Pounce
Kerangka kerja ini dapat bekerja di semua orientasi. Dan akan pergi di atas keyboard. github.com/HarrisonXi/TopmostView
Harrison Xi
47

Masalahnya ada dua: Jendela atas, tampilan atas di jendela atas.

Semua jawaban yang ada melewatkan bagian jendela atas. Tapi [[UIApplication sharedApplication] keyWindow]tidak dijamin akan menjadi jendela atas.

  1. Jendela atas. Sangat tidak mungkin bahwa akan ada dua jendela dengan aplikasi yang windowLevelberdampingan sama , jadi kita dapat mengurutkan semua jendela windowLeveldan mendapatkan yang paling atas.

    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
  2. Tampilan atas di jendela atas. Sekadar melengkapi. Seperti yang telah ditunjukkan dalam pertanyaan:

    UIView *topView = [[topWindow subviews] lastObject];
an0
sumber
5
Solusi ini berhenti berfungsi untuk saya setelah UIAlertViews mulai bekerja - mereka tampaknya meninggalkan UIWindow yang kosong, jadi saya kembali ketopView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
Gereon
4
Ini tidak berfungsi jika keyboard sudah menyala. Karena itu akan menjadi jendela paling atas
entropi
@Gereon, pastikan Anda tidak memiliki referensi yang kuat ke UIAlertView mana pun. Jika ini bukan referensi yang lemah, UIAlertOverlayWindow akan tetap berada dalam hierarki dan dikenali sebagai jendela kunci, tetapi subview yang ditambahkan ke dalamnya tidak akan ditampilkan.
beebcon
Nilai topWindow tidak sesuai untuk iOS 8
Mehul Thakkar
11

Sebenarnya mungkin ada lebih dari satu UIWindow di aplikasi Anda. Misalnya, jika keyboard ada di layar, maka[[UIApplication sharedApplication] windows] akan berisi setidaknya dua jendela (jendela kunci dan jendela keyboard Anda).

Jadi jika Anda ingin tampilan Anda muncul di atas keduanya maka Anda harus melakukan sesuatu seperti:

[[[[UIApplication sharedApplication] windows] lastObject] addSubview:view];

(Dengan asumsi lastObject berisi jendela dengan prioritas windowLevel tertinggi).

lorean
sumber
[[[UIApplication sharedApplication] windows] lastObject] nilai tidak sesuai untuk ios 8
Mehul Thakkar
Saya mulai menggunakan ini, tetapi tampaknya terkadang jendela keyboard ada tetapi tidak muncul, dan kemudian tampilan saya tidak ditampilkan sama sekali!
teradyl
3

Saya berpegang pada pertanyaan seperti yang dinyatakan judul dan bukan pembahasannya. Tampilan mana yang paling terlihat pada titik tertentu?

@implementation UIView (Extra)

- (UIView *)findTopMostViewForPoint:(CGPoint)point
{
    for(int i = self.subviews.count - 1; i >= 0; i--)
    {
        UIView *subview = [self.subviews objectAtIndex:i];
        if(!subview.hidden && CGRectContainsPoint(subview.frame, point))
        {
            CGPoint pointConverted = [self convertPoint:point toView:subview];
            return [subview findTopMostViewForPoint:pointConverted];
        }
    }

    return self;
}

- (UIWindow *)topmostWindow
{
    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
    return topWindow;
}

@end

Dapat digunakan langsung dengan UIWindow apa pun sebagai penerima atau UIView apa pun sebagai penerima.

hfossli.dll
sumber
topWindow memberikan nilai yang salah dalam kasus iOS 8, Bisakah Anda memperbarui jawaban Anda untuk iOS 8
Mehul Thakkar
1
Saya tidak punya waktu untuk mencari tahu. Jika Anda melakukannya, Anda bebas memperbarui posting ini.
hfossli
1

Jika Anda menambahkan tampilan pemuatan (misalnya, tampilan indikator aktivitas), pastikan Anda memiliki objek kelas UIWindow. Jika Anda memperlihatkan lembar tindakan tepat sebelum Anda memperlihatkan tampilan pemuatan Anda, itu keyWindowakan menjadi UIActionSheetdan tidak UIWindow. Dan karena lembar tindakan akan hilang, tampilan pemuatan juga akan hilang. Atau itulah yang menyebabkan masalah bagi saya.

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
if (![NSStringFromClass([keyWindow class]) isEqualToString:@"UIWindow"]) {
    // find uiwindow in windows
    NSArray *windows = [UIApplication sharedApplication].windows;
    for (UIWindow *window in windows) {
        if ([NSStringFromClass([window class]) isEqualToString:@"UIWindow"]) {
            keyWindow = window;
            break;
        }
    }
}
Miha Hribar
sumber
1

Jika aplikasi Anda hanya bekerja dalam orientasi potret, ini sudah cukup:

[[[UIApplication sharedApplication] keyWindow] addSubview:yourView]

Dan tampilan Anda tidak akan ditampilkan di atas keyboard dan bilah status.

Jika Anda ingin mendapatkan tampilan teratas yang melewati keyboard atau bilah status, atau Anda ingin tampilan paling atas dapat berputar dengan benar dengan perangkat, silakan coba kerangka kerja ini:

https://github.com/HarrisonXi/TopmostView

Ini mendukung iOS7 / 8/9.

Harrison Xi
sumber
0

Cukup gunakan kode ini jika Anda ingin menambahkan tampilan di atas semua yang ada di layar.

[[UIApplication sharedApplication].keyWindow addSubView: yourView];
handiansom
sumber
0

coba ini

UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
Chandramani
sumber
Tentang properti jendela: "Properti ini berisi larik objek NSWindow yang sesuai dengan semua jendela yang ada saat ini untuk aplikasi. Larik tersebut mencakup semua jendela di layar dan di luar layar, baik yang terlihat di ruang mana pun atau tidak. Tidak ada jaminan pesanan jendela dalam larik. "
Steven Fisher
0
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
if (![NSStringFromClass([keyWindow class]) isEqualToString:@"UIWindow"]) {

    NSArray *windows = [UIApplication sharedApplication].windows;
    for (UIWindow *window in windows) {
        if ([NSStringFromClass([window class]) isEqualToString:@"UIWindow"]) {
            keyWindow = window;
            break;
        }
    }
}
pengguna12775146
sumber