Pertanyaan In Call Status Bar (Tidak Dapat Memuaskan Batasan)


Sama seperti pertanyaan ini: Tata letak otomatis dan bilah status dalam panggilan dan pertanyaan ini: Ubah ukuran untuk bilah status dalam panggilan?, Saya mengalami masalah dengan In Call Status Bar yang mengacaukan tata letak tampilan saya.

Inilah struktur bersarang saya. Saya memiliki Custom Modal ViewController yang bersarang dalam ViewController lain. Setiap kali In Call Status Bar ditampilkan (dan kemudian ditutup keluar), inilah yang terjadi:

enter image description here

Berikut ini gambar apa yang seharusnya terlihat sebelum Bilah Status Panggilan Masuk ditampilkan:

enter image description here

Latar belakang warna biru dari bar status setelah bug terjadi adalah warna latar belakang pengendali tampilan root.

Setiap kali Bilah Status Panggilan Masuk ditampilkan, kesalahan berikut ini dicetak:

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7fdac6192320 V:|-(20)-[UIInputSetContainerView:0x7fdac6190a40]   (Names: '|':UITextEffectsWindow:0x7fdac6061a10 )>",
    "<NSLayoutConstraint:0x7fdac608ebb0 'UIInputWindowController-top' V:|-(0)-[UIInputSetContainerView:0x7fdac6190a40]   (Names: '|':UITextEffectsWindow:0x7fdac6061a10 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fdac6192320 V:|-(20)-[UIInputSetContainerView:0x7fdac6190a40]   (Names: '|':UITextEffectsWindow:0x7fdac6061a10 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7fc60b03d230 V:|-(20)-[UIInputSetContainerView:0x7fc608d22020]   (Names: '|':UITextEffectsWindow:0x7fc60b171720 )>",
    "<NSLayoutConstraint:0x7fc60b03d2d0 UIInputSetContainerView:0x7fc608d22020.bottom == UITextEffectsWindow:0x7fc60b171720.bottom>",
    "<NSLayoutConstraint:0x7fc60b17c4b0 'UIInputWindowController-height' UIInputSetContainerView:0x7fc608d22020.height == UITextEffectsWindow:0x7fc60b171720.height>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fc60b03d2d0 UIInputSetContainerView:0x7fc608d22020.bottom == UITextEffectsWindow:0x7fc60b171720.bottom>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

Menggunakan alat debugging FLEX saya bisa melihatnya

UINavigationBarBackground dan UIStatusBarForegroundView tumpang tindih sebelum In Call Status Bar, bagaimanapun setelahnya UINavigationBarBackground di bawah UIStatusBarForegroundView.

Bug ini hanya terjadi SETELAH saya menyajikan Modal View Controller. Jika saya menampilkan In Call Status Bar, maka masalah tidak terjadi. Anda tidak dapat kembali ke pengontrol tampilan akar setelah Kontroler Tampilan Modal ditampilkan.

Apa yang bisa saya lakukan untuk memperbaiki ini?


32
2017-10-13 21:15


asal


Jawaban:


iOS 9.2.1, Xcode 7.2.1, ARC diaktifkan

UPDATE 3/25/2016: Konflik masih ada di Xcode 7.3, iOS 9.3.

Ringkasan: Dalam hierarki jendela untuk aplikasi Anda ada berbagai jendela yang ditambahkan ke jendela aplikasi. Dalam kasus saya, ini adalah UITextEffectsWindow dan UIRemoteKeyboardWindow. Jendela-jendela ini datang dengan batasan yang sudah dikonfigurasikan. Tampaknya ada bug yang memperbarui beberapa kendala tata letak vertikal, tetapi tidak kendala terkait lainnya untuk jendela yang sama. Ini melempar konflik kendala dalam debugger. Ini terjadi ketika jendela khusus ditambahkan ke hierarki jendela atau ketika bilah status dalam panggilan masuk ke atau keluar, pada simulator dan perangkat iOS yang sebenarnya.

Kendalanya adalah prioritas 1000, ini menunjukkan bahwa mereka membutuhkan batasan.

