CoreLocation框架詳細解析(十九) —— 基於UNLocationNotificationTrigger的位置通知(二) 版本記錄 前言 源碼 後記

版本記錄

版本號 時間
V1.0 2021.05.08 星期六

前言

很多的app都有定位功能,比如說滴滴,美團等,他們都需要獲取客戶所在的位置,並且根據位置推送不同的模塊數據以及服務,可以說,定位方便了我們的生活,接下來這幾篇我們就說一下定位框架CoreLocation。感興趣的可以看我寫的上面幾篇。
1. CoreLocation框架詳細解析 —— 基本概覽(一)
2. CoreLocation框架詳細解析 —— 選擇定位服務的授權級別(二)
3. CoreLocation框架詳細解析 —— 確定定位服務的可用性(三)
4. CoreLocation框架詳細解析 —— 獲取用戶位置(四)
5. CoreLocation框架詳細解析 —— 監控用戶與地理區域的距離(五)
6. CoreLocation框架詳細解析 —— 確定接近iBeacon(六)
7. CoreLocation框架詳細解析 —— 將iOS設備轉換爲iBeacon(七)
8. CoreLocation框架詳細解析 —— 獲取指向和路線信息(八)
9. CoreLocation框架詳細解析 —— 在座標和用戶友好的地名之間轉換(九)
10. CoreLocation框架詳細解析(十) —— 跟蹤訪問位置簡單示例(一)
11. CoreLocation框架詳細解析(十一) —— 跟蹤訪問位置簡單示例(二)
12. CoreLocation框架詳細解析(十二) —— 仿Runkeeper的簡單實現(一)
13. CoreLocation框架詳細解析(十三) —— 仿Runkeeper的簡單實現(二)
14. CoreLocation框架詳細解析(十四) —— 仿Runkeeper的簡單實現(三)
15. CoreLocation框架詳細解析(十五) —— 仿Runkeeper的簡單實現(四)
16. CoreLocation框架詳細解析(十六) —— 基於Core Location地理圍欄的實現(一)
17. CoreLocation框架詳細解析(十七) —— 基於Core Location地理圍欄的實現(二)
18. CoreLocation框架詳細解析(十八) —— 基於UNLocationNotificationTrigger的位置通知(一)

源碼

1. Swift

首先看下工程組織結構

下面就是源碼了

1. AppMain.swift
import SwiftUI

@main
struct AppMain: App {
  @StateObject private var locationManager = LocationManager()

  var body: some Scene {
    WindowGroup {
      ContentView()
        .environmentObject(locationManager)
    }
  }
}
2. ContentView.swift
import SwiftUI

struct ContentView: View {
  let takeOut = TakeOutStore()
  @EnvironmentObject private var locationManager: LocationManager

