UITableView在viewDidAppear之前不会呈现

时间:2019-08-10 17:45:59

标签: ios swift uitableview

我正在使用Hero为过渡到UITableViewController的动画设置动画,这导致我的UITableView在调用viewDidAppear()之后才渲染任何单元格。 / p>

我已经确定UITableView 在调用viewDidAppear()之前(使用日志记录和时间戳记)早已完成加载,并且已使用print语句确定UITableView恰好在调用viewDidAppear()时出现。其他一些堆栈溢出帖子建议将tableView.reloadData()放在主线程上,我已经这样做了(无济于事)。

import UIKit
import os.log
import Hero

class MemberTableViewController: UITableViewController, UIGestureRecognizerDelegate {

    // MARK: Properties

    var members: [Member]?

    // drag to exit
    var panGestureRecognizer: UIPanGestureRecognizer!
    var progressBool: Bool = false
    var dismissBool: Bool = false

    // MARK: Overrides

    override func viewDidLoad() {
        super.viewDidLoad()

        // register cells
        tableView.register(UINib(nibName: "MemberTableViewCell", bundle: Bundle.main), forCellReuseIdentifier: "MemberCell")

        // configure hero
        view.hero.isEnabled = true
        view.hero.isEnabledForSubviews = false
        view.hero.id = "members"
        view.hero.modifiers = [.arc(), .useLayerRenderSnapshot]

        // fetch members
        API.shared.getMembers { members in
            self.members = members
            DispatchQueue.main.async {
                self.tableView.reloadSections(IndexSet(arrayLiteral: 0), with: .fade)
            }
        }

        // setup drag to exit
        panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(exit))
        panGestureRecognizer.delegate = self
        view.addGestureRecognizer(panGestureRecognizer)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // customize navigationbar
        self.navigationController?.navigationBar.shadowImage = UIImage()
        self.navigationController?.navigationBar.backgroundColor = self.tableView.backgroundColor
        self.navigationController?.setNavigationBarHidden(false, animated: animated)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("appeared")
    }

    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }

    override var prefersHomeIndicatorAutoHidden: Bool {
        return true
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return members?.count ?? 0
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "MemberCell", for: indexPath) as? MemberTableViewCell else {
            fatalError("Dequeued cell not an instance of MemberTableViewCell")
        }

        // Configure the cell...
        cell.load(from: members![indexPath.row])

        return cell
    }

    // MARK: Private Methods

    @objc private func exit(recognizer: UIPanGestureRecognizer) {
        let translation = recognizer.translation(in: nil)
        let progressY = (translation.y / 2) / view.bounds.height

        if recognizer.direction == .down && tableView.isAtTop {
            if dismissBool {
                dismissBool = false
                hero.dismissViewController()
                self.hero.modalAnimationType = .uncover(direction: .down)
                progressBool = true
                recognizer.setTranslation(.zero, in: view)
            }
        }

        switch recognizer.state {
        case .changed:
            if progressBool {
                let currentPos = CGPoint(x: view.center.x, y: translation.y + view.center.y)
                Hero.shared.update(progressY)
                Hero.shared.apply(modifiers: [.position(currentPos)], to: view)
            }

        default:
            dismissBool = true
            progressBool = false

            if abs(progressY + recognizer.velocity(in: nil).y / view.bounds.height) > 0.5 {
                Hero.shared.finish()
            } else {
                Hero.shared.cancel()
            }
        }
    }

    // MARK: UIGestureRecognizerDelegate

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

    // MARK: UIScrollViewDelegate

    override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if scrollView.isAtTop && scrollView.panGestureRecognizer.direction == .down {
            scrollView.contentOffset = CGPoint(x: 0, y: -(UIApplication.shared.keyWindow?.safeAreaInsets.top ?? 0)
            )
        }
    }
}

我希望UITableView会在动画结束的那一刻出现,并且会播放tableView.reloadSections()动画,但似乎动画发生在UITableView实际出现之前,因此稍等一会儿(看起来很糟,请参阅下文)后,它就会闪烁进入视野。

What's happening

注意:禁用英雄功能后,tableView.reloadSections()动画将立即播放。

1 个答案:

答案 0 :(得分:1)

我没有使用您提到的框架,但是您可以尝试增加重新加载tableview的延迟,直到视图完成渲染为止:

DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
    self.tableView.reloadSections(IndexSet(arrayLiteral: 0), with: .fade)
}

三秒钟是很长的时间。我喜欢称之为“ caveman”调试。三秒钟是非常荒谬的时间,因此,如果可行,请尝试减少延迟因子。

另一种想法是也将成员设置在主队列上,所以类似这样:

DispatchQueue.main.async {
    self.members = members
    self.tableView.reloadSections(IndexSet(arrayLiteral: 0), with: .fade)
}

另一种想法是将您的重载移至viewDidAppear()

更新

这是一个断言,您可以使用它来等待视图完全加载并调整大小:

var tableViewLoaded: Bool = false

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    guard isViewLoaded, view.window != nil, !tableViewLoaded else { return }
    tableView.reloadSections(IndexSet(arrayLiteral: 0), with: .fade)
    tableViewLoaded = true // flip it to true so you don't constantly reload
}