首页 > 解决方案 > 暂停/恢复后 iOS UIView 变慢

问题描述

UIImage.drawAt我注意到一件奇怪的事情:UIView在挂起/恢复后的执行速度变慢了两倍。在按 Home 并返回应用程序后,我在 iOS 9-13 上的模拟器和设备上看到 FPS 下降了 2 倍。它在String.drawAt或填充矩形方面的能力保持不变(这也使我在恢复后重新启动时不太可能犯一些错误)。

谁能解释为什么以及如何修复它?恢复后重新创建视图可以解决问题,但我宁愿使用现有实例。

下面是一个简短的例子。您只需要将一些图像添加到资产中(我的是 112x145 PNG,具有透明度)。如果您的图像太小以至于示例应用程序显示 60 FPS,max请在最后一个方法中增加参数。

编辑确保将您的图像包含在所有 x1、x2、x3 中,以便加载原始分辨率。否则放大将是最慢的操作,而不是绘图。

//  AppDelegate.swift

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder,UIApplicationDelegate
{
  var window: UIWindow?

  func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)->Bool
  {
    self.window=UIWindow(frame: UIScreen.main.bounds)
    window!.makeKeyAndVisible()
    let testViewController=TestViewController()
    window!.rootViewController=testViewController
    Runner._instance.testViewController=testViewController
    return true
  }

  func applicationDidEnterBackground(_ application: UIApplication)
  {
    Runner._instance.stop()
  }

  func applicationDidBecomeActive(_ application: UIApplication)
  {
    Runner._instance.start()
  }

  func applicationWillTerminate(_ application: UIApplication)
  {
    Runner._instance.stop()
  }
}

class Runner: NSObject
{
  static let _instance=Runner()
  var testViewController: TestViewController!
  var displayLink: CADisplayLink!
  var fps=0
  var lastFpsCounterTime=Date().timeIntervalSince1970
  var fpsCounter=0

  func start()
  {
    if displayLink == nil
    {
      displayLink=CADisplayLink(target: self,selector: #selector(Runner.run))
      displayLink.add(to: RunLoop.main,forMode: RunLoop.Mode.common)
    }
  }

  func stop()
  {
    displayLink?.invalidate()
    displayLink=nil
  }

  @objc func run()
  {
    if lastFpsCounterTime+1<Date().timeIntervalSince1970
    {
      fps=fpsCounter
      lastFpsCounterTime=Date().timeIntervalSince1970
      fpsCounter=0
    }
    fpsCounter+=1
    testViewController.view.setNeedsDisplay()
  }
}

class TestViewController: UIViewController
{
  required init?(coder aDecoder: NSCoder)
  {
    super.init(coder: aDecoder)
    loadView()
  }

  override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
  {
    super.init(nibName: nibNameOrNil,bundle: nibBundleOrNil)
    loadView()
  }

  override func loadView()
  {
    view=TestView(frame: UIScreen.main.bounds)
  }
}

class TestView: UIView
{
  let image=UIImage(named: "card")!

  required init?(coder: NSCoder)
  {
    super.init(coder: coder)
  }

  override init(frame: CGRect)
  {
    super.init(frame: frame)
    isOpaque=true
  }

  override func draw(_ _rect: CGRect)
  {
    let context=UIGraphicsGetCurrentContext()!
    context.setFillColor(red: 1, green: 1, blue: 1, alpha: 1)
    context.fill(bounds)
    let max=50  //Choose max high enough to get less than 60 FPS
    let time=Date().timeIntervalSince1970
    for i in 1...max
    {
      //image.draw(at: ) is affected by suspend/resume, but "String".draw(at: ) is not 
      image.draw(at: CGPoint(x: (image.size.width+bounds.width)*CGFloat(time.truncatingRemainder(dividingBy: Double(1+i))/Double(1+i))-image.size.width,y: bounds.height*CGFloat(i)/CGFloat(max)))
    }
    let font=UIFont(name: "Arial", size: 15)!
    let textFontAttributes=[NSAttributedString.Key.font: font,
     NSAttributedString.Key.foregroundColor: UIColor(red: 1, green: 0, blue: 0, alpha: 1),
     NSAttributedString.Key.paragraphStyle: NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle]
    "FPS: \(Runner._instance.fps)".draw(at: CGPoint(x: 2,y: 30),withAttributes: textFontAttributes)
  }
}

标签: iosswiftuiviewuiimagecore-graphics

解决方案


推荐阅读