gpt4 book ai didi

ios - 如何使用 SwiftUI 打开通知操作的自定义 View ?

转载 作者:行者123 更新时间:2023-12-05 09:35:05 27 4
gpt4 key购买 nike

我正在尝试获取打开自定义 View 的通知操作。我的通知基本上是新闻,我希望用户在点击“阅读通知”操作时转到显示简单文本的页面(出于这个问题的目的)。我已经尝试了很多教程,但它们都使用了一些现有的 View ,例如“imagePicker”,这些 View 已经默认启用了大量的东西,我不知道我需要添加到我的自定义 View 中才能完成这项工作的所有东西.像 UIViewControllerRepresentable、协调器或任何我可能需要的东西。

这是我处理通知的主要 swift 文件。(通知工作正常)文件末尾是 AppDelegate: UNUserNotificationCenterDelegate {} 的扩展名,该扩展名取 self 正在遵循的教程,它创建了一个 NewsItem,我也将在此处包含它。但由于我在 SwiftUI 而不是 UIKit 中工作,所以我无法按照我设法找到的任何教程来按照我想要的方式工作。

我包含了完整的应用程序委托(delegate)扩展和 newsItem 只是为了使代码可编译,但我将需要更改的部分放在注释 block 中。

import SwiftUI
import UserNotifications
enum Identifiers {
static let viewAction = "VIEW_IDENTIFIER"
static let readableCategory = "READABLE"
}
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
TabView{
NavigationView{
ContentView()
}
.tabItem {
Label("Home", systemImage : "house")
}
}
}
}
}


class AppDelegate: NSObject, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self// set the delegate
registerForPushNotifications()
return true
}
func application( // registers for notifications and gets token
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("device token : \(token)")
}//handles sucessful register for notifications

func application( //handles unsucessful register for notifications
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
print("Failed to register: \(error)")
}//handles unsucessful register for notifications

func application( //handles notifications when app in foreground
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler:
@escaping (UIBackgroundFetchResult) -> Void
) {
guard let aps = userInfo["aps"] as? [String: AnyObject] else {
completionHandler(.failed)
return
}
print("new notification received")
}//handles notifications when app in foreground

func registerForPushNotifications() {
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, _ in
print("permission granted: \(granted)")
guard granted else { return }
let viewAction = UNNotificationAction(
identifier: Identifiers.viewAction,
title: "Mark as read",
options: [.foreground])

let readableNotification = UNNotificationCategory(
identifier: Identifiers.readable,
actions: [viewAction2],
intentIdentifiers: [],
options: [])
UNUserNotificationCenter.current().setNotificationCategories([readableNotification])
self?.getNotificationSettings()
}
}

func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
print("notification settings: \(settings)")
}
}
}

// MARK: - UNUserNotificationCenterDelegate

extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification.request.content.userInfo

if let aps = userInfo["aps"] as? [String: AnyObject],
let newsItem = NewsItem.makeNewsItem(aps) {
(window?.rootViewController as? UITabBarController)?.selectedIndex = 1

if response.actionIdentifier == Identifiers.viewAction,
let url = URL(string: newsItem.link) {
let safari = SFSafariViewController(url: url)
window?.rootViewController?.present(safari, animated: true, completion: nil)
}
}

completionHandler()
}
}

// MARK: - UNUserNotificationCenterDelegate

extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification.request.content.userInfo

if let aps = userInfo["aps"] as? [String: AnyObject],
/*
let newsItem = NewsItem.makeNewsItem(aps) {
(window?.rootViewController as? UITabBarController)?.selectedIndex = 1
if response.actionIdentifier == Identifiers.viewAction,
let url = URL(string: newsItem.link) {
let safari = SFSafariViewController(url: url)
window?.rootViewController?.present(safari, animated: true, completion: nil)
}
}
*/

completionHandler()
}
}

这是教程中的 NewsItem.swift 以防万一,但这是一个我不需要或不想使用的文件。

import Foundation

struct NewsItem: Codable {
let title: String
let date: Date
let link: String

@discardableResult
static func makeNewsItem(_ notification: [String: AnyObject]) -> NewsItem? {
guard
let news = notification["alert"] as? String,
let url = notification["link_url"] as? String
else {
return nil
}

let newsItem = NewsItem(title: news, date: Date(), link: url)
let newsStore = NewsStore.shared
newsStore.add(item: newsItem)

NotificationCenter.default.post(
name: NewsFeedTableViewController.refreshNewsFeedNotification,
object: self)

return newsItem
}
}

简化的 ContentView

import SwiftUI
struct ContentView: View {
var body: some View {
VStack{
Text(DataForApp.welcomeText)
.font(.title)
.bold()
.multilineTextAlignment(.center)
.foregroundColor(.secondary)
.shadow(radius: 8 )
} .navigationTitle("My Mobile App")
}
}

