繁体   English   中英

如何在swiftUI中以高性能显示来自Observable Object的数据?

[英]How to display data from Observable Object with high performances in swiftUI?

在我的指南针应用程序中,我显示航向但性能不高:当我快速转动设备时,所有度数都没有显示。 我搜索了与 Apple 的 natif compass 应用程序相同的性能。 例如,当角度超过 359 到 0 度时,我会振动以通知用户。 但有时振动不会出现。

我的 LocationProvider 类:

import SwiftUI
import CoreLocation
import Combine

public class LocationProvider: NSObject, CLLocationManagerDelegate, ObservableObject {

  private let locationManager: CLLocationManager
  public let heading = PassthroughSubject<CGFloat, Never>()

  @Published var currentHeading: CGFloat {
    willSet {
      heading.send(newValue)
    }
  }

  public override init() {
    currentHeading = 0
    locationManager = CLLocationManager()
    super.init()
    locationManager.delegate = self
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.startUpdatingHeading()
    locationManager.requestWhenInUseAuthorization()
  }

  public func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
    self.currentHeading = CGFloat(newHeading.trueHeading)
  }
}

我的内容视图:

import SwiftUI
import CoreLocation

struct ContentView: View {

  @ObservedObject var location: LocationProvider = LocationProvider()

  @State var angle: CGFloat = 0

  var body: some View {
    VStack {
      Text(String(Double(-self.location.currentHeading + 360).stringWithoutZeroFraction) + "°")
        .font(.system(size: 80))
    }
    .onReceive(self.location.heading) { heading in
      compassTapticFeedback(-heading + 360)
    }
  }
}

public extension Double {
  var stringWithoutZeroFraction: String {
    return String(format: "%.0f", self)
  }
}

// Taptic feednack
func tapticFeedback(_ type: String) {
  switch type {
    case "heavy":
      let tapticFeedback = UIImpactFeedbackGenerator(style: .heavy)
      tapticFeedback.prepare()
      tapticFeedback.impactOccurred()
    case "medium":
      let tapticFeedback = UIImpactFeedbackGenerator(style: .medium)
      tapticFeedback.prepare()
      tapticFeedback.impactOccurred()
    case "light":
      let tapticFeedback = UIImpactFeedbackGenerator(style: .light)
      tapticFeedback.prepare()
      tapticFeedback.impactOccurred()
    default:
      let tapticFeedback = UIImpactFeedbackGenerator(style: .medium)
      tapticFeedback.prepare()
      tapticFeedback.impactOccurred()
  }
}

func compassTapticFeedback(_ angle: CGFloat) {
  switch Int(angle) {
    case 0:
      tapticFeedback("heavy")
    case 30:
      tapticFeedback("heavy")
    case 60:
      tapticFeedback("heavy")
    case 90:
      tapticFeedback("heavy")
    case 120:
      tapticFeedback("heavy")
    case 250:
      tapticFeedback("heavy")
    case 180:
      tapticFeedback("heavy")
    case 210:
      tapticFeedback("heavy")
    case 240:
      tapticFeedback("heavy")
    case 270:
      tapticFeedback("heavy")
    case 300:
      tapticFeedback("heavy")
    case 330:
      tapticFeedback("heavy")
    default:
      return
  }
}

可能是因为我对角度做了很多计算以获得没有零分数的值? 我不知道。 即使我不确定这是原因。

问题是您正在检查当前角度是否是30的倍数。 这样做的问题是,如果您将设备旋转得太快,该数字将被跳过,因此不会被检测到。

在完成compassTapticFeedback(_:)的缩短版本后,如下面的进一步提示中的第二点所示,我添加了更多代码以使该方法检测中间跳过的角度:

enum TapticSync {
    static var lastAngle: CGFloat = 0
}


func compassTapticFeedback(_ angle: CGFloat) {
    // If the angle is 30°, carry on. Otherwise do more checks
    if !Int(angle).isMultiple(of: 30) {
        // If there was an angle at a multiple of 30° skipped, carry on. Otherwise do not trigger haptic feedback.
        let minVal = min(angle, TapticSync.lastAngle)
        let maxVal = max(angle, TapticSync.lastAngle)
        let roundUpToNextMultiple = ceil(minVal / 30) * 30
        guard maxVal > roundUpToNextMultiple else { return }
    }

    // If the checks were passed, trigger the haptic feedback and update the last angle
    tapticFeedback("heavy")
    TapticSync.lastAngle = angle
}

更多提示

1:触觉反馈.prepare()

在您的情况下, tapticFeedback.prepare()毫无意义。 正如.prepare()的文档中.prepare()

调用 prepare() 然后立即触发反馈(中间没有任何时间)不会改善延迟。

你应该:

想想什么时候可以最好地准备发电机。 在触发反馈的事件之前调用 prepare()。 系统需要时间来准备 Taptic Engine 以实现最小延迟。

有关完整信息,请参阅文档

2:检查角度

在您的情况下,您不需要switch来检查每个 30 的倍数的角度。相反,将compassTapticFeedback(_:)更改为如下所示:

func compassTapticFeedback(_ angle: CGFloat) {
    if Int(angle).isMultiple(of: 30) {
        tapticFeedback("heavy")
    }
}

3:你的罗盘方向反了

由于这些部分,以度为单位的方向是相反的(删除了不必要的self. ):

-location.currentHeading + 360

compassTapticFeedback(-heading + 360)

相反,因此度数顺时针增加,请使用:

location.currentHeading

compassTapticFeedback(heading)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM