wake-up-neo.com

presentViewController: animated: Die YES-Ansicht wird erst angezeigt, wenn der Benutzer erneut tippt

Ich bekomme ein seltsames Verhalten mit presentViewController:animated:completion. Was ich mache, ist im Wesentlichen ein Ratespiel. 

Ich habe eine UIViewController (frequencyViewController), die eine UITableView (frequencyTableView) enthält. Wenn der Benutzer auf die Zeile in questionTableView tippt, die die richtige Antwort enthält, sollte eine Ansicht (correctViewController) instanziiert werden und ihre Ansicht sollte als modale Ansicht vom unteren Rand des Bildschirms nach oben gleiten. Dies teilt dem Benutzer mit, dass er eine korrekte Antwort hat, und setzt den frequencyViewController dahinter für die nächste Frage zurück. Bei einem Knopfdruck wird correctViewController verworfen, um die nächste Frage anzuzeigen.

Dies funktioniert jedes Mal korrekt, und die Ansicht des correctViewControllers wird sofort angezeigt, solange presentViewController:animated:completionanimated:NO hat.

Wenn ich animated:YES setze, wird correctViewController initialisiert und ruft viewDidLoad auf. viewWillAppear, viewDidAppear und der Beendigungsblock von presentViewController:animated:completion werden jedoch nicht aufgerufen. Die App sitzt nur noch dort und zeigt FrequencyViewController, bis ich einen zweiten Tipp mache. Jetzt werden viewWillAppear, viewDidAppear und der Beendigungsblock aufgerufen.

Ich habe ein bisschen mehr nachgeforscht und es ist nicht nur ein weiterer Tipp, der dazu führt, dass es weitergeht. Es scheint, wenn ich mein iPhone neige oder schüttle, kann dies auch dazu führen, dass es viewWillLoad usw. auslöst. Es ist, als würde es auf andere Benutzereingaben warten, bevor es weitergeht. Dies geschieht auf einem echten iPhone und im Simulator, was ich durch Senden des Shake-Befehls an den Simulator bewiesen habe.

Ich bin wirklich ratlos, was ich dagegen tun soll ... Ich würde mich über jede Hilfe freuen, die jemand geben kann.

Vielen Dank

Hier ist mein Code. Es ist ziemlich einfach ...

Dies ist der Code in questionViewController, der als Delegat für die questionTableView fungiert

- (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];
        }];
    }
}

Dies ist die Gesamtheit des correctViewControllers

@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

Bearbeiten:

Ich habe diese Frage zuvor gefunden: UITableView und presentViewController benötigt 2 Klicks zur Anzeige

Und wenn ich meinen didSelectRow-Code in diesen Code ändere, funktioniert das sehr zeitaufwändig mit der Animation ... Aber es ist chaotisch und macht keinen Sinn, warum es überhaupt nicht funktioniert. Also ich zähle das nicht als Antwort ...

- (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];
    }];
}
85
HalfNormalled

Ich habe heute das gleiche Problem gesehen. Ich habe mich in das Thema eingegraben und es scheint, dass es damit zusammenhängt, dass die Hauptschleife schläft. 

Es ist eigentlich ein sehr subtiler Fehler, denn wenn Sie die geringste Feedback-Animation, Timer usw. in Ihrem Code haben, wird dieses Problem nicht auftauchen, da der Runloop durch diese Quellen am Leben erhalten wird. Ich habe das Problem gefunden, indem ich eine UITableViewCell verwendet habe, deren selectionStyle auf UITableViewCellSelectionStyleNone gesetzt war, so dass keine Auswahlanimation den Runloop auslöste, nachdem der Zeilenauswahlhandler ausgeführt wurde. 

Um das Problem zu beheben (bis Apple etwas tut), können Sie den Haupt-Runloop auf verschiedene Arten auslösen:

Die am wenigsten aufdringliche Lösung ist der Aufruf von CFRunLoopWakeUp:

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

Oder Sie können einen leeren Block in die Hauptwarteschlange einreihen:

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

Es ist lustig, aber wenn Sie das Gerät schütteln, wird auch die Hauptschleife ausgelöst (die Bewegungsereignisse müssen verarbeitet werden). Das Gleiche gilt für Taps, aber das ist in der ursprünglichen Frage enthalten :) Auch wenn das System die Statusleiste aktualisiert (z. B. die Aktualisierung der Uhr, die WLAN-Signalstärke usw.), wird auch die Hauptschleife aktiviert und die Ansicht angezeigt Regler. 

Für alle Interessierten schrieb ich ein minimales Demonstrationsprojekt des Problems, um die Runloop-Hypothese zu überprüfen: https://github.com/tzahola/present-bug

Ich habe den Fehler auch an Apple gemeldet. 

143
Tamás Zahola

Schauen Sie sich dies an: https://devforums.Apple.com/thread/201431 .__ Wenn Sie nicht alles lesen möchten, war die Lösung für einige Personen (einschließlich mir) der Aufruf der Variable presentViewController explizit im Hauptthread:

Swift 4.2:

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

Ziel c:

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

Wahrscheinlich verwirrt iOS7 die Threads in didSelectRowAtIndexPath.

64
AXE

Ich habe es in Swift 3.0 mit dem folgenden Code umgangen:

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

Das Aufrufen von [viewController view] auf dem dargelegten View-Controller hat den Trick für mich erledigt.

2
JaganY

Ich habe die Erweiterung (Kategorie) mit der Methode swizzling für UIViewController geschrieben, die das Problem löst. Vielen Dank an AX und NSHipster für Implementierungshinweise ( Swift / object-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)
    }
  }
}  

Ziel 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
Gladkov_Art

XCode Vesion: 9.4.1, Swift 4.1

In meinem Fall geschieht dies, wenn ich auf Zelle tippe und mich für die andere Ansicht bewege. Ich debugge in die Tiefe und es scheint, dass innerhalb von viewDidAppear wegen folgenden Code passiert

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

dann fügte ich oben Code-Segment in prepare(for segue: UIStoryboardSegue, sender: Any?) hinzu und arbeite perfekt.

Nach meiner Erfahrung lautet meine Lösung: Wenn wir hoffen, dass wir neue Änderungen (z. B. Neu laden der Tabelle, Deaktivieren der ausgewählten Zelle usw.) für die Tabellenansicht vornehmen, wenn wieder aus der zweiten Ansicht zurückgekehrt wird, dann Delegate anstelle von viewDidAppear verwenden und oben verwenden tableView.deselectRow Codesegment vor dem Verschieben des zweiten View-Controllers

0
Sachintha Udara

Ich bin gespannt, was [self setUpViewForNextQuestion]; macht. 

Sie könnten versuchen, [self.correctViewController.view setNeedsDisplay]; am Ende Ihres Beendigungsblocks in presentViewController aufzurufen.

0
Nick

Prüfen Sie, ob Ihre Zelle im Storyboard Auswahl = Keine hat

Ändern Sie in diesem Fall Blau oder Grau, und es sollte funktionieren

0
Mkey