如何在当前位置设置 pin,将 pin 用作地理围栏,提醒用户 onEnter,然后全部重置/重新开始?
Posted
技术标签:
【中文标题】如何在当前位置设置 pin,将 pin 用作地理围栏,提醒用户 onEnter,然后全部重置/重新开始?【英文标题】:How do I set a pin on Current Location, use Pin as Geofence, alert user onEnter, then reset all / start over again? 【发布时间】:2021-07-03 19:05:54 【问题描述】:我是一名产品设计师,我试图提出一个基于位置的提醒概念来验证一个相当早期的想法。我可以在 Figma 中对所有这些屏幕进行原型制作,但实时位置和上下文警报是我假设的基础,所以我在这里竭尽全力尝试在 SwiftUI 中实现这一点????
https://www.figma.com/proto/jxYwPbqe4oqsXBbf6CZDDi/Location-Reminder-Spec?page-id=0%3A1&node-id=1%3A189&viewport=161%2C384%2C0.12701110541820526&scaling=scale-down
我一直在从各种教程(Ray Wenderlich、Hacking Swift、Apple 的 Swift UI)和关于 Location 的 sn-ps 等中拼凑起来,但大多数教程都有更多的对象和视图以及其他我不知道的东西'不需要这使我斗志昂扬的 MVP 拼贴方法变得复杂。
到目前为止,我的 MKMapView 以我的用户位置坐标为中心,并显示了我的按钮。
我想知道
如何使用来自用户位置的坐标创建一个 CLCircularRegion?
如何使用来自用户位置的坐标在地图上放置 MKMapAnnotation,以及如何在按下通知内的按钮后删除注释?
通过以上内容,我想我将能够理清如何触发通知并在 Enter 上显示通知。
到目前为止,我的项目中有 4 个文件...
LocationManager.swift
import Foundation
import MapKit
import CoreLocation
class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject
@Published var region = MKCoordinateRegion()
private let manager = CLLocationManager()
override init()
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
locations.last.map
region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: $0.coordinate.latitude, longitude: $0.coordinate.longitude),
span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
)
extension CLLocation
var latitude: Double
return self.coordinate.latitude
var longitude: Double
return self.coordinate.longitude
ContentView.swift
import CoreLocation
import MapKit
import SwiftUI
struct ContentView: View
@StateObject var manager = LocationManager()
@State private var locations = [MKPointAnnotation] ()
var body: some View
VStack
HStack
Text("Location Reminder")
.font(.largeTitle)
.bold()
.foregroundColor(.black)
Spacer()
.padding()
ZStack (alignment: .bottomTrailing)
Map(coordinateRegion: $manager.region,
showsUserLocation: true)
.ignoresSafeArea()
ReminderButton()
.padding()
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView()
ReminderButton.swift
import Foundation
import SwiftUI
struct ReminderButton: View
var body: some View
VStack
Spacer()
Button(action:
)
HStack
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 40, height: 40)
Text("Add a Reminder")
.padding()
.background(Color.white)
.cornerRadius(40)
.shadow(color: .gray, radius: 0.2, x: 1, y: 1)
我有一个名为 Geofence.swift 的空文件,我计划在其中创建一个 CLCircularRegion 并设置通知/警报 onEnter 等。
感谢所有帮助/建议/建议。如果有办法改进我的帖子和/或它是否属于其他地方,也请 LMK。
【问题讨论】:
【参考方案1】:import SwiftUI
import MapKit
import CoreLocation
struct SingleLocationView: View
@StateObject var manager = LocationManager()
@State private var locations = [PinnedLocation] ()
@State var userTrackingMode: MapUserTrackingMode = .follow
let radius: CGFloat = 160 //meters approx 0.1 miles
var body: some View
VStack
HStack
VStack
Text("Location Reminder")
.font(.largeTitle)
.bold()
.foregroundColor(.black)
Spacer()
.padding()
ZStack (alignment: .bottomTrailing)
GeometryReader geo in
Map(coordinateRegion: $manager.region, interactionModes: .all, showsUserLocation: true, userTrackingMode: $userTrackingMode, annotationItems: locations, annotationContent: annotation in
MapAnnotation(coordinate: annotation.coordinate, anchorPoint: CGPoint(x: 0.5, y: 0.5), content:
//Use this vs Circle because your map is a rectangle circumference is an oval
//Maybe a stretched Circle could be a little more accurate
RoundedRectangle(cornerRadius: 25)
.foregroundColor(.white)
.overlay(
Image(systemName: "mappin").foregroundColor(.red))
// The screen vertical and horizontal multiplied by your radius divided Map vertial/horizontal span in meters
.frame(width: (geo.size.width/manager.regionSpanInLongitudeDeltaMeters.value) * radius, height: (geo.size.height/manager.regionSpanInLatitudeDeltaMeters.value) * radius, alignment: .center)
)
)
.ignoresSafeArea()
ReminderButton(locations: $locations).environmentObject(manager)
.padding()
struct SingleLocationView_Previews: PreviewProvider
static var previews: some View
SingleLocationView()
//You MKAnnotaion is a Protocol you need to create your own class
//MKPointAnnotation is a MKShape for use with MKMapView
class PinnedLocation: NSObject, MKAnnotation, Identifiable
var title: String?
var subtitle: String?
var coordinate: CLLocationCoordinate2D
init(title: String? = nil, subtitle: String? = nil, coordinate: CLLocationCoordinate2D)
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
///Get the CLCircularRegion with the given radius
func getCLCircularRegion(radius: CGFloat) -> CLCircularRegion
CLCircularRegion(center: self.coordinate, radius: radius, identifier: "\(self.id)")
extension UnitLength
//This is approx do research on degrees to meters
static let degree = UnitLength(symbol: "degree", converter: UnitConverterLinear(coefficient: 111111))
class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject
@Published var region = MKCoordinateRegion()
//If you do it the way you were doing it get the most current location from here to append to your array
@Published var lastLocation: CLLocation = CLLocation()
///Region gives span in degrees. This is a rough conversion
var regionSpanInLatitudeDeltaMeters: Measurement<UnitLength>
Measurement(value: region.span.latitudeDelta, unit: UnitLength.degree).converted(to: .meters)
///Region gives span in degrees. This is a rough conversion
var regionSpanInLongitudeDeltaMeters: Measurement<UnitLength>
Measurement(value: region.span.longitudeDelta, unit: UnitLength.degree).converted(to: .meters)
private let manager = CLLocationManager()
override init()
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
//Don't need it SwiftUI does this for you
//manager.requestWhenInUseAuthorization()
//manager.startUpdatingLocation()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
locations.last.map
region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: $0.coordinate.latitude, longitude: $0.coordinate.longitude),
span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
)
lastLocation = locations.last!
extension CLLocationCoordinate2D
var annotation: PinnedLocation
PinnedLocation(coordinate: CLLocationCoordinate2D(latitude: self.latitude, longitude: self.longitude))
struct ReminderButton: View
//The button joins your location manager with the locations displayed. It needs access to both
@EnvironmentObject var manager: LocationManager
@Binding var locations: [PinnedLocation]
var body: some View
VStack
Spacer()
Button(action:
//Append the current center/location to your list of annotions
locations.append(manager.region.center.annotation)
)
HStack
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 40, height: 40)
Text("Add a Reminder")
.padding()
.background(Color.white)
.cornerRadius(40)
.shadow(color: .gray, radius: 0.2, x: 1, y: 1)
【讨论】:
以上是关于如何在当前位置设置 pin,将 pin 用作地理围栏,提醒用户 onEnter,然后全部重置/重新开始?的主要内容,如果未能解决你的问题,请参考以下文章
在当前用户位置放置 pin iphone mkmapview