iOS App冻结PushViewController

时间:2016-04-08 15:13:14

标签: ios swift uinavigationcontroller sdwebimage

我的导航控制器间歇地会在推送时冻结。它似乎将新的视图控制器添加到堆栈中,但动画永远不会发生。我还有两个其他容器在屏幕上保存视图控制器,我可以在导航控制器冻结后很好地与它们进行交互。真正有趣的是,如果我尝试将另一个视图控制器推到导航控制器的堆栈上,我注意到堆栈顶部有一个额外的视图控制器(我最初推动的视图控制器冻结了导航控制器) )。因此,如果我在主屏幕上(我们将其称为VC-Home)并尝试推送新视图(VC-1)并冻结,那么我尝试推送新视图(VC -2),这是我在推送之前在当前堆栈中看到的内容:

{ [VC-Home, VC-1] }

在调用pushViewController之后,它保持不变; VC-2未添加到堆栈中。

据我所知,导航控制器通过在动画开始之前使前一个视图控制器处于非活动状态来启动动画,但随后动画永远不会发生,导航控制器处于冻结状态。

我通过调用从故事板创建新的视图控制器 UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController")所以我不认为那里有任何问题。我也没有在导航栏上覆盖pushViewController。关于我的应用程序的一些独特之处在于它是非常高分辨率的图像(使用SDWebImage来管理它)并且我一次在屏幕上有三个容器(一个导航控制器,一个用于搜索的视图控制器和一个交互式装订线/侧面/滑出菜单)。

CPU使用率低且内存使用率正常(发生冻结时设备稳定在60-70MB左右)。

是否有任何想法可能导致此问题或任何调试技巧可以帮助我发现真正的问题?

更新

UINavigationController没有唯一的代码,因为我只是推动使用pushViewController()。这是调用它的代码:

func didSelectItem(profile: SimpleProfile) {
     let vc = UIStoryboard.profileViewController()
     vc.profile = profile
     navigationController?.pushViewController(vc, animated: true)
}

我推送的ViewController在viewDidLoad中有以下代码:

override func viewDidLoad() {
    super.viewDidLoad()
    button.roundView()

    if let type = profile?.profileType {
        //load multiple view controllers into a view pager based on type 
        let viewControllers = ProfileTypeTabAdapter.produceViewControllersBasedOnType(type)
        loadViewPagerViews(viewControllers)

        let topInset = headerView.bounds.height + tabScrollView.contentSize.height
        if let viewPager = viewPager {
            for view in viewPager.views {
                if let tempView = view as? PagingChildViewController {
                    tempView.profile = fullProfile
                    tempView.parentVCDelegate = self
                    tempView.topInset = topInset
                }
            }
        }
    }
}

func loadViewPagerViews(viewControllers: [UIViewController]) {
    viewPager?.views = viewControllers
    viewPager?.delegate = self

    //loading views into paging scroll view (using PureLayout to create constraints)
    let _ = subviews.map { $0.removeFromSuperview() }
    var i = 0
    for item in views {
        addSubview(item.view)
        item.view.autoSetDimensionsToSize(CGSize(width: tabWidth, height: tabHeight))
        if i == 0 {
            item.view.autoPinEdgeToSuperviewEdge(.Leading)
        } else if let previousView = views[i-1].view {
            item.view.autoPinEdge(.Leading, toEdge: .Trailing, ofView: previousView)
        }
        if i == views.count {
            item.view.autoPinEdgeToSuperviewEdge(.Trailing)
        }
        i += 1
    }

    contentSize = CGSize(width: Double(i)*Double(tabWidth), height: Double(tabHeight))
}

更新2

我终于让它再次冻结。该应用程序在后台,我把它带回来,并试图在它冻结时推动堆栈上的视图控制器。我注意到一个动画正在发生。我在页面顶部有一个滚动视图,每10秒翻阅一次内容(想想应用程序存储顶部横幅)。在这个冻结中,我注意到横幅是动画中期。

这是来自我的UIScrollView的滚动功能,每10秒调用一次:

func moveToNextItem() {
    let pageWidth: CGFloat = CGRectGetWidth(frame)
    let maxWidth: CGFloat = pageWidth * CGFloat(max(images.count, profileImages.count))
    let contentOffset: CGFloat = self.contentOffset.x
    let slideToX = contentOffset + pageWidth

    //if this is the end of the line, stop the timer
    if contentOffset + pageWidth == maxWidth {
        timer?.invalidate()
        timer = nil
        return
    }

    scrollRectToVisible(CGRectMake(slideToX, 0, pageWidth, CGRectGetHeight(frame)), animated: true)
}

我不记得因为动画/滚动发生而停止了推,但我可能错了。

