presentViewController: animasyonlu: EVET görünümü, kullanıcı tekrar dokunana kadar görünmeyecek


93

İle bazı garip davranışlar alıyorum presentViewController:animated:completion. Yaptığım şey aslında bir tahmin oyunu.

Bir UIViewController(FrequencyTableView) içeren bir UITableView(FrequencyViewController) var. Kullanıcı doğru cevabı içeren söz konusu satıra dokunduğunda, bir görünüm (doğru GörünümController) somutlaştırılmalı ve görünümü modal bir görünüm olarak ekranın altından yukarı kaymalıdır. Bu, kullanıcıya doğru bir cevabı olduğunu söyler ve arkasındaki FrequencyViewController'ı bir sonraki soru için hazır hale getirir. DoğruViewController, bir sonraki soruyu ortaya çıkarmak için bir düğmeye basılarak kapatılır.

Bu, tüm doğru her zaman çalışır ve correctViewController bakış açısı sürece anında görünür presentViewController:animated:completionvardır animated:NO.

Eğer ayarlarsam animated:YES, doğruViewController başlatılır ve viewDidLoad. Ancak viewWillAppear, viewDidAppearve gelen tamamlama bloğu presentViewController:animated:completionçağrılmaz. Uygulama, ikinci bir dokunuş yapana kadar hala FrequencyViewController'ı gösteriyor. Şimdi, viewWillAppear, viewDidAppear ve tamamlama bloğu çağrılır.

Biraz daha araştırdım ve devam etmesine neden olacak sadece başka bir dokunuş değil. Görünüşe göre iPhone'umu yana yatırırsam veya sallarsam, bu da viewWillLoad vb. Tetiklemesine neden olabilir. İlerlemeden önce başka bir kullanıcı girdisini bekliyor gibi. Bu gerçek bir iPhone'da ve simülatöre sallama komutunu göndererek kanıtladığım simülatörde oluyor.

Bu konuda ne yapacağımı bilemiyorum ... Herhangi birinin sağlayabileceği her türlü yardımı gerçekten takdir ediyorum.

Teşekkürler

İşte kodum. Oldukça basit ...

Bu, questionViewController'daki, questionTableView için temsilci görevi gören koddur.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

    if (indexPath.row != [self.frequencyModel currentFrequencyIndex])
    {
        // If guess was wrong, then mark the selection as incorrect
        NSLog(@"Incorrect Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]);
        UITableViewCell *cell = [self.frequencyTableView cellForRowAtIndexPath:indexPath];
        [cell setBackgroundColor:[UIColor colorWithRed:240/255.0f green:110/255.0f blue:103/255.0f alpha:1.0f]];            
    }
    else
    {
        // If guess was correct, show correct view
        NSLog(@"Correct Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]);
        self.correctViewController = [[HFBCorrectViewController alloc] init];
        self.correctViewController.delegate = self;
        [self presentViewController:self.correctViewController animated:YES completion:^(void){
            NSLog(@"Completed Presenting correctViewController");
            [self setUpViewForNextQuestion];
        }];
    }
}

Bu, doğruViewController'ın tamamıdır

@implementation HFBCorrectViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self)
    {
        // Custom initialization
        NSLog(@"[HFBCorrectViewController initWithNibName:bundle:]");
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    NSLog(@"[HFBCorrectViewController viewDidLoad]");
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    NSLog(@"[HFBCorrectViewController viewDidAppear]");
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)close:(id)sender
{
    NSLog(@"[HFBCorrectViewController close:sender:]");
    [self.delegate didDismissCorrectViewController];
}


@end

Düzenle:

Bu soruyu daha önce buldum: UITableView ve presentViewController'ın görüntülenmesi 2 tıklama alıyor