  var body: some View {
    NavigationView {
      MenuListView(takeOut: takeOut)
    }
    .navigationViewStyle(StackNavigationViewStyle())
    .alert(isPresented: $locationManager.didArriveAtTakeout) {
      Alert(
        title: Text("Check In"),
        message:
          Text("""
            You have arrived to collect your order.
            Do you want to check in?
            """),
        primaryButton: .default(Text("Yes")),
        secondaryButton: .default(Text("No"))
      )
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}
3. MenuListView.swift
import SwiftUI

struct MenuListView: View {
  var takeOut: TakeOutStore
  var menuItems: [MenuItem] {
    return takeOut.menu
  }

  var body: some View {
    List(menuItems, id: \.id) { item in
      NavigationLink(
        destination: DetailView(menuItem: item)) {
        MenuListRow(menuItem: item)
      }
    }
    .navigationTitle(takeOut.title)
  }
}

struct MenuListView_Previews: PreviewProvider {
  static var previews: some View {
    MenuListView(takeOut: TakeOutStore())
  }
}
4. MenuListRow.swift
import SwiftUI

struct MenuListRow: View {
  var menuItem: MenuItem

  var body: some View {
    HStack {
      Image(menuItem.imageName)
        .resizable()
        .frame(width: 50, height: 50)
      Text(menuItem.name)

      Spacer()
    }
  }
}

struct MenuListRow_Previews: PreviewProvider {
  static var previews: some View {
    MenuListRow(menuItem: menuItems[0])
      .previewLayout(.fixed(width: 300, height: 50))
  }
}
5. DetailView.swift
import SwiftUI

struct DetailView: View {
  @EnvironmentObject private var locationManager: LocationManager
  @State private var orderPlaced = false
  let menuItem: MenuItem
  var price: String {
    return "$" + String(format: "%.2f", menuItem.price)
  }

  var body: some View {
    VStack {
      Image(menuItem.imageName)
        .resizable()
        .frame(maxWidth: 300, maxHeight: 600)
        .aspectRatio(contentMode: .fit)
      Text(menuItem.name)
        .font(.headline)
      Divider()
      Text("Price: \(price)")
        .font(.subheadline)
        .padding(15)
      Button(action: placeOrder) {
        Text("Place Order: \(price)")
          .foregroundColor(.white)
          .frame(minWidth: 100, maxWidth: .infinity)
          .frame(height: 45)
      }
      .background(Color("rw-green"))
      .cornerRadius(3.0)
    }
    .alert(isPresented: $orderPlaced) {
      Alert(
        title: Text("Food Ordered"),
        message:
          Text("""
            Your food has been ordered.
            Would you like to be notified on arrival?
            """),
        primaryButton: .default(Text("Yes")) {
          requestNotification()
        },
        secondaryButton: .default(Text("No"))
      )
    }
    .padding()
    .navigationBarTitle(Text(menuItem.name), displayMode: .inline)
  }

  func placeOrder() {
    orderPlaced = true
  }

  func requestNotification() {
    locationManager.validateLocationAuthorizationStatus()
  }
}

struct DetailView_Previews: PreviewProvider {
  static var previews: some View {
    DetailView(menuItem: menuItems[0])
  }
}
6. SimulatedLocations.gpx
<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode">
  <wpt lat="37.422155" lon="-122.134751">
    <name>Apple Park</name>
    <time>2021-01-23T14:00:00Z</time>
  </wpt>
  <wpt lat="37.33182000" lon="-122.03118000">
    <name>Apple Campus</name>
    <time>2021-01-23T14:00:05Z</time>
  </wpt>
</gpx>
7. LocationManager.swift
import CoreLocation
import UserNotifications

class LocationManager: NSObject, ObservableObject {
  let location = CLLocationCoordinate2D(latitude: 37.33182, longitude: -122.03118)
  let notificationCenter = UNUserNotificationCenter.current()
  lazy var storeRegion = makeStoreRegion()
  @Published var didArriveAtTakeout = false
  // 1
  lazy var locationManager = makeLocationManager()
  // 2
  private func makeLocationManager() -> CLLocationManager {
    // 3
    let manager = CLLocationManager()
    manager.allowsBackgroundLocationUpdates = true
    // 4
    return manager
  }

  // 1
  private func makeStoreRegion() -> CLCircularRegion {
    // 2
    let region = CLCircularRegion(
      center: location,
      radius: 2,
      identifier: UUID().uuidString)
    // 3
    region.notifyOnEntry = true
    // 4
    return region
  }

  // 1
  func validateLocationAuthorizationStatus() {
    // 2
    switch locationManager.authorizationStatus {
    // 3
    case .notDetermined, .denied, .restricted:
      // 4
      print("Location Services Not Authorized")
      locationManager.requestWhenInUseAuthorization()
      requestNotificationAuthorization()

    // 5
    case .authorizedWhenInUse, .authorizedAlways:
      // 6
      print("Location Services Authorized")
      requestNotificationAuthorization()

    default:
      break
    }
  }

  // 1
  private func requestNotificationAuthorization() {
    // 2
    let options: UNAuthorizationOptions = [.sound, .alert]
    // 3
    notificationCenter
      .requestAuthorization(options: options) { [weak self] result, _ in
        // 4
        print("Auth Request result: \(result)")
        if result {
          self?.registerNotification()
        }
      }
  }

  // 1
  private func registerNotification() {
    // 2
    let notificationContent = UNMutableNotificationContent()
    notificationContent.title = "Welcome to Swifty TakeOut"
    notificationContent.body = "Your order will be ready shortly."
    notificationContent.sound = .default

    // 3
    let trigger = UNLocationNotificationTrigger(region: storeRegion, repeats: false)

    // 4
    let request = UNNotificationRequest(
      identifier: UUID().uuidString,
      content: notificationContent,
      trigger: trigger)

    // 5
    notificationCenter
      .add(request) { error in
        if error != nil {
          print("Error: \(String(describing: error))")
        }
      }
  }

  // 1
  override init() {
    super.init()
    // 2
    notificationCenter.delegate = self
  }
}
extension LocationManager: UNUserNotificationCenterDelegate {
  // 1
  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void
  ) {
    // 2
    print("Received Notification")
    didArriveAtTakeout = true
    // 3
    completionHandler()
  }

  // 4
  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    willPresent notification: UNNotification,
    withCompletionHandler completionHandler:
      @escaping (UNNotificationPresentationOptions) -> Void
  ) {
    // 5
    print("Received Notification in Foreground")
    didArriveAtTakeout = true
    // 6
    completionHandler(.sound)
  }
}
8. TakeOutStore.swift
import Foundation

struct TakeOutStore {
  let menu = menuItems
  var title = "Swifty TakeOut"
}
9. MenuItem.swift
import Foundation

struct MenuItem {
  let id = UUID()
  var name: String
  var imageName: String
  var price: Double
}

// MARK: - Menu Items
let menuItems: [MenuItem] = [
  MenuItem(name: "Margherita Pizza SE", imageName: "cheese-pizza-small", price: 7.00),
  MenuItem(name: "Margherita Pizza Max", imageName: "cheese-pizza", price: 10.00),
  MenuItem(name: "Margherita Pizza Max Pro", imageName: "cheese-pizza", price: 12.00),
  MenuItem(name: "Chicken Pizza SE", imageName: "chicken-pizza-small", price: 8.00),
  MenuItem(name: "Chicken Pizza Max", imageName: "chicken-pizza", price: 11.00),
  MenuItem(name: "Chicken Pizza Max Pro", imageName: "chicken-pizza", price: 14.00),
  MenuItem(name: "Pepperoni Pizza SE", imageName: "pep-pizza-small", price: 7.00),
  MenuItem(name: "Pepperoni Pizza Max", imageName: "pep-pizza", price: 11.00),
  MenuItem(name: "Pepperoni Pizza Max Pro", imageName: "pep-pizza", price: 14.00),
  MenuItem(name: "Swifty Shake Series 6", imageName: "shake", price: 3.50),
  MenuItem(name: "Swifty Shake SE", imageName: "shake", price: 2.00),
  MenuItem(name: "Swifty Shake Series 3", imageName: "shake", price: 2.75)
]

後記

本篇主要講述了基於UNLocationNotificationTrigger的位置通知,感興趣的給個贊或者關注~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章