现在我的目标是使用此 MyView,一旦用户点击“标记为已读”操作,我希望显示此 View 。

import SwiftUI
struct MyView: View {
var body: some View {
Text("Notification text here")
}
}

显然 MyView 不包含任何它需要的东西,但我不想在这里发布我尝试过的代码,因为我尝试了 200 种不同的东西,因为它们都不起作用,我意识到我什至没有接近正确的轨道。

最佳答案

我通过使用我创建的名为 NotificationManager 的 @ObservableObject 解决了这个问题——它存储了最新通知的文本(如果你愿意,你可以扩展它来存储一个数组) 提供一个绑定(bind)来告诉应用程序是否根据是否有要显示的通知在堆栈中显示新 View 。

NotificationManager 必须是 ContentView 上的 @ObservedObject 才能正常工作,因为 ContentView 需要监视 currentNotificationText 状态的变化>,这是一个@Published 属性。

ContentView 有一个不可见的 NavigationLink(通过 .overlayEmptyView),它只在有通知的事件。

在 App Delegate 方法中,我只是将通知传递给一个简单的函数 handleNotification,该函数解析 aps 并将生成的 String 放入在 NotificationManager 中。您还可以使用更强大的功能轻松增强它,包括解析 aps

中的其他字段

import SwiftUI
import UserNotifications

//new class to store notification text and to tell the NavigationView to go to a new page
class NotificationManager : ObservableObject {
@Published var currentNotificationText : String?

var navigationBindingActive : Binding<Bool> {
.init { () -> Bool in
self.currentNotificationText != nil
} set: { (newValue) in
if !newValue { self.currentNotificationText = nil }
}

}
}

enum Identifiers {
static let viewAction = "VIEW_IDENTIFIER"
static let readableCategory = "READABLE"
}

@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

var body: some Scene {
WindowGroup {
TabView{
NavigationView{
ContentView(notificationManager: appDelegate.notificationManager) //pass the notificationManager as a dependency
}
.tabItem {
Label("Home", systemImage : "house")
}
}
}
}
}


class AppDelegate: NSObject, UIApplicationDelegate {
var notificationManager = NotificationManager() //here's where notificationManager is stored

var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self// set the delegate
registerForPushNotifications()
return true
}
func application( // registers for notifications and gets token
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("device token : \(token)")
}//handles sucessful register for notifications

func application( //handles unsucessful register for notifications
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
print("Failed to register: \(error)")
}//handles unsucessful register for notifications

func application( //handles notifications when app in foreground
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler:
@escaping (UIBackgroundFetchResult) -> Void
) {
guard let aps = userInfo["aps"] as? [String: AnyObject] else {
completionHandler(.failed)
return
}
print("new notification received")
handleNotification(aps: aps)
completionHandler(.noData)
}//handles notifications when app in foreground

func registerForPushNotifications() {
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, _ in
print("permission granted: \(granted)")
guard granted else { return }
let viewAction = UNNotificationAction(
identifier: Identifiers.viewAction,
title: "Mark as read",
options: [.foreground])

let readableNotification = UNNotificationCategory(
identifier: Identifiers.readableCategory,
actions: [viewAction],
intentIdentifiers: [],
options: [])
UNUserNotificationCenter.current().setNotificationCategories([readableNotification])
self?.getNotificationSettings()
}
}

func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
print("notification settings: \(settings)")
}
}
}

// MARK: - UNUserNotificationCenterDelegate

extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification.request.content.userInfo
if let aps = userInfo["aps"] as? [String: AnyObject] {
handleNotification(aps: aps)
}
}
}

extension AppDelegate {
@discardableResult func handleNotification(aps: [String:Any]) -> Bool {

guard let alert = aps["alert"] as? String else { //get the "alert" field
return false
}
self.notificationManager.currentNotificationText = alert
return true
}
}

struct ContentView: View {
@ObservedObject var notificationManager : NotificationManager

var body: some View {
VStack{
Text("Welcome")
.font(.title)
.bold()
.multilineTextAlignment(.center)
.foregroundColor(.secondary)
.shadow(radius: 8 )
}
.navigationTitle("My Mobile App")
.overlay(NavigationLink(destination: MyView(text: notificationManager.currentNotificationText ?? ""), isActive: notificationManager.navigationBindingActive, label: {
EmptyView()
}))
}
}

struct MyView: View {
var text : String

var body: some View {
Text(text)
}
}

(我不得不用你的问题中的原始代码修复一些拼写错误/编译错误,所以请确保如果你使用它,你直接复制和粘贴以获得正确的方法签名等.)

关于ios - 如何使用 SwiftUI 打开通知操作的自定义 View ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66110013/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com