swiftui - 如何暂停在 SwiftUI 中使用的计时器?
问题描述
我有这个功能可以改变地图的中心位置,这会导致地图在所有位置上进行动画处理。纬度和经度正在递增和递减,并使用计时器重复。但是我目前正在尝试暂停计时器。我尝试了多种方法,似乎无法让计时器暂停几秒钟并恢复。唯一有效的是使用睡眠,但这会导致整个 UI 暂停。这个“移动”函数正在更新@State CLLocationCoordinate 的经纬度。此方法在 .onAppear() 中调用。
timer.fire() 不起作用
我也尝试过这样的事情:
timer.invalidate()
DispatchQueue.main.asyncAfter(deadline: .now() + 5.00) {
timer.fire()
}
上面的代码不起作用
func moveRegion() {
var currentLatitude = region.center.latitude
var currentLongitude = region.center.longitude
let increment = 0.25
var southAmerica = false
var europe = false
var australia = false
var america = true
Timer.scheduledTimer(withTimeInterval: (1.0/30.0), repeats: true) { (timer) in
//AMERICA CORD 37.0000, -95.000
//MOVING TO SOUTH AMERICA
//SOUTH AMERICA CORD -33.000, -70.000
if (america == true && southAmerica == false && europe == false && australia == false){
if (currentLatitude <= -33.000 && currentLatitude >= 37.0000 || currentLongitude <= -70.0000 && currentLongitude >= -95.0000 || currentLongitude >= -101.69999999998991) {
currentLatitude -= increment
currentLatitude -= increment
currentLatitude -= increment
if currentLongitude < -70.00 {
currentLongitude += increment
}
}
if (currentLatitude == -38.15000000000002 || currentLatitude <= -38.150000000000006 && currentLongitude <= -69.95000000000142){
// sleep(5), works but pauses whole UI
timer.invalidate()
Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { pauseTimer in
timer.fire()
print(timer.isValid)
}
southAmerica = true
australia = false
america = false
europe = false
}
}
//MOVING TO EUROPE
//Europe CORD 48.000, 15.000
//if (currentLongitude > -74.00 && currentLongitude < -4.00)
if (southAmerica == true && australia == false && america == false && europe == false ){
if (currentLatitude > -39.000 && currentLatitude < 55.000 || currentLongitude > -70.00 && currentLongitude < 16.000) {
currentLongitude += increment
currentLongitude += increment
currentLatitude += increment
currentLatitude += increment
}
if (currentLatitude >= 48.04999999999936 || currentLatitude == 48.04999999999937 && currentLongitude <= 16.249999999996835){
sleep(5)
europe = true
southAmerica = false
australia = false
america = false
}
}
//MOVING TO AUSTRALIA
//AUSTRALIA CORD -36.000, 133.000
if (europe == true && southAmerica == false && australia == false && america == false){
if (currentLongitude > 9.00 && currentLongitude < 133.000) {
currentLongitude += increment
currentLongitude += increment
currentLongitude += increment
}
if (currentLatitude >= -38.000 && currentLatitude <= 49.0000){
currentLatitude -= increment
currentLatitude -= increment
}
if (currentLatitude <= -37.05000000000008 && currentLongitude >= 132.04999999999274){
sleep(5)
australia = true
southAmerica = false
europe = false
america = false
}
}
//MOVING TO AMERIA
if (australia == true && southAmerica == false && america == false && europe == false){
if (currentLongitude < 179.55){
currentLongitude += increment
currentLongitude += increment
if (currentLongitude > 179.500){
currentLongitude = -179.000
}
}
if (currentLongitude > -95.100) {
currentLongitude += increment
currentLongitude += increment
}
if (currentLatitude >= -39.000 && currentLatitude <= 37.0000){
currentLatitude += increment
currentLatitude += increment
}
if (currentLatitude >= 37.04999999999998 && currentLongitude >= -95.100){
australia = false
southAmerica = false
europe = false
america = true
currentLatitude = 37.0000
currentLongitude = -95.000
sleep(5)
}
}
region.center.longitude = currentLongitude
region.center.latitude = currentLatitude
centerLocation.latitude = currentLatitude
centerLocation.longitude = currentLongitude
}
}
然后在视图中
var body: some View {
SwiftUIMapView(centerLocation: $centerLocation)
.onAppear {
DispatchQueue.main.async {
moveRegion()
}
}
}
下面是完整的代码:
import SwiftUI
import MapKit
struct MapView: View {
var timer = Timer()
//Start Location of the Map
@State var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 37.0000, longitude: -95.000),
span: MKCoordinateSpan(latitudeDelta: 40, longitudeDelta: 40)
)
@State var centerLocation = CLLocationCoordinate2D()
func moveRegion() {
var currentLatitude = region.center.latitude
var currentLongitude = region.center.longitude
let increment = 0.25
var southAmerica = false
var europe = false
var australia = false
var america = true
Timer.scheduledTimer(withTimeInterval: (1.0/30.0), repeats: true) { (timer) in
//AMERICA CORD 37.0000, -95.000
//MOVING TO SOUTH AMERICA
//SOUTH AMERICA CORD -33.000, -70.000
if (america == true && southAmerica == false && europe == false && australia == false){
if (currentLatitude <= -33.000 && currentLatitude >= 37.0000 || currentLongitude <= -70.0000 && currentLongitude >= -95.0000 || currentLongitude >= -101.69999999998991) {
currentLatitude -= increment
currentLatitude -= increment
currentLatitude -= increment
if currentLongitude < -70.00 {
currentLongitude += increment
}
}
if (currentLatitude == -38.15000000000002 || currentLatitude <= -38.150000000000006 && currentLongitude <= -69.95000000000142){
// sleep(5), works but pauses whole UI
timer.invalidate()
Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { pauseTimer in
timer.fire()
print(timer.isValid)
}
southAmerica = true
australia = false
america = false
europe = false
}
}
//MOVING TO EUROPE
//Europe CORD 48.000, 15.000
//if (currentLongitude > -74.00 && currentLongitude < -4.00)
if (southAmerica == true && australia == false && america == false && europe == false ){
if (currentLatitude > -39.000 && currentLatitude < 55.000 || currentLongitude > -70.00 && currentLongitude < 16.000) {
currentLongitude += increment
currentLongitude += increment
currentLatitude += increment
currentLatitude += increment
}
if (currentLatitude >= 48.04999999999936 || currentLatitude == 48.04999999999937 && currentLongitude <= 16.249999999996835){
sleep(5)
europe = true
southAmerica = false
australia = false
america = false
}
}
//MOVING TO AUSTRALIA
//AUSTRALIA CORD -36.000, 133.000
if (europe == true && southAmerica == false && australia == false && america == false){
if (currentLongitude > 9.00 && currentLongitude < 133.000) {
currentLongitude += increment
currentLongitude += increment
currentLongitude += increment
}
if (currentLatitude >= -38.000 && currentLatitude <= 49.0000){
currentLatitude -= increment
currentLatitude -= increment
}
if (currentLatitude <= -37.05000000000008 && currentLongitude >= 132.04999999999274){
sleep(5)
australia = true
southAmerica = false
europe = false
america = false
}
}
//MOVING TO AMERIA
if (australia == true && southAmerica == false && america == false && europe == false){
if (currentLongitude < 179.55){
currentLongitude += increment
currentLongitude += increment
if (currentLongitude > 179.500){
currentLongitude = -179.000
}
}
if (currentLongitude > -95.100) {
currentLongitude += increment
currentLongitude += increment
}
if (currentLatitude >= -39.000 && currentLatitude <= 37.0000){
currentLatitude += increment
currentLatitude += increment
}
if (currentLatitude >= 37.04999999999998 && currentLongitude >= -95.100){
australia = false
southAmerica = false
europe = false
america = true
currentLatitude = 37.0000
currentLongitude = -95.000
sleep(5)
}
}
region.center.longitude = currentLongitude
region.center.latitude = currentLatitude
centerLocation.latitude = currentLatitude
centerLocation.longitude = currentLongitude
}
}
var body: some View {
SwiftUIMapView(centerLocation: $centerLocation)
.onAppear {
DispatchQueue.main.async {
moveRegion()
}
}
}
}
非常感谢您的任何帮助,谢谢!
解决方案
使用Timer
它并尝试invalidate
重新触发等将很困难(或者实际上是不可能的——Timer
一旦失效就无法重新触发)。但是,对于使用 Combine 的发布者来说,这似乎是一个很好的机会Timer
,然后只给它一些时间间隔来等待——发布者仍然会触发事件,它只是在下一个等待间隔结束之前不对它们做任何事情打:
struct ContentView : View {
var body: some View {
MapView()
}
}
class LocationManager : ObservableObject {
@Published var centerLocation = CLLocationCoordinate2D()
@Published var region : MKCoordinateRegion
private var currentLatitude : CLLocationDegrees
private var currentLongitude : CLLocationDegrees
init() {
let region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 37.0000, longitude: -95.000),
span: MKCoordinateSpan(latitudeDelta: 40, longitudeDelta: 40)
)
currentLatitude = region.center.latitude
currentLongitude = region.center.longitude
self.region = region
}
private var cancellable : AnyCancellable?
private var waitUntil : Date?
private var currentFireDate : Date = Date()
func start() {
cancellable = Timer.publish(every: (1.0/30.0), on: .main, in: .default)
.autoconnect()
.sink { val in
if let waitUntil = self.waitUntil {
if val < waitUntil {
return
} else {
self.waitUntil = nil
}
}
self.currentFireDate = val
self.moveRegion()
}
}
let increment = 0.25
var southAmerica = false
var europe = false
var australia = false
var america = true
func moveRegion() {
//AMERICA CORD 37.0000, -95.000
//MOVING TO SOUTH AMERICA
//SOUTH AMERICA CORD -33.000, -70.000
if (america == true && southAmerica == false && europe == false && australia == false){
if (currentLatitude <= -33.000 && currentLatitude >= 37.0000 || currentLongitude <= -70.0000 && currentLongitude >= -95.0000 || currentLongitude >= -101.69999999998991) {
currentLatitude -= increment
currentLatitude -= increment
currentLatitude -= increment
if currentLongitude < -70.00 {
currentLongitude += increment
}
}
if (currentLatitude == -38.15000000000002 || currentLatitude <= -38.150000000000006 && currentLongitude <= -69.95000000000142){
waitUntil = currentFireDate.addingTimeInterval(5.0)
southAmerica = true
australia = false
america = false
europe = false
}
}
//MOVING TO EUROPE
//Europe CORD 48.000, 15.000
//if (currentLongitude > -74.00 && currentLongitude < -4.00)
if (southAmerica == true && australia == false && america == false && europe == false ){
if (currentLatitude > -39.000 && currentLatitude < 55.000 || currentLongitude > -70.00 && currentLongitude < 16.000) {
currentLongitude += increment
currentLongitude += increment
currentLatitude += increment
currentLatitude += increment
}
if (currentLatitude >= 48.04999999999936 || currentLatitude == 48.04999999999937 && currentLongitude <= 16.249999999996835){
waitUntil = currentFireDate.addingTimeInterval(5.0)
europe = true
southAmerica = false
australia = false
america = false
}
}
//MOVING TO AUSTRALIA
//AUSTRALIA CORD -36.000, 133.000
if (europe == true && southAmerica == false && australia == false && america == false){
if (currentLongitude > 9.00 && currentLongitude < 133.000) {
currentLongitude += increment
currentLongitude += increment
currentLongitude += increment
}
if (currentLatitude >= -38.000 && currentLatitude <= 49.0000){
currentLatitude -= increment
currentLatitude -= increment
}
if (currentLatitude <= -37.05000000000008 && currentLongitude >= 132.04999999999274){
waitUntil = currentFireDate.addingTimeInterval(5.0)
australia = true
southAmerica = false
europe = false
america = false
}
}
//MOVING TO AMERIA
if (australia == true && southAmerica == false && america == false && europe == false){
if (currentLongitude < 179.55){
currentLongitude += increment
currentLongitude += increment
if (currentLongitude > 179.500){
currentLongitude = -179.000
}
}
if (currentLongitude > -95.100) {
currentLongitude += increment
currentLongitude += increment
}
if (currentLatitude >= -39.000 && currentLatitude <= 37.0000){
currentLatitude += increment
currentLatitude += increment
}
if (currentLatitude >= 37.04999999999998 && currentLongitude >= -95.100){
australia = false
southAmerica = false
europe = false
america = true
currentLatitude = 37.0000
currentLongitude = -95.000
waitUntil = currentFireDate.addingTimeInterval(5.0)
}
}
region.center.longitude = currentLongitude
region.center.latitude = currentLatitude
centerLocation.latitude = currentLatitude
centerLocation.longitude = currentLongitude
}
}
struct MapView: View {
@ObservedObject var locationManager = LocationManager()
var body: some View {
SwiftUIMapView(centerLocation: $locationManager.centerLocation)
.onAppear {
locationManager.start()
}
}
}
struct SwiftUIMapView : UIViewRepresentable {
@Binding var centerLocation : CLLocationCoordinate2D
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
uiView.centerCoordinate = centerLocation
}
}
这是发生的事情:
- 位置现在由 ObservableObject LocationManager 控制
- On
start()
,每 1/30 秒触发一次的发布者被触发 - 每次火灾时,它都会呼唤
moveLocation()
- 当
moveLocation
想要暂停时,它设置waitUntil
在发布者触发的最后日期之前 5 秒 - 如果发布者被解雇并且时间在之前
waitUntil
,它只是返回并等待下一次解雇
推荐阅读
- bash - bash getopts循环不迭代
- tensorflow - 如何构建 tensorflow 基准测试工具
- jquery - 使用jquery重新加载页面时如何保持复选框状态
- python - 用熊猫分箱
- javascript - React:如何在 ReactJS 中以 asc 和 desc 对数据进行排序
- python - 如何通过python发送带有标头的GET请求
- java - 具有多个子报表的 Jasper 报表
- php - 在自定义请求 Laravel 中获取模型实例
- javascript - 滚动到元素时猫头鹰轮播自动播放不起作用
- python - 从 python 字典中获取所有子键的列表