Ve eğer didSelectRowkodumu buna değiştirirsem , animasyonla çok iyi çalışır ... Ama bu dağınık ve ilk başta neden çalışmadığı konusunda bir anlam ifade etmiyor. Bu yüzden bunu bir cevap olarak saymıyorum ...

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

    if (indexPath.row != [self.frequencyModel currentFrequencyIndex])
    {
        // If guess was wrong, then mark the selection as incorrect
        NSLog(@"Incorrect Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]);
        UITableViewCell *cell = [self.frequencyTableView cellForRowAtIndexPath:indexPath];
        [cell setBackgroundColor:[UIColor colorWithRed:240/255.0f green:110/255.0f blue:103/255.0f alpha:1.0f]];
        // [cell setAccessoryType:(UITableViewCellAccessoryType)]

    }
    else
    {
        // If guess was correct, show correct view
        NSLog(@"Correct Guess: %@", [self.frequencyModel frequencyLabelAtIndex:(int)indexPath.row]);

        ////////////////////////////
        // BELOW HERE ARE THE CHANGES
        [self performSelector:@selector(showCorrectViewController:) withObject:nil afterDelay:0];
    }
}

-(void)showCorrectViewController:(id)sender
{
    self.correctViewController = [[HFBCorrectViewController alloc] init];
    self.correctViewController.delegate = self;
    self.correctViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    [self presentViewController:self.correctViewController animated:YES completion:^(void){
        NSLog(@"Completed Presenting correctViewController");
        [self setUpViewForNextQuestion];
    }];
}

1
Bende de aynı sorun var. NSLogs ile yürütmesi uzun süren herhangi bir yöntem çağırmadığımı kontrol ettim. Görünüşe göre presentViewController:tetiklemesi gereken sadece animasyon büyük bir gecikmeyle başlıyor. Bu, iOS 7'de bir hata gibi görünüyor ve ayrıca Apple Dev Forumlarında tartışılıyor.
Theo

Bende de aynı sorun var. Bir iOS7 hatası gibi görünüyor - iOS6'da olmuyor. Ayrıca, benim durumumda yalnızca sunum görünümü denetleyicisini açtıktan sonra ilk kez oluyor. @Theo, Apple Geliştirici Forumları'na bir bağlantı sağlayabilir misiniz?
AX

Yanıtlar:


158

Bugün aynı sorunla karşılaştım. Konuyu araştırdım ve görünen o ki, ana runloop'un uykuda olmasıyla ilgili.

Aslında bu çok ince bir hata çünkü kodunuzda en ufak bir geri bildirim animasyonu, zamanlayıcılar vb. Varsa bu sorun ortaya çıkmayacaktır çünkü runloop bu kaynaklar tarafından canlı tutulacaktır. Sorunu , satır seçim işleyicisi çalıştırıldıktan sonra hiçbir seçim animasyonu çalıştırma döngüsünü tetiklemediği için ayarlanmış UITableViewCellolan a kullanarak buldum .selectionStyleUITableViewCellSelectionStyleNone

Düzeltmek için (Apple bir şey yapana kadar) ana runloop'u birkaç yolla tetikleyebilirsiniz:

En az müdahaleci çözüm CFRunLoopWakeUpşunları aramaktır :

[self presentViewController:vc animated:YES completion:nil];
CFRunLoopWakeUp(CFRunLoopGetCurrent());

Veya boş bir bloğu ana kuyruğa sıralayabilirsiniz:

[self presentViewController:vc animated:YES completion:nil];
dispatch_async(dispatch_get_main_queue(), ^{});

Komik, ancak cihazı sallarsanız, ana döngüyü de tetikler (hareket olaylarını işlemesi gerekir). Dokunmalarla aynı şey, ancak bu orijinal soruya dahil :) Ayrıca, sistem durum çubuğunu güncellerse (örneğin saat güncellemeleri, WiFi sinyal gücü değişiklikleri vb.), Bu da ana döngüyü uyandıracak ve görünümü sunacaktır. denetleyici.

İlgilenen herkes için runloop hipotezini doğrulamak için sorunun minimal bir tanıtım projesi yazdım: https://github.com/tzahola/present-bug

Hatayı Apple'a da bildirdim.


Teşekkürler Tamás. Bu harika bir bilgi. Neden bazen koşu döngüsünü başlatmak için dokunmam gerektiğini veya bazen sadece beklediğimi ve işe yarayacağını açıklıyor. Hata düzeltilene kadar yapabileceğimiz başka bir şey yok gibi göründüğü için bunu çözüm olarak işaretledim.
Yarı Normalleştirildi

