willMoveToWindow is called twice
Asked Answered
C

2

4

I'm swizzling willMoveToWindow: and I came across an issue where it was being called twice on views.

When a new view controller is pushed onto a UINavigationController,

willMoveToWindow: is called on the existing view with nil value (Makes sense since the view is moving offscreen)

After that time, the method willMoveToWindow: is called again , but now with with the original window.

My initial thought is that swizzling and calling the window property before the original method has kicked in.

To be on the safe side I've created a small sample project and confirmed the the same behavior.

Basically I need a way to know for sure that the view is not on the window (Because I am triggering logic when a view is moving to window which in reality shouldn't be run (at least not twice))

For reference the problem can be reproduced with the following code:

  @implementation RandomView

    -(void)willMoveToWindow:(UIWindow *)newWindow {
    // when the new view controller is pushed - 
    //the method is called twice on the existing view (on the screen view)- 
    //first time will be called with nil - 
    //second time with the original window
        NSLog(@"********%s <RandomView %p> <Window %p>",__PRETTY_FUNCTION__,self,newWindow);
    }

    -(void)didMoveToWindow {
        NSLog(@"********%s <RandomView %p> <Window %p>",__PRETTY_FUNCTION__,self,self.window);
    }
    @end

    @implementation ViewController

    - (void)viewDidLoad {
        [super viewDidLoad];
        RandomView *k = [[RandomView alloc] initWithFrame:self.view.bounds];
        [self.view addSubview:k];
    }


    -(void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                ViewController *vc = [[ViewController alloc] init];
                [self.navigationController pushViewController:vc animated:YES];
            });
        });

        //[self becomeFirstResponder];
    }
    @end

EDIT Console

    [RandomView willMoveToWindow:] <RandomView 0x7f8b21e16630> <Window 0x7f8b21d220d0>
    [RandomView didMoveToWindow] <RandomView 0x7f8b21e16630> <Window 0x7f8b21d220d0>
  //THIS IS THE ISSUE
    [RandomView willMoveToWindow:] <RandomView 0x7f8b21e16630> <Window 0x0>
    [RandomView didMoveToWindow] <RandomView 0x7f8b21e16630> <Window 0x0>
    [RandomView willMoveToWindow:] <RandomView 0x7f8b21e16630> <Window 0x7f8b21d220d0>
    [RandomView didMoveToWindow] <RandomView 0x7f8b21e16630> <Window 0x7f8b21d220d0>

    [RandomView willMoveToWindow:] <RandomView 0x7f8b21e16630> <Window 0x0>
    [RandomView didMoveToWindow] <RandomView 0x7f8b21e16630> <Window 0x0>
Castle answered 27/12, 2015 at 20:29 Comment(6)
Can't you check the view.window? Why do you need to swizzle? Swizzling is bad.Amuck
thats not relevant - it was just a side note. The issue is that it is called twice -Castle
It makes sense to me. 1. ViewDidLoad adds sub views 2. View is added to window. With that chain of commands you must get two calls of willMoveToWindow. You may want to use some flag or something to register relevant events.Amuck
its the same instance - not a different instance: (The one that was already on screen) It is being moved off screen and the willMoveToWindow: is called twice. Once with nil and then with the original windowCastle
[RandomView willMoveToWindow:] <RandomView 0x7f8b21e16630> <Window 0x0> [RandomView didMoveToWindow] <RandomView 0x7f8b21e16630> <Window 0x0> [RandomView willMoveToWindow:] <RandomView 0x7f8b21e16630> <Window 0x7f8b21d220d0> [RandomView didMoveToWindow] <RandomView 0x7f8b21e16630> <Window 0x7f8b21d220d0> [RandomView willMoveToWindow:] <RandomView 0x7f8b21e16630> <Window 0x0> [RandomView didMoveToWindow] <RandomView 0x7f8b21e16630> <Window 0x0>Castle
Maybe this is how navigation controller animations work? Try using navigation controller delegate or something more reliable. Also willMoveToWindow may be called when you switch between apps. So I would really avoid using it for any kind of logicAmuck
U
0

I also facing with the same problem

but after lookup the view hierarchy through responder chain, there is a small different can check. I am not sure this is safe or not.

but I think Apple did the animation code in wrong order, if they add the animation view to window first, the willMoveToWindow: will not call twice.

2017-03-08 22:49:35.167 view[36189:410065] show
0x7fa9c36059f0,MyView
0x7fa9c3407cb0,UIView
0x7fa9c340b9d0,ViewController
0x7fa9c3403c50,UIViewControllerWrapperView
0x7fa9c340ee90,UINavigationTransitionView
0x7fa9c5802d10,UILayoutContainerView
0x7fa9c381ee00,UINavigationController
0x7fa9c3609c40,UIWindow
0x7fa9c3400020,UIApplication
0x608000038900,AppDelegate
2017-03-08 22:49:54.501 view[36189:410065] hide
0x7fa9c36059f0,MyView
0x7fa9c3407cb0,UIView
0x7fa9c340b9d0,ViewController
0x7fa9c3500bd0,UIView  <----- not real hide
2017-03-08 22:49:54.501 view[36189:410065] show
0x7fa9c36059f0,MyView
0x7fa9c3407cb0,UIView
0x7fa9c340b9d0,ViewController
0x7fa9c3500bd0,UIView
0x7fa9c3403c50,UIViewControllerWrapperView
0x7fa9c340ee90,UINavigationTransitionView
0x7fa9c5802d10,UILayoutContainerView
0x7fa9c381ee00,UINavigationController
0x7fa9c3609c40,UIWindow
0x7fa9c3400020,UIApplication
0x608000038900,AppDelegate
2017-03-08 22:49:54.501 view[36189:410065] show
0x7fa9c35062f0,MyView
0x7fa9c3505ae0,UIView
0x7fa9c58030c0,ViewController
0x7fa9c3506c10,_UIParallaxDimmingView
0x7fa9c35022c0,UIView
0x7fa9c3403c50,UIViewControllerWrapperView
0x7fa9c340ee90,UINavigationTransitionView
0x7fa9c5802d10,UILayoutContainerView
0x7fa9c381ee00,UINavigationController
0x7fa9c3609c40,UIWindow
0x7fa9c3400020,UIApplication
0x608000038900,AppDelegate
2017-03-08 22:49:55.037 view[36189:410065] hide
0x7fa9c36059f0,MyView
0x7fa9c3407cb0,UIView
0x7fa9c340b9d0,ViewController  <----- real hide
Umbilicate answered 8/3, 2017 at 15:10 Comment(0)
C
0

Try to set NO for pushViewController:animated:, in another word, use pushViewController:vc animated:NO instead. I Guess if we set YES for animated:, in order to show the animation of current VC dismissing, RandomView will be added again. And after the animation ends,RandomView will be removed. That could explain the log that willMoveToWindow: is called another twice

Carboniferous answered 24/2, 2024 at 14:24 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.