我还重新检查了堆栈,上面描述的情况仍然是[VC-Home,VC-1]是堆栈而VC-2没有按下的情况。我也经历了VC-1的变量,所有内容都已加载(数据调用和图像加载)。

更新3

这是第二个变得越来越陌生。我已经覆盖了pushViewController,因此我可以在其中放置断点并根据Alessandro Ornano的响应进行一些调试。如果我推动视图控制器失败,然后将我的应用程序发送到后台,将一个断点放入pushViewController调用,并将应用程序恢复,断点会立即被多次击中。如果我继续经过所有命中,下一个视图控制器突然变得可见,我试图推送的最后一个视图控制器现在作为最后一个视图控制器在堆栈上。这意味着我看到的那个仍然被禁用,这实际上使我处于与以前相同的位置。

6 个答案:

答案 0 :(得分:39)

我们几周后面临同样的问题。对于我们的问题,我们将其缩小到left-edge pop gesture recogniser。您可以尝试使用以下步骤检查是否可以重现此问题

  • 当下方没有视图控制器时(即在根视图控制器上,您的VC-Home控制器),尝试使用左边缘弹出手势
  • 在此之后尝试点击任何UI元素。

如果您能够重现冻结,请尝试在视图控制器堆栈只有一个视图控制器时禁用interactivePopGestureRecognizer

有关详细信息,请参阅this问题。以下是链接中的代码,以便于参考。

- (void)navigationController:(UINavigationController *)navigationController
   didShowViewController:(UIViewController *)viewController
                animated:(BOOL)animate
{
    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
{
        if (self.viewControllers.count > 1)
        {
            self.interactivePopGestureRecognizer.enabled = YES;
        }
        else
        {
            self.interactivePopGestureRecognizer.enabled = NO;
        }
    }
}

答案 1 :(得分:5)

很棒的answer来自@Penkey Suresh!救了我的一天!这是一个 SWIFT 3 版本,只有少量添加,这对我有所帮助:

    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {


    if (navigationController.viewControllers.count > 1)
    {
         self.navigationController?.interactivePopGestureRecognizer?.delegate = self
        navigationController.interactivePopGestureRecognizer?.isEnabled = true;
    }
    else
    {
         self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
        navigationController.interactivePopGestureRecognizer?.isEnabled = false;
    }
}

不要忘记添加UINavigationControllerDelegate并设置navigationController?.delegate = self 另一个重要的部分是将interactivePopGestureRecognizer分配给self或者相应地为nil。

答案 2 :(得分:2)

我在考虑间歇性冻结主线程 SDWebImage

假设您使用从 downloadImageWithURL下载的图像:options:progress:completed:已完成的块...如果是这样,请确保在使用图像之前调度到主队列。

如果直接使用SDWebImageDownloader,将在后台队列上调用完成块(如您所述),您可以在完成时使用主队列上的dispatch_async来修复它。

否则你可以使用: SDWebImageManager downloadImageWithURL:options:progress:completed:(调用主队列上的完成块的方法)。

如果问题仍然存在(仅仅是因为你在说“我的应用程序的一些独特的东西是它非常重要......”)同样看看Common problems特别是处理图像刷新知道问题。

此处还添加了一个不错的代码段:

import UIKit.UINavigationController

public typealias VoidBlock = (Void -> Void)

public extension UINavigationController  
{
    public func pushViewController(viewController: UIViewController, animated: Bool, completion: VoidBlock) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        self.pushViewController(viewController, animated: animated)
        CATransaction.commit()
    }
}

也许可以帮助理解pushViewController是否完成,如果完成所有viewControllers预期..

我尝试进行的另一项测试是使用iOS 8.x和iPhone 6+启动应用程序,因为iOS 9中的pureLayout项目中有一些issues。您可以围绕此测试发送反馈吗?

在推送视图操作之前,我对真实的滚动视图维度也有一些怀疑,您能否按examing the view hierarchy分析当前视图?

答案 3 :(得分:0)

尝试主线程

dispatch_async(dispatch_get_main_queue()){

UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController")
// your code

}

答案 4 :(得分:0)

请检查您的BaseNavigationViewController中是否有任何不必要的代码,如下所示:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

在这里,我有一个示例代码来重现此问题(在推送VC中冻结应用程序),您必须通过以下步骤重现此问题:

  • 当下方没有视图控制器时(即在根视图控制器上,即VC-Home控制器上)尝试使用(左|右)边缘弹出手势
  • 尝试在此之后单击任何UI元素(这会将您推送到下一个ViewController)。

示例代码:https://github.com/aliuncoBamilo/TestNavigationPushBug

答案 5 :(得分:0)

我的情况解决了,方法是重新启动Xcode和模拟器,然后从模拟器列表中选择其他设备。