Solusi di bawah ini akan menghapus kendala yang bertentangan dan menambahkannya kembali setelah bilah status di-panggilan dilepas.

EDIT 2/25/2016:  Tidak ada solusi yang memecahkan masalah memiliki bilah status dalam panggilan yang sudah ditampilkan saat membuka aplikasi. Konflik terjadi sebelum perubahan status bar didaftarkan.

Tampaknya konflik kendala ini hanya terjadi saat pertama kali bilah status dalam panggilan ditampilkan (ini paling umum), atau dalam skenario lain yang melibatkan penyajian tambahan jendela khusus yang akan berada di atas jendela kunci. Saya juga mencoba hanya membuat aplikasi tampilan kosong tunggal dan dengan menambahkan tidak ada toggling di bar status dalam panggilan, dan saya mendapat konflik kendala yang sama.

Saya pikir ini adalah bug dan dibahas di forum Dev. Artikel asli dari forum Dev dapat ditemukan di sini (seperti yang ditunjukkan oleh Matty):

https://forums.developer.apple.com/thread/16375

Saya ingin memperluas sedikit jawaban matty di sini. Yang menurut saya sangat membantu. Saya tidak yakin apa dampak penghapusan "semua" kendala yang akan menyebabkan, jadi saya menghapus hanya kendala yang bertentangan. Alasan saya adalah bahwa kendala yang saling bertentangan akan rusak pula sebagai informasi debugger "Akan berusaha memulihkan dengan melanggar batasan"; jadi kendala itu tidak akan berguna juga.

Berikut adalah kesalahan konflik kendala yang saya terima:

enter image description here

enter image description here

Setelah menafsirkan kesalahan kendala (lihat dokumen apel ini untuk membantu dengan itu: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/DebuggingTricksandTips.html) Saya menggunakan kode berikut untuk menyingkirkan kendala yang bertentangan:

Objective-C:

* AppDelegate.h

...

@interface YourAppName : UIResponder <UIApplicationDelegate>
{
    NSMutableDictionary *dictionaryConstraints;
}

...

* AppDelegate.m

...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.

    dictionaryConstraints = [[NSMutableDictionary alloc] init];

    return true;

}

- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame
{
   NSLog(@"newStatusBarFrame: %@", NSStringFromCGRect(newStatusBarFrame));

   if (newStatusBarFrame.size.height > 20.0)
   {
        for (UIWindow *window in [[UIApplication sharedApplication] windows])
        {
            if ([window.class.description isEqual:@"UITextEffectsWindow"] || [window.class.description isEqual:@"UIRemoteKeyboardWindow"])
            {
                NSMutableArray *constraints = [[NSMutableArray alloc] initWithCapacity:[window.constraints count]];

                for (NSLayoutConstraint *constraint in window.constraints)
                {
                    if (!([constraint.description rangeOfString:@"V:|-(0)-[UIInputSetContainerView"].location == NSNotFound))
                    {
                        NSLog(@"");
                        NSLog(@"%@: %@, %f, %f", window.class.description, constraint.description, constraint.priority, constraint.constant);
                        NSLog(@"");

                        [constraints addObject:constraint];
                        [window removeConstraint:constraint];
                    }
                    else
                    {
                        nil;
                    }
                }

                if ([constraints count] > 0)
                {
                    [dictionaryConstraints setObject:constraints forKey:[NSString stringWithFormat:@"%p", window]];
                }
                else
                {
                    nil;
                }
            }
            else
            {
                nil;
            }
        }
    }
    else
    {
        nil;
    }
}

- (void)resetConstraints
{
    for (UIWindow *window in [[UIApplication sharedApplication] windows])
    {
        if ([window.class.description isEqual:@"UITextEffectsWindow"] || [window.class.description isEqual:@"UIRemoteKeyboardWindow"])
        {
            if (dictionaryConstraints)
            {
                NSArray *keys = [dictionaryConstraints allKeys];

                for (int i = 0; i < [keys count]; i++)
                {
                    if ([[NSString stringWithFormat:@"%p", window] isEqualToString:keys[i]])
                    {
                        [window addConstraints:[dictionaryConstraints objectForKey:keys[i]]];
                    }
                    else
                    {
                        nil;
                    }
                }
            }
            else
            {
                nil;
            }
        }
        else
        {
            nil;
        }
    }
}

