如何在swiftUI中以高性能显示可观察对象的数据?

问题描述 投票:0回答:1

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

我的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)
  }
}

我的contentView:

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
  }
}

可能是因为我在角度上进行了大量计算以获得没有零分数的值?我不知道。即使我不确定是什么原因。

swift swiftui core-location
1个回答
0
投票

问题是您正在检查当前角度是否为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()。系统需要时间来准备Taptic Engine,以将延迟降至最低。

请参阅documentation以获取完整信息。

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)
© www.soinside.com 2019 - 2024. All rights reserved.