如何只在 viewDidAppear 中做一些事情?

Posted

技术标签:

【中文标题】如何只在 viewDidAppear 中做一些事情?【英文标题】:How to do some stuff in viewDidAppear only once? 【发布时间】:2015-01-29 04:33:54 【问题描述】:

我想检查粘贴板并在视图出现时显示是否包含特定值的警报。我可以将代码放入viewDidLoad 以确保它只被调用一次,但问题是警报视图显示得太快。我知道我可以设置一个计时器来推迟警报的出现,但我认为这不是一个好的解决方法。

查了ios 7 - Difference between viewDidLoad and viewDidAppear的问题,发现有一个步骤可以检查视图是否存在。所以我想知道是否有任何api可以做到这一点?

更新:“仅一次”表示视图控制器实例的生命周期

【问题讨论】:

我创建了一个库来做同样的事情github.com/T-Pham/ViewDidAppearFirstTime 【参考方案1】:

有一个标准的内置方法可供您使用。

目标-C:

- (void)viewDidAppear:(BOOL)animated 
    [super viewDidAppear:animated];

    if ([self isBeingPresented] || [self isMovingToParentViewController]) 
        // Perform an action that will only be done once
    

斯威夫特 3:

override func viewDidAppear(_ animated: Bool) 
    super.viewDidAppear(animated)

    if self.isBeingPresented || self.isMovingToParentViewController 
        // Perform an action that will only be done once
    

当视图控制器由于模态显示而首次显示时,对isBeingPresented 的调用为真。 isMovingToParentViewController 在视图控制器第一次被推送到导航堆栈时为真。当视图控制器第一次出现时,两者中的一个为真。

无需处理BOOL ivars 或任何其他技巧来跟踪第一个呼叫。

【讨论】:

很好,我不知道 赞成...但是,如果您保持对视图控制器的强引用并多次关闭并呈现它,这仍然是正确的吗? @LyndseyScott 可能。这个问题在这个细节上有点模糊。如果 OP 的愿望是在应用程序的生命周期中只调用一次该方法(或者在视图控制器实例的生命周期中至少只调用一次),而不管它显示多少次,那么这不是正确的解决方案。在这种情况下,最好在 Inder 的回答中使用dispatch_once @rmaddy 我试过了,好像当viewController被添加为childViewController时它不起作用。 :( @farzadshbfn,容器将在其 viewDid 中正确获取这些标志/将出现,这取决于它将这些标志传递给子项的方式。 UINavigationController 不会将这些标志传递给它的 rootviewcontroler【参考方案2】:

rmaddy 的回答非常好,但是当视图控制器是导航控制器的根视图控制器以及所有其他不将这些标志传递给其子视图控制器的容器时,它并不能解决问题。

所以我发现这种情况最好使用标志并在以后使用它。

@interface SomeViewController()

    BOOL isfirstAppeareanceExecutionDone;

@end

@implementation SomeViewController

-(void)viewDidAppear:(BOOL)animated 
    [super viewDidAppear:animated];
    if(isfirstAppeareanceExecutionDone == NO) 
        // Do your stuff
        isfirstAppeareanceExecutionDone = YES;
    


@end

【讨论】:

正确。感谢您的帮助【参考方案3】:

如果我正确理解你的问题,你可以简单地设置一个 BOOL 变量来识别 viewDidAppear 已经被调用,例如:

- (void)viewDidAppear 
    if (!self.viewHasBeenSet)  // <-- BOOL default value equals NO

        // Perform whatever code you'd like to perform
        // the first time viewDidAppear is called

        self.viewHasBeenSet = YES; 
    

【讨论】:

我认为您不需要在 viewDidLoad 中将 BOOL 设置为 NO。创建对象时,它会以NO 开头,而viewDidLoad,afaik,只会在视图控制器对象的生命周期内被调用一次。 @MaxGabriel 为了清楚起见,我这样写,但是是的,这是真的。 好的,好的。我删除了变量的初始设置并添加了注释来解释 BOOL 默认为 NO。【参考方案4】:

此解决方案将在应用程序的整个生命周期中仅调用一次viewDidAppear,即使您创建了视图控制器的多个对象,这一次也不会被调用。请参考rmaddy's answer above

您可以在viewDidLoad 中执行选择器,也可以在viewDidAppear 中使用dispatch_once_t。如果您找到更好的解决方案,请与我分享。我就是这样做的。

- (void)viewDidLoad 
  [super viewDidLoad];
  [self performSelector:@selector(myMethod) withObject:nil afterDelay:2.0];


- (void)viewDidAppear:(BOOL)animated 
  [super viewDidAppear:animated];
  static dispatch_once_t once;
  dispatch_once(&once, ^
    //your stuff
    [self myMethod];
  );

【讨论】:

我喜欢你的回答,但是如果static dispatch_once_t once; 是在本地声明的,那么应用程序如何知道在释放变量后下次不执行它?似乎需要将其存储为类变量... dispatch_once 解决方案的真正问题是static 跨越视图控制器的实例。此解决方案确保myMethod 在应用进程的整个生命周期中仅被调用一次。 @LyndseyScott 不,没关系。这是一个静态变量而不是局部变量。您可以阅读有关静态变量的信息。这是一个线程***.com/q/13415321/468724 @rmaddy 正确,如果你拆除并重建视图控制器,被 dispatch_once 包裹的代码将不会再次执行。在用户硬重置应用程序之前,它永远不会再次执行!这在几乎所有情况下都是不受欢迎的行为(包括本文中指定的情况) 那么最好有一个你第一次设置的布尔标志。随后检查标志是否设置【参考方案5】:

通过阅读其他 cmets(并基于 @rmaddy 的回答),我知道这不是 OP 所要求的,而是对于那些因为问题标题而来到这里的人:

extension UIViewController 
    var isPresentingForFirstTime: Bool 
        return isBeingPresented() || isMovingToParentViewController()
    

更新

您应该在viewDidAppearviewWillAppear 中使用此方法。 (感谢@rmaddy)

更新 2

此方法仅适用于模态呈现的视图控制器和推送的视图控制器。它不适用于 childViewController。使用 didMoveToParentViewController 会更好地使用 childViewControllers。

【讨论】:

您应该注意,这仅在从viewWillAppear:viewDidAppear: 访问isPresentingForFirstTime 时才有效。 对于 UITabBarController 内的视图控制器,这两个建议都不适合我??‍♂️ @OliverPearmain ViewControllers 内 UITabBarController 被管理为子容器(如果我没记错的话)......如果你检查 didMoveToParentViewController 它应该只被调用一次【参考方案6】:

您不应该在嵌套视图控制器中遇到此检查问题

extension UIViewController 

  var isPresentingForFirstTime: Bool 
      if let parent = parent 
        return parent.isPresentingForFirstTime
      
      return isBeingPresented || isMovingFromParent
  

【讨论】:

【参考方案7】:

尝试设置一个BOOL值,当情况发生时调用它。

@interface AViewController : UIViewController
   @property(nonatomic) BOOL doSomeStuff;
@end

@implementation AViewController
- (void) viewWillAppear:(BOOL)animated

    if(doSomeStuff)
    
       [self doSomeStuff];
       doSomeStuff = NO;
    

在你初始化 AViewController 实例的地方:

AddEventViewController *ad = [AddEventViewController new];
ad.doSomeStuff = YES;

不确定您为什么在ViewDidAppear 中这样做?但是,如果您希望 doSomeStuff 是私有的并且 soSomeStuff 只被调用一次,这是另一种通知解决方案:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doSomeStuff) name:@"do_some_stuff" object:nil];

- (void) doSomeStuff

然后在某个地方发布:

[[NSNotificationCenter defaultCenter] postNotificationName:@"do_some_stuff" object:nil];

【讨论】:

这段代码去哪儿了?它还要求在某处将doSomeStuff 初始化为YES 这不是一个合适的解决方案。 doSomeStuff 不应公开。视图控制器本身应该是唯一知道是否应该设置 doSomeStuff 的类。 不知道你为什么这样做,可能是另一种方式可以帮助,再次编辑它,希望它有帮助。【参考方案8】:

斯威夫特 5

我尝试过 isBeingPresented() 或 isMovingToParent。 但它不起作用。 所以我尝试了下面的代码。它对我有用!

override func viewDidAppear(_ animated: Bool) 
    if (self.isViewLoaded) 
         // run only once
    

【讨论】:

【参考方案9】:

你可以在 ViewDidLoad 方法中使用这个函数

performSelector:withObject:afterDelay:

它会在延迟后调用该函数。所以您不必使用任何自定义计时器对象。 并且您可以使用一次

dispatch_once DCD 块。只需在 dispatch_once 块中执行选择器,它只会在调用 ViewDidLoad 时调用一次 performSelector

希望对你有帮助

【讨论】:

这是从哪里调用的?通过展示如何实际使用来改进您的答案。 问题是关于 viewDidApear 方法 @mohamadrezakoohkan viewdidApear 可以被多次调用,即使视图已经加载。所以上述问题中的问题是他只想执行一次某件事。所以我让他将他的逻辑从 viewdidApear 转移到 viewdidLoad 因为 viewDidload 只被调用一次。如果他想做一些 UI 的事情,那么他可以在延迟后使用执行选择器调用它,直到 UI 正确显示,这样他就可以避免额外的条件,如果他必须在 viewdidApear 中实现。希望它能解决您的疑虑。

以上是关于如何只在 viewDidAppear 中做一些事情?的主要内容,如果未能解决你的问题,请参考以下文章

在 OpenCV 中,一些方法只存在于 c++ 中,如何在 python 中做同样的事情?

如何在OpenGL中做“AND”模具?

在非 UI 线程中做一些 Android UI 的事情

iPhone - 从 URL 加载图像时内容不会滚动

如何让同一个类的对象在同一个函数中做不同的事情?

iOS 上的 LibGDX:使用 Robovm 以编程方式旋转屏幕