- (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame
{
    NSLog(@"oldStatusBarFrame: %@", NSStringFromCGRect(oldStatusBarFrame));

    if (oldStatusBarFrame.size.height > 20.0)
    {
        if ([dictionaryConstraints count] > 0)
        {
            [self resetConstraints];
            [dictionaryConstraints removeAllObjects];
        }
        else
        {
            nil;
        }
    }
    else
    {
        nil;
    }

    for (UIWindow *window in [[UIApplication sharedApplication] windows])
    {
        if ([window.class.description isEqual:@"UITextEffectsWindow"] || [window.class.description isEqual:@"UIRemoteKeyboardWindow"])
        {
            for (NSLayoutConstraint *constraint in window.constraints)
            {
                if (!([constraint.description rangeOfString:@"V:|-(0)-[UIInputSetContainerView"].location == NSNotFound))
                {
                    NSLog(@"");
                    NSLog(@"%@: %@, %f, %f", window.class.description, constraint.description, constraint.priority, constraint.constant);
                    NSLog(@"");
                }
                else
                {
                    nil;
                }

            }
        }
        else
        {
            nil;
        }
    }
}

...

Cepat:

* AppDelegate.swift

...

var dictionaryConstraints = [NSString : NSArray]();

...

func application(application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect)
{
    print("newStatusBarFrame: \(newStatusBarFrame)")

    if newStatusBarFrame.size.height > 20.0
    {
        for window in UIApplication.sharedApplication().windows
        {
            if ((window.classForCoder.description() == "UITextEffectsWindow") || (window.classForCoder.description() == "UIRemoteKeyboardWindow"))
            {
                var constraints = [NSLayoutConstraint]()

                for constraint in window.constraints
                {
                    if (constraint.description.containsString("V:|-(0)-[UIInputSetContainerView"))
                    {
                        print("\(window.classForCoder.debugDescription), \(constraint.description), \(constraint.priority), \(constraint.constant)")

                        constraints.append(constraint)
                        window.removeConstraint(constraint)
                    }
                    else
                    {
                        //nil
                    }
                }

                if (constraints.count > 0)
                {
                    dictionaryConstraints[NSString(format: "%p", unsafeAddressOf(window))] = constraints
                }
                else
                {
                    //nil
                }
            }
            else
            {
                //nil
            }
        }
    }
    else
    {
        //nil
    }
}

func resetConstraints()
{
    for window in UIApplication.sharedApplication().windows
    {
        if ((window.classForCoder.description() == "UITextEffectsWindow") || (window.classForCoder.description() == "UIRemoteKeyboardWindow"))
        {
            if (dictionaryConstraints.count > 0)
            {
                let keys = Array(dictionaryConstraints.keys)

                for i in 0 ..< keys.count
                {
                    if (NSString(format: "%p", unsafeAddressOf(window)) == keys[i])
                    {
                        window.addConstraints(dictionaryConstraints[keys[i]] as! [NSLayoutConstraint])
                    }
                    else
                    {
                        //nil
                    }
                }
            }
            else
            {
                //nil
            }
        }
        else
        {
            //nil
        }
    }
}

func application(application: UIApplication, didChangeStatusBarFrame oldStatusBarFrame: CGRect)
{
    print("oldStatusBarFrame: \(oldStatusBarFrame)")

    if (oldStatusBarFrame.size.height > 20.0)
    {
        if (dictionaryConstraints.count > 0)
        {
            self.resetConstraints()
            dictionaryConstraints.removeAll()
        }
        else
        {
            //nil
        }
    }
    else
    {
        //nil
    }

    for window in UIApplication.sharedApplication().windows
    {
        if ((window.classForCoder.description() == "UITextEffectsWindow") || (window.classForCoder.description() == "UIRemoteKeyboardWindow"))
        {
            for constraint in window.constraints
            {
                if (constraint.description.containsString("V:|-(0)-[UIInputSetContainerView"))
                {
                    print("\(window.classForCoder.debugDescription), \(constraint.description), \(constraint.priority), \(constraint.constant)")
                }
                else
                {
                    //nil
                }
            }
        }
        else
        {
            //nil
        }
    }
}

...

Catatan: Ini menyimpan semua batasan yang tidak bertentangan. Dan menambahkan kendala yang bertentangan dihapus kembali setelah situasi yang bertentangan tidak lebih, yaitu bar status di-panggilan toggled.

UPDATE 2/25/2015:  Pengujian lebih lanjut ...

Saya memutuskan untuk mengisolasi perubahan kendala menggunakan dua metode dalam aplikasi. delegasikan file * .m:

...

- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame
{
    NSLog(@"New status bar frame: %@", NSStringFromCGRect(newStatusBarFrame));

    for (UIWindow *window in [[UIApplication sharedApplication] windows])
    {
        NSLog(@"%@, %@", window.description, window.constraints);
    }
}

- (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame
{
    NSLog(@"Old status bar frame: %@", NSStringFromCGRect(oldStatusBarFrame));

    for (UIWindow *window in [[UIApplication sharedApplication] windows])
    {
        NSLog(@"%@, %@", window.description, window.constraints);
    }
}

...

Ketika bilah status di-panggil berganti, batasan yang bertentangan berubah dari:

UITextEffectsWindow:

<UITextEffectsWindow: 0x7fbf994cc810; frame = (0 0; 320 568); buram   = TIDAK; autoresize = W + H; layer = <UIWindowLayer: 0x7fbf994c8470>>,

("<NSLayoutConstraint: 0x7fbf99667eb0 V: | - (0) - [UIInputSetContainerView: 0x7fbf99668ce0] (Nama:   '|': UITextEffectsWindow: 0x7fbf994cc810)> ",

...dihilangkan

"<NSLayoutConstraint: 0x7fbf9966c800 'UIInputWindowController-top' V: | - (0) - [UIInputSetContainerView: 0x7fbf99668ce0] (Nama:   '|': UITextEffectsWindow: 0x7fbf994cc810)> ",

...dihilangkan )

UIRemoteKeyboardWindow:

<UIRemoteKeyboardWindow: 0x7fbf994ceb80; frame = (0 0; 320 568);   buram = TIDAK; autoresize = W + H; layer = <UIWindowLayer: 0x7fbf994cf190>>,

("<NSLayoutConstraint: 0x7fbf994cfb20 V: | - (0) - [UIInputSetContainerView: 0x7fbf99744ec0] (Nama:   '|': UIRemoteKeyboardWindow: 0x7fbf994ceb80)> ",

...dihilangkan

"<NSLayoutConstraint: 0x7fbf9966c800 'UIInputWindowController-top' V: | - (0) - [UIInputSetContainerView: 0x7fbf99668ce0] (Nama: '|': UITextEffectsWindow: 0x7fbf994cc810)>",

...dihilangkan )

... dan berubah menjadi:

UITextEffectsWindow:

<UITextEffectsWindow: 0x7fbf994cc810; frame = (0 0; 320 568); buram   = TIDAK; autoresize = W + H; layer = <UIWindowLayer: 0x7fbf994c8470>>,

("<NSLayoutConstraint: 0x7fbf99667eb0 V: | - (20) - [UIInputSetContainerView: 0x7fbf99668ce0] (Nama:   '|': UITextEffectsWindow: 0x7fbf994cc810)> ",

...dihilangkan

"<NSLayoutConstraint: 0x7fbf9966c800 'UIInputWindowController-top' V: | - (0) - [UIInputSetContainerView: 0x7fbf99668ce0] (Nama:   '|': UITextEffectsWindow: 0x7fbf994cc810)> ",

...dihilangkan )

UIRemoteKeyboardWindow:

<UIRemoteKeyboardWindow: 0x7fbf994ceb80; frame = (0 0; 320 568);   buram = TIDAK; autoresize = W + H; layer = <UIWindowLayer: 0x7fbf994cf190>>,

("<NSLayoutConstraint: 0x7fbf994cfb20 V: | - (20) - [UIInputSetContainerView: 0x7fbf99744ec0] (Nama:   '|': UIRemoteKeyboardWindow: 0x7fbf994ceb80)> ",

...dihilangkan

"<NSLayoutConstraint: 0x7fbf9966c800 'UIInputWindowController-top' V: | - (0) - [UIInputSetContainerView: 0x7fbf99668ce0] (Nama: '|': UITextEffectsWindow: 0x7fbf994cc810)>",

...dihilangkan )

Untuk memahami bahasa pemformatan visual yang dibaca https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html

Tampaknya bahwa kendala tata letak vertikal dalam format "Vertical Layout V: [topField] -XX- [bottomField]" berubah dari ...

NSLayoutConstraint: 0x7fbf99667eb0   V: | - (0) - [UIInputSetContainerView: 0x7fbf99668ce0]

untuk...

NSLayoutConstraint: 0x7fbf99667eb0   V: | - (20) - [UIInputSetContainerView: 0x7fbf99668ce0]

... untuk kedua jendela: UITextEffectsWindow dan UIRemoteKeyboardWindow; Namun, ...

NSLayoutConstraint: 0x7fbf9966c800 'UIInputWindowController-top'   V: | - (0) - [UIInputSetContainerView: 0x7fbf99668ce0]

...tidak.

Jadi dari apa yang dapat saya simpulkan, jendela menyesuaikan batasannya untuk memperhitungkan bilah status dalam panggilan yang ditambahkan, tetapi UIInputWindowController tidak. Oleh karena itu, timbul konflik kendala.

Tapi alurnya semakin tebal ...

Setelah konflik kendala awal yang dihasilkan dari toggling status to-call dalam-panggilan, kendala tidak berubah, dan prioritasnya sama, tetapi konflik kendala tidak muncul dengan toggling lebih lanjut dari bilah status dalam panggilan masuk atau keluar. Tapi, ini hanya karena konflik awal sudah terlempar.

Semoga ini membantu! Tepuk tangan.

Terima kasih kepada semua kontributor asli.


12
2018-02-24 17:35



Versi Swift dari jawaban @matty:

func application(application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect) {
    for window in UIApplication.sharedApplication().windows {
        if window.dynamicType.self.description().containsString("UITextEffectsWindow") {
            window.removeConstraints(window.constraints)
        }
    }
}

9
2017-12-20 13:16



Masalah serupa juga dapat ditemukan di sini: https://forums.developer.apple.com/thread/20632

Saya mencoba solusi yang disarankan dari topik tersebut dengan menerapkan cuplikan di bawah di AppDelegate saya. Tampaknya untuk menyingkirkan kesalahan kendala tetapi saya sangat enggan untuk merilis aplikasi dengan ini karena pasti Apple Bug.

Langkah-langkah untuk mereproduksi:

  1. Buat aplikasi tampilan tunggal baru di Xcode 7.1
  2. Jalankan aplikasi dalam simulator iOS 9.1
  3. Alihkan bar di-panggilan (CMD + Y)
  4. Anda akan melihat 'Tidak dapat secara bersamaan memenuhi batasan.' kesalahan.

Untuk menyingkirkan kesalahan kendala:

- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame {
    for(UIWindow *window in [[UIApplication sharedApplication] windows])
    {
        if([window.class.description isEqual:@"UITextEffectsWindow"])
        {
            [window removeConstraints:window.constraints];
        }  
    }
}

Secara pribadi saya hanya menggunakan cuplikan di atas untuk memastikan kesalahan kendala tidak menyebabkan masalah kendala lain yang saya alami yaitu: layar hitam saat aplikasi diluncurkan di latar belakang. Ternyata bukan itu masalahnya.


3
2017-10-29 10:15



Semua di atas sepertinya bukan solusi sederhana. Saya mengalami masalah yang sama ketika saya menambahkan subview di viewDidLoad. Saya membulatkannya dengan memindahkan kode ke viewDidAppear. FYI.


0
2018-05-29 04:16