Apple harika bir çukur kazıyor, içine düşüyorum ve çok incinmiş hissediyorum.
OpenThread

7
Aman Tanrım, bu çok komik! BTW, bu hata hala var.
igrrik

1
Aynı problem gerçekten can sıkıcıydı, neyse ki bu sorunu çözdü.
Mark

1
Bu sorunun iOS 13'te hala devam ettiğini doğruladı
Eric Horacek

69

Şuna bir göz atın: https://devforums.apple.com/thread/201431 Tümünü okumak istemiyorsanız - bazı insanlar için çözüm (ben de dahil) presentViewControlleraramayı açıkça ana başlıkta yapmaktı :

Swift 4.2:

DispatchQueue.main.async { 
    self.present(myVC, animated: true, completion: nil)
}

Amaç-C:

dispatch_async(dispatch_get_main_queue(), ^{
    [self presentViewController:myVC animated:YES completion:nil];
});

Muhtemelen iOS7, konu başlıklarını karıştırıyor didSelectRowAtIndexPath.


vay - bu benim için çalıştı. Ben de gecikmeleri görüyordum. Bunun neden gerekli olduğunu anlayamıyorum. Bunu gönderdiğiniz için teşekkürler.
drudru

4
Apple ile bu sorun hakkında bir radar doldurdu: rdar: // 19563577 openradar.appspot.com/19563577
mluisbrown

1
İOS 8'deyim ve bu benim için düzeltmiyor - her zaman ana iş parçacığında olduğunu bildiriyor, ancak yine de açıklanan sorunu görüyorum.
Robert

7

Swift 3.0'da aşağıdaki kodu kullanarak atladım:

DispatchQueue.main.async { 
    self.present(UIViewController(), animated: true, completion: nil)
}

1
sorunu çözer. Aynı sorunu yaşadım çünkü presentkapatma geri aramasında arıyordum .
Jeremy Piednoel

2

[viewController view]Sunulan görünüm denetleyicisini aramak benim için hile yaptı.


Bu benim için iOS 8'de çalıştı. Bunun dispatch_async'den daha iyi olduğunu düşünüyorum.
Robert

0

Ne işe yaradığını merak ediyorum [self setUpViewForNextQuestion];.

[self.correctViewController.view setNeedsDisplay];Tamamlama bloğunuzun sonunda aramayı deneyebilirsiniz presentViewController.


Şu anda, setUpViewForNextQuestion yalnızca tablo görünümünde reloadData öğesini çağırır (herhangi biri yanlış bir tahminle kırmızı olarak ayarlanmışsa tüm arka plan renklerini beyaza döndürmek için). Ama oradaki herhangi bir değişiklik bir fark yaratır mı? Sorun, doğruViewController'ın kullanım tekrar dokunana kadar görünmemesidir, bu nedenle bu tamamlama bloğu ikinci dokunuş sonrasına kadar çağrılmaz.
YarıNormalled

Önerinizi denedim, ama hiçbir şeyi değiştirmedi. Dediğim gibi, görünüm görünmüyor, bu nedenle tamamlama bloğu ikinci dokunuşa veya sallama hareketine kadar çağrılmaz.
HalfNormalled

hmm. yeni başlayanlar için, sunum kodunuzun 2. dokunuşa kadar çağrılmadığını onaylamak için kesme noktaları kullanırdım. (ilk dokunuşta çağrılmaya karşı, ancak daha sonra ekrana çizilmeme). ikincisi doğruysa, sunumunuzu ana başlıkta gerçekleşmeye zorlamayı deneyin. "PerformSelectorWithDelay: 0" kullandığınızda, uygulamayı çalıştırmadan önce çalıştırma döngüsünün bir sonraki yinelemesine kadar beklemeye zorlar. İş parçacığı ile ilgili olabilir.
Nick

