我正在尝试使用Core Location获取CLRegionState来更新SwiftUI应用程序中的元素。我正在使用XCode 11 beta 6,并且在我的设备上安装了iOS 13 beta 7。
我可以看到两个问题:
应用程序崩溃,并且错误线程1:EXC_BAD_ACCESS出现在第147行(... ScrollView {...)
CLRegionState永远不会被调用或不会更新。
我基于Paul Hudson的SwiftUI Beacon Detector教程(我也无法使它工作),并将其修改为使用CLRegionState而不是信标接近度。
代码如下:
import SwiftUI
import CoreLocation
import Combine
class MYLocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager?
var willChange = PassthroughSubject<Void, Never>()
var lastRegionState = CLRegionState.unknown
override init() {
super.init()
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.requestWhenInUseAuthorization()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAuthorization()
}
func update(state: CLRegionState) {
lastRegionState = state
willChange.send(())
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
print("Your location is \(location)")
update(state: .unknown)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
func startScanning() {
// temporary coordinates
var workCoordinates: CLLocationCoordinate2D {
return CLLocationCoordinate2D(
latitude: 43.486525,
longitude: -11.912542)
}
var homeCoordinates = CLLocationCoordinate2D(
latitude: 43.499541,
longitude: -11.875079)
let workRegion: CLCircularRegion = CLCircularRegion(center: workCoordinates, radius: 100, identifier: "Work")
let homeRegion: CLCircularRegion = CLCircularRegion(center: homeCoordinates, radius: 100, identifier: "Home")
locationManager!.startMonitoring(for: workRegion)
locationManager!.startMonitoring(for: homeRegion)
locationManager!.requestState(for: workRegion)
locationManager!.requestState(for: homeRegion)
}
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
switch state {
case .inside:
switch region.identifier {
case "Work":
print("You are at work")
case "Home":
print("You are at home")
default:
print("unknown")
}
default:
break
}
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
switch region.identifier {
case "Work":
print("Work**********")
//self.taskTypeSegCtrl.selectedSegmentIndex = 0
case "Home":
print("Home*********8")
//self.taskTypeSegCtrl.selectedSegmentIndex = 1
default:
break
}
}
func checkLocationAuthorization() {
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
startScanning()
break
case .authorizedAlways:
startScanning()
break
case .denied:
// show an alert instructing them howto turn on permissions
break
case .notDetermined:
print("Location authorization is not determined.")
locationManager!.requestAlwaysAuthorization()
break
case .restricted:
break
@unknown default:
fatalError()
}
}
}
struct ContentView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(entity: Task.entity(),
sortDescriptors: [NSSortDescriptor(
keyPath: \Task.name, ascending: true)])
var tasks: FetchedResults<Task>
var locationManager = CLLocationManager()
@ObservedObject var location: MYLocationManager = MYLocationManager()
@State private var taskName = ""
@State private var taskType = 0
@State private var selectedTask = ""
@State private var numberOfTaps = 0
@State private var regionState = CLRegionState.unknown
var body: some View {
ScrollView {
VStack {
TextField("Enter a task name", text: $taskName)
.textFieldStyle(RoundedBorderTextFieldStyle())
Picker(selection: $taskType, label: Text("Task type")) {
Text("Work").tag(1)
Text("Home").tag(2)
}.pickerStyle(SegmentedPickerStyle())
Text(selectedTask)
Button(action: {
let task = Task(context: self.managedObjectContext)
task.name = self.taskName
task.type = Int16(self.taskType)
do {
try self.managedObjectContext.save()
} catch {
// handle the Core Data error
}
self.taskName = ""
}) {
Text("Save Task")
}.padding()
Button(action: {
if self.numberOfTaps < self.tasks.count {
let task = self.tasks[self.numberOfTaps].name
self.selectedTask = task ?? "No task..."
self.numberOfTaps = self.numberOfTaps + 1
} else {
self.selectedTask = "No more tasks! Have a wonderful day."
}
}) {
Text("Next Task")
}
List {
ForEach(tasks, id: \.self) {
task in
VStack(alignment: .leading, spacing: 6) {
Text(task.name ?? "Unknown")
.font(.headline)
Text("Task type \(task.type)")
.font(.caption)
}
}.onDelete(perform: removeTask)
}
} .frame(width: 300, height: 400, alignment: .top)
.padding()
.border(Color.black)
if regionState == .inside {
Text("inside")
} else if regionState == .outside {
Text("outside")
} else {
Text("unknown")
}
Spacer()
}
}
func removeTask(at offsets: IndexSet) {
for index in offsets {
let task = tasks[index]
managedObjectContext.delete(task)
do {
try managedObjectContext.save()
} catch {
// handle the Core Data error
}
}
}
func showTask(at offsets: IndexSet) {
for index in offsets {
let task = tasks[index]
selectedTask = task.name ?? "No task..."
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
实施Fabian所做的更改后,以下是控制台日志的内容:
已授予:true 2019-08-22 14:30:07.051062-0600 AppName [4452:2089841] locationManager(_管理器:CLLocationManager,didChangeAuthorization状态:CLAuthorizationStatus) 2019-08-22 14:30:07.052803-0600 New1Thing [4452:2089841] startScanning 2019-08-22 14:30:07.054319-0600 New1Thing [4452:2089841]当前位置:<+ .49945068,-* .87504490> +/- 65.00m(速度-1.00 mps /课程-1.00)@ 18/8/22,2:30:07 PM ****夏令时间
答案 0 :(得分:1)
这是一个完整的工作示例。我解决了几个问题。
ObservableObject
现在可以与objectWillChange
一起使用,而不是willChange
。我之前认为更新部分尚未完成
import SwiftUI
import CoreLocation
import Combine
import CoreData
import os
class MYLocationManager: NSObject, ObservableObject {
var locationManager: CLLocationManager?
var objectWillChange = PassthroughSubject<Void, Never>()
@Published var lastRegionState = CLRegionState.unknown {
willSet {
objectWillChange.send()
}
}
@Published var currentRegion: Region = .nowhereKnown {
willSet {
objectWillChange.send()
}
}
override init() {
super.init()
locationManager = CLLocationManager()
locationManager!.delegate = self
locationManager!.requestWhenInUseAuthorization()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
fatalError("error: \(error.localizedDescription)")
}
enum Region: String {
case work = "Work"
case home = "Home"
case nowhereKnown = "Nowhere Known"
}
func startScanning() {
os_log("startScanning")
// temporary coordinates
var workCoordinates: CLLocationCoordinate2D {
return CLLocationCoordinate2D(
latitude: 43.486525,
longitude: -11.912542)
}
var homeCoordinates = CLLocationCoordinate2D(
latitude: 43.499541,
longitude: -11.875079)
if let currentLocation = locationManager?.location {
os_log("Current location: %@", currentLocation.description)
homeCoordinates = currentLocation.coordinate
} else {
os_log("Current location: failed")
}
let workRegion: CLCircularRegion = CLCircularRegion(center: workCoordinates, radius: 100, identifier: Region.work.rawValue)
let homeRegion: CLCircularRegion = CLCircularRegion(center: homeCoordinates, radius: 100, identifier: Region.home.rawValue)
locationManager!.startMonitoring(for: workRegion)
locationManager!.startMonitoring(for: homeRegion)
locationManager!.requestState(for: workRegion)
locationManager!.requestState(for: homeRegion)
}
}
// MARK: Authorization
extension MYLocationManager {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
os_log("locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus)")
checkLocationAuthorization()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
os_log("locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])")
guard let location = locations.last else { return }
print("Your location is \(location)")
update(state: .unknown)
}
func checkLocationAuthorization() {
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
startScanning()
break
case .authorizedAlways:
startScanning()
break
case .denied:
// show an alert instructing them howto turn on permissions
break
case .notDetermined:
print("Location authorization is not determined.")
locationManager!.requestAlwaysAuthorization()
break
case .restricted:
break
@unknown default:
fatalError()
}
}
}
// MARK: UI Updates
extension MYLocationManager: CLLocationManagerDelegate {
func updateCurrentRegion(region: CLRegion) {
guard let region = Region(rawValue: region.identifier) else {
currentRegion = .nowhereKnown
return
}
currentRegion = region
}
func update(state: CLRegionState) {
lastRegionState = state
}
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
self.lastRegionState = state
updateCurrentRegion(region: region)
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
updateCurrentRegion(region: region)
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
updateCurrentRegion(region: region)
}
}
struct CoreLocationView: View {
private static func makeContainer() -> NSPersistentContainer {
let store = NSPersistentContainer(name: "CoreLocationView")
store.loadPersistentStores { (desc, err) in
if let err = err {
fatalError("core data error: \(err)")
}
}
return store
}
let container: NSPersistentContainer
init() {
self.container = CoreLocationView.makeContainer()
}
var body: some View {
CoreLocationView_NeedsEnv().environment(\.managedObjectContext, container.viewContext)
}
}
struct CoreLocationView_NeedsEnv: View {
@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(entity: Task.entity(),
sortDescriptors: [NSSortDescriptor(
keyPath: \Task.name, ascending: true)])
var tasks: FetchedResults<Task>
var locationManager = CLLocationManager()
@ObservedObject var location: MYLocationManager = MYLocationManager()
@State private var taskName = ""
@State private var taskType = 0
@State private var selectedTask = ""
@State private var numberOfTaps = 0
//@State private var regionState = CLRegionState.unknown
var body: some View {
ScrollView {
VStack {
TextField("Enter a task name", text: $taskName)
.textFieldStyle(RoundedBorderTextFieldStyle())
Picker(selection: $taskType, label: Text("Task type")) {
Text("Work").tag(1)
Text("Home").tag(2)
}.pickerStyle(SegmentedPickerStyle())
Text(selectedTask)
Button(action: {
let task = Task(context: self.managedObjectContext)
task.name = self.taskName
task.type = Int16(self.taskType)
do {
try self.managedObjectContext.save()
} catch {
// handle the Core Data error
}
self.taskName = ""
}) {
Text("Save Task")
}.padding()
Button(action: {
if self.numberOfTaps < self.tasks.count {
let task = self.tasks[self.numberOfTaps].name
self.selectedTask = task ?? "No task..."
self.numberOfTaps = self.numberOfTaps + 1
} else {
self.selectedTask = "No more tasks! Have a wonderful day."
}
}) {
Text("Next Task")
}
List {
ForEach(tasks, id: \.self) {
task in
VStack(alignment: .leading, spacing: 6) {
Text(task.name ?? "Unknown")
.font(.headline)
Text("Task type \(task.type)")
.font(.caption)
}
}.onDelete(perform: removeTask)
}
} .frame(width: 300, height: 400, alignment: .top)
.padding()
.border(Color.black)
if location.lastRegionState == .inside {
Text("inside")
} else if location.lastRegionState == .outside {
Text("outside")
} else {
Text("unknown")
}
Text("Where am I: \(location.currentRegion.rawValue)")
Spacer()
}
}
func removeTask(at offsets: IndexSet) {
for index in offsets {
let task = tasks[index]
managedObjectContext.delete(task)
do {
try managedObjectContext.save()
} catch {
// handle the Core Data error
}
}
}
func showTask(at offsets: IndexSet) {
for index in offsets {
let task = tasks[index]
selectedTask = task.name ?? "No task..."
}
}
}
答案 1 :(得分:0)
首先,我要感谢Fabian和graycampbell的帮助。
第二,据我所知@ObservableObject在使用XCode 11 beta 6的iOS 13 beta 8中仍然无法正常工作。
这对我有用: 1.我更改了
@ObservedObject var location: MYLocationManager = MYLocationManager()
收件人:
@EnvironmentObject var location: MYLocationManager
2。在SceneDelegate中,我添加了:
let myLocationManager = MYLocationManager()
和:
window.rootViewController = UIHostingController(rootView: CoreLocationView_NeedsEnv()
.environmentObject(myLocationManager)
不再崩溃!
P.S。我正在使用Fabian的更新代码。再次感谢!