Teşekkürler Nick. Kesme noktalarını kullanarak nasıl kontrol ederim? Şu anda her yöntemin ne zaman çağrıldığını kontrol etmek için NSLog kullanıyorum. Şu anda sahip olduğum Correct Guess: 1 kHz 18:04:57.173 Frequency[641:60b] [HFBCorrectViewController initWithNibName:bundle:] 18:04:57.177 Frequency[641:60b] [HFBCorrectViewController viewDidLoad] şey bu : 1'e dokunun Şimdi şu tarihe kadar hiçbir şey olmuyor: 2'ye dokunun18:05:00.515 Frequency[641:60b] [HFBCorrectViewController viewDidAppear] 18:05:00.516 Frequency[641:60b] Completed Presenting correctViewController 18:05:00.517 Frequency[641:60b] [HFBFrequencyViewController setUpViewForNextQuestion]
Yarı Normalleştirilmiş

Üzgünüm, daha açık olmalıydı. Kesme noktalarının nasıl kullanılacağını anlıyorum. NSLog ile aynı hikayeyi alıyorum. ViewDidLoad ve viewDidAppear üzerine kesme noktaları koydum doğruViewController. ViewDidLoad'da kırılıyor, sonra devam ettiğimde oturuyor ve başka bir dokunuş için bekler, sonra viewDidAppear'da kesiliyor. Bazen bir dokunmaya ihtiyaç duymuyor gibi görünüyor ve zamana bağlı, başka ne adlandırıldığına bakarak şeylere adım attığımda, viewDidLoad'u çağırmaya başladı.
YarıNormalled

0

Sorunu çözen UIViewController için yöntem değiştirerek uzantı (kategori) yazdım. Uygulama ipuçları için AX ve NSHipster'a teşekkürler ( hızlı / hedef-c ).

Swift

extension UIViewController {

 override public class func initialize() {
    struct DispatchToken {
      static var token: dispatch_once_t = 0
    }
    if self != UIViewController.self {
      return
    }
    dispatch_once(&DispatchToken.token) {
      let originalSelector = Selector("presentViewController:animated:completion:")
      let swizzledSelector = Selector("wrappedPresentViewController:animated:completion:")

      let originalMethod = class_getInstanceMethod(self, originalSelector)
      let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)

      let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))

      if didAddMethod {
        class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
      }
      else {
        method_exchangeImplementations(originalMethod, swizzledMethod)
      }
    }
  }

  func wrappedPresentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) {
    dispatch_async(dispatch_get_main_queue()) {
      self.wrappedPresentViewController(viewControllerToPresent, animated: flag, completion: completion)
    }
  }
}  

Amaç-C

#import <objc/runtime.h>

@implementation UIViewController (Swizzling)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(presentViewController:animated:completion:);
        SEL swizzledSelector = @selector(wrappedPresentViewController:animated:completion:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod =
            class_addMethod(class,
                originalSelector,
                method_getImplementation(swizzledMethod),
                method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class,
                swizzledSelector,
                method_getImplementation(originalMethod),
                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

- (void)wrappedPresentViewController:(UIViewController *)viewControllerToPresent 
                             animated:(BOOL)flag 
                           completion:(void (^ __nullable)(void))completion {
    dispatch_async(dispatch_get_main_queue(),^{
        [self wrappedPresentViewController:viewControllerToPresent
                                  animated:flag 
                                completion:completion];
    });

}

@end

0

Film şeridindeki hücrenizde Seçim = yok olup olmadığını kontrol edin

Öyleyse, mavi veya gri olarak değiştirin ve çalışmalıdır


0

XCode Vesion: 9.4.1, Swift 4.1

Benim durumumda bu, hücreye dokunduğumda ve başka bir görünüm için hareket ettiğimde oluyor. Daha derine hata ayıkladım ve viewDidAppearaşağıdaki kodu içerdiği için içeride oluyor gibi görünüyor

if let indexPath = tableView.indexPathForSelectedRow {
   tableView.deselectRow(at: indexPath, animated: true)
}

sonra yukarıdaki kod segmentini içine ekledim prepare(for segue: UIStoryboardSegue, sender: Any?)ve mükemmel çalışıyor.

Deneyimlerime göre, çözümüm şudur: İkinci görünümden geri döndüğünüzde tablo görünümü için herhangi bir yeni değişiklik yapmayı umarsak (örneğin, tablo yeniden yükleme, seçilen hücrenin seçimini kaldırma vb.), O zaman viewDidAppearyukarıdaki tableView.deselectRowkod yerine temsilci kullanın. ikinci görünüm denetleyicisini taşımadan önce segment

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.