Swift @State更改后,SwfitUI元素不会更改
说到SwiftUI,我是个新手。我试图构建的是入职屏幕。我目前正在使用的其中一个屏幕是,当我需要向用户请求一系列隐私信息时,比如访问相机、苹果照片库、麦克风、位置和通知。 和 现在,我开始运行应用程序并单击蓝色的“允许”按钮开始请求用户的数据。 当涉及到请求位置和通知数据时,问题开始出现。首先,我做了一个选择,UI元素[checkmark]没有显示出来,这是我试图解决的第一个问题。但是当我再次点击蓝色的Allow按钮时,它同时显示了两个复选标记(用于位置和通知),这是第二个问题。类似于通知复选标记的相同问题,我需要再次单击蓝色的“允许”按钮 请帮我解决这些问题,因为我尽了全力。此外,以下是执行此操作的所有代码:Swift @State更改后,SwfitUI元素不会更改,swift,swiftui,Swift,Swiftui,说到SwiftUI,我是个新手。我试图构建的是入职屏幕。我目前正在使用的其中一个屏幕是,当我需要向用户请求一系列隐私信息时,比如访问相机、苹果照片库、麦克风、位置和通知。 和 现在,我开始运行应用程序并单击蓝色的“允许”按钮开始请求用户的数据。 当涉及到请求位置和通知数据时,问题开始出现。首先,我做了一个选择,UI元素[checkmark]没有显示出来,这是我试图解决的第一个问题。但是当我再次点击蓝色的Allow按钮时,它同时显示了两个复选标记(用于位置和通知),这是第二个问题。类似于通知复
import SwiftUI
import AVFoundation
import Photos
import CoreLocation
import UserNotifications
struct ContentView: View {
@AppStorage("onBoardingViewed") var hasOnboarded = false
private var locationManager = CLLocationManager()
// MARK: - UI States
@State private var isAllowedCamera = false
@State private var isAllowedPhotoLibrary = false
@State private var isAllowedMicrophone = false
@State private var isAllowedLocation = false
@State private var isAllowedNotification = false
@State private var isHiddenCam = true
@State private var isHiddenPL = true
@State private var isHiddenMic = true
@State private var isHiddenLoc = true
@State private var isHiddenNot = true
@State private var isFinished = false
// MARK: - UI
var body: some View {
VStack(spacing: 60) {
Text("Allow Access")
.font(.system(.largeTitle, design: .rounded))
.fontWeight(.bold)
HStack(spacing: 20) {
VStack(alignment: .center, spacing: 20) {
Image(systemName: isAllowedCamera ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isAllowedCamera ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(isHiddenCam ? 0 : 1)
.animation(.spring())
Image(systemName: isAllowedPhotoLibrary ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isAllowedPhotoLibrary ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(isHiddenPL ? 0 : 1)
.animation(.spring())
Image(systemName: isAllowedMicrophone ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isAllowedMicrophone ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(isHiddenMic ? 0 : 1)
.animation(.spring())
Image(systemName: isAllowedLocation ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isAllowedLocation ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(isHiddenLoc ? 0 : 1)
.animation(.spring())
Image(systemName: isAllowedNotification ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isAllowedNotification ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(isHiddenLoc ? 0 : 1)
.animation(.spring())
}
VStack(alignment: .leading, spacing: 20) {
Text("Camera permission")
.font(.title)
.fontWeight(.light)
Text("Photos permission")
.font(.title)
.fontWeight(.light)
Text("Microphone permission")
.font(.title)
.fontWeight(.light)
Text("Location permission")
.font(.title)
.fontWeight(.light)
Text("Notification permission")
.font(.title)
.fontWeight(.light)
}
}
// MARK: - Action
Button {
reqestCamera { (success) in
if success {
isAllowedCamera = true
} else {
isAllowedCamera = false
}
isHiddenCam = false
}
reqestPhotoLibrary { (success) in
if success {
isAllowedPhotoLibrary = true
} else {
isAllowedPhotoLibrary = false
}
isHiddenPL = false
}
reqestMicrophone { (success) in
if success {
isAllowedMicrophone = true
} else {
isAllowedMicrophone = false
}
isHiddenMic = false
reqestLocation { (success) in
if success == true {
isAllowedLocation = true
print("Location ON")
} else {
isAllowedLocation = false
print("Location OFF")
}
isHiddenLoc = false
reqestNotifications { (success) in
if success == true {
isAllowedNotification = true
} else {
isAllowedNotification = false
}
isHiddenNot = false
isFinished = true
}
}
}
if isFinished == true {
withAnimation {
hasOnboarded = true
}
}
} label: {
Text(isFinished ? "START" : "ALLOW")
.font(.custom("SF-Compact-Rounded-Medium", size: 20))
.foregroundColor(.white)
.padding()
.frame(width: 250, height: 50)
.background(Color.blue)
.cornerRadius(5)
}
}
}
// MARK: - Request access methods
private func reqestCamera(completion: @escaping (Bool) -> Void) {
AVCaptureDevice.requestAccess(for: .video, completionHandler: completion)
}
private func reqestPhotoLibrary(completion: @escaping (Bool) -> Void) {
PHPhotoLibrary.requestAuthorization(for: .addOnly) { (status) in
switch status {
case .authorized:
completion(true)
case .limited:
completion(true)
case .notDetermined:
completion(false)
case .restricted:
completion(false)
case .denied:
completion(false)
@unknown default:
completion(false)
}
}
}
private func reqestMicrophone(completion: @escaping (Bool) -> Void) {
AVCaptureDevice.requestAccess(for: .audio, completionHandler: completion)
}
private func reqestLocation(completion: @escaping (Bool) -> Void) {
let status = locationManager.authorizationStatus
DispatchQueue.main.async {
locationManager.requestWhenInUseAuthorization()
if status == .authorizedWhenInUse {
print("autorized--------")
completion(true)
} else if status == .authorizedAlways {
completion(true)
} else if status == .denied {
completion(false)
print("denied--------")
} else if status == .restricted {
completion(false)
}
}
}
private func reqestNotifications(completion: @escaping (Bool) -> Void) {
let center = UNUserNotificationCenter.current()
DispatchQueue.main.async {
center.requestAuthorization(options: [.alert, .badge, .sound]) { (success, error) in
if success {
completion(true)
} else if let error = error {
print(error.localizedDescription)
completion(false)
} else {
completion(false)
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
更新代码
现在它有了入职屏幕和AllowAcsesView。我希望你现在能更清楚地看到这个问题。例如,当用户将在隐私请求进程的中间停止或将更新应用程序时,他将只查看位置许可的检查标记,并且在第一页将立即请求通知屏幕的许可,并且按钮不改变为“开始”标签。
这里有一个可能的解决办法。我创建了两个新的管理器类,它们处理位置和通知请求
struct ContentView: View {
@AppStorage("onBoardingViewed") var hasOnboarded = false
private var locationManager = CLLocationManager()
@ObservedObject var locManager = LocationManager()
@ObservedObject var notificationManager = NotificationManager()
// MARK: - UI States
@State private var isAllowedCamera = false
@State private var isAllowedPhotoLibrary = false
@State private var isAllowedMicrophone = false
@State private var isAllowedLocation = false
@State private var isAllowedNotification = false
@State private var isHiddenCam = true
@State private var isHiddenPL = true
@State private var isHiddenMic = true
@State private var isHiddenLoc = true
@State private var isHiddenNot = true
@State private var isFinished = false
// MARK: - UI
var body: some View {
VStack(spacing: 60) {
Text("Allow Access")
.font(.system(.largeTitle, design: .rounded))
.fontWeight(.bold)
HStack(spacing: 20) {
VStack(alignment: .center, spacing: 20) {
Image(systemName: isAllowedCamera ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isAllowedCamera ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(isHiddenCam ? 0 : 1)
.animation(.spring())
Image(systemName: isAllowedPhotoLibrary ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isAllowedPhotoLibrary ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(isHiddenPL ? 0 : 1)
.animation(.spring())
Image(systemName: isAllowedMicrophone ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isAllowedMicrophone ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(isHiddenMic ? 0 : 1)
.animation(.spring())
Image(systemName: (locManager.authorisationStatus.rawValue == 4) ? "checkmark.circle" : "xmark.circle")
.foregroundColor((locManager.authorisationStatus.rawValue == 4) ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(!locManager.showResult ? 0 : 1)
.animation(.spring())
Image(systemName: notificationManager.allowedNotifications ? "checkmark.circle" : "xmark.circle")
.foregroundColor(notificationManager.allowedNotifications ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(!notificationManager.showResult ? 0 : 1)
.animation(.spring())
}
VStack(alignment: .leading, spacing: 20) {
Text("Camera permission")
.font(.title)
.fontWeight(.light)
Text("Photos permission")
.font(.title)
.fontWeight(.light)
Text("Microphone permission")
.font(.title)
.fontWeight(.light)
Text("Location permission")
.font(.title)
.fontWeight(.light)
Text("Notification permission")
.font(.title)
.fontWeight(.light)
}
}
// MARK: - Action
Button {
reqestCamera { (success) in
if success {
isAllowedCamera = true
} else {
isAllowedCamera = false
}
isHiddenCam = false
}
reqestPhotoLibrary { (success) in
if success {
isAllowedPhotoLibrary = true
} else {
isAllowedPhotoLibrary = false
}
isHiddenPL = false
}
reqestMicrophone { (success) in
if success {
isAllowedMicrophone = true
} else {
isAllowedMicrophone = false
}
isHiddenMic = false
self.locManager.notificationManager = notificationManager
self.locManager.request()
}
if isFinished == true {
withAnimation {
hasOnboarded = true
}
}
} label: {
Text(isFinished ? "START" : "ALLOW")
.font(.custom("SF-Compact-Rounded-Medium", size: 20))
.foregroundColor(.white)
.padding()
.frame(width: 250, height: 50)
.background(Color.blue)
.cornerRadius(5)
}
}
}
然后添加以下位置管理器类:
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
var authorisationStatus: CLAuthorizationStatus = .notDetermined {
willSet {
objectWillChange.send()
}
}
var showResult = false {
willSet {
objectWillChange.send()
notificationManager.requestPush()
}
}
var notificationManager = NotificationManager()
override init() {
super.init()
self.locationManager.delegate = self
}
public func request() {
self.locationManager.requestWhenInUseAuthorization()
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
self.authorisationStatus = status
if (status != .notDetermined) {
self.showResult = true
}
}
}
以及以下通知管理器:
class NotificationManager : NSObject, ObservableObject {
@Published var allowedNotifications = false
@Published var showResult = false
func requestPush() {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { (success, error) in
if success {
DispatchQueue.main.async {
self.allowedNotifications = true
}
} else if let error = error {
print(error.localizedDescription)
} else {
}
DispatchQueue.main.async {
self.showResult = true
}
}
}
}
我使用观察对象
。位置和通知管理器充当可观察对象
。当状态更新时,视图的图标也相应更新。问题是顺序,一个接一个地调用。这就是为什么必须将NotificationManager传递给LocationManager的原因。完成后,位置管理器将调用通知
顺便说一句,屏幕看起来很棒。这个图标真的很酷!ö
编辑:
下面是加载推送通知初始状态的更新
在NotificationManager内部添加以下init()
方法
override init() {
super.init()
let center = UNUserNotificationCenter.current()
center.getNotificationSettings { (settings) in
if(settings.authorizationStatus == .authorized)
{
self.allowedNotifications = true
self.showResult = true
}
else if (settings.authorizationStatus == .denied)
{
self.allowedNotifications = false
self.showResult = true
}
}
}
这里有一个可能的解决办法。我创建了两个新的管理器类,它们处理位置和通知请求
struct ContentView: View {
@AppStorage("onBoardingViewed") var hasOnboarded = false
private var locationManager = CLLocationManager()
@ObservedObject var locManager = LocationManager()
@ObservedObject var notificationManager = NotificationManager()
// MARK: - UI States
@State private var isAllowedCamera = false
@State private var isAllowedPhotoLibrary = false
@State private var isAllowedMicrophone = false
@State private var isAllowedLocation = false
@State private var isAllowedNotification = false
@State private var isHiddenCam = true
@State private var isHiddenPL = true
@State private var isHiddenMic = true
@State private var isHiddenLoc = true
@State private var isHiddenNot = true
@State private var isFinished = false
// MARK: - UI
var body: some View {
VStack(spacing: 60) {
Text("Allow Access")
.font(.system(.largeTitle, design: .rounded))
.fontWeight(.bold)
HStack(spacing: 20) {
VStack(alignment: .center, spacing: 20) {
Image(systemName: isAllowedCamera ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isAllowedCamera ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(isHiddenCam ? 0 : 1)
.animation(.spring())
Image(systemName: isAllowedPhotoLibrary ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isAllowedPhotoLibrary ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(isHiddenPL ? 0 : 1)
.animation(.spring())
Image(systemName: isAllowedMicrophone ? "checkmark.circle" : "xmark.circle")
.foregroundColor(isAllowedMicrophone ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(isHiddenMic ? 0 : 1)
.animation(.spring())
Image(systemName: (locManager.authorisationStatus.rawValue == 4) ? "checkmark.circle" : "xmark.circle")
.foregroundColor((locManager.authorisationStatus.rawValue == 4) ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(!locManager.showResult ? 0 : 1)
.animation(.spring())
Image(systemName: notificationManager.allowedNotifications ? "checkmark.circle" : "xmark.circle")
.foregroundColor(notificationManager.allowedNotifications ? Color.green : Color.red)
.font(.largeTitle)
.scaleEffect(!notificationManager.showResult ? 0 : 1)
.animation(.spring())
}
VStack(alignment: .leading, spacing: 20) {
Text("Camera permission")
.font(.title)
.fontWeight(.light)
Text("Photos permission")
.font(.title)
.fontWeight(.light)
Text("Microphone permission")
.font(.title)
.fontWeight(.light)
Text("Location permission")
.font(.title)
.fontWeight(.light)
Text("Notification permission")
.font(.title)
.fontWeight(.light)
}
}
// MARK: - Action
Button {
reqestCamera { (success) in
if success {
isAllowedCamera = true
} else {
isAllowedCamera = false
}
isHiddenCam = false
}
reqestPhotoLibrary { (success) in
if success {
isAllowedPhotoLibrary = true
} else {
isAllowedPhotoLibrary = false
}
isHiddenPL = false
}
reqestMicrophone { (success) in
if success {
isAllowedMicrophone = true
} else {
isAllowedMicrophone = false
}
isHiddenMic = false
self.locManager.notificationManager = notificationManager
self.locManager.request()
}
if isFinished == true {
withAnimation {
hasOnboarded = true
}
}
} label: {
Text(isFinished ? "START" : "ALLOW")
.font(.custom("SF-Compact-Rounded-Medium", size: 20))
.foregroundColor(.white)
.padding()
.frame(width: 250, height: 50)
.background(Color.blue)
.cornerRadius(5)
}
}
}
然后添加以下位置管理器类:
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
var authorisationStatus: CLAuthorizationStatus = .notDetermined {
willSet {
objectWillChange.send()
}
}
var showResult = false {
willSet {
objectWillChange.send()
notificationManager.requestPush()
}
}
var notificationManager = NotificationManager()
override init() {
super.init()
self.locationManager.delegate = self
}
public func request() {
self.locationManager.requestWhenInUseAuthorization()
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
self.authorisationStatus = status
if (status != .notDetermined) {
self.showResult = true
}
}
}
以及以下通知管理器:
class NotificationManager : NSObject, ObservableObject {
@Published var allowedNotifications = false
@Published var showResult = false
func requestPush() {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { (success, error) in
if success {
DispatchQueue.main.async {
self.allowedNotifications = true
}
} else if let error = error {
print(error.localizedDescription)
} else {
}
DispatchQueue.main.async {
self.showResult = true
}
}
}
}
我使用观察对象
。位置和通知管理器充当可观察对象
。当状态更新时,视图的图标也相应更新。问题是顺序,一个接一个地调用。这就是为什么必须将NotificationManager传递给LocationManager的原因。完成后,位置管理器将调用通知
顺便说一句,屏幕看起来很棒。这个图标真的很酷!ö
编辑:
下面是加载推送通知初始状态的更新
在NotificationManager内部添加以下init()
方法
override init() {
super.init()
let center = UNUserNotificationCenter.current()
center.getNotificationSettings { (settings) in
if(settings.authorizationStatus == .authorized)
{
self.allowedNotifications = true
self.showResult = true
}
else if (settings.authorizationStatus == .denied)
{
self.allowedNotifications = false
self.showResult = true
}
}
}
发生这种情况的原因是位置和通知在DispatchQueueok谢谢,Davidev,但是您能帮助我如何使用该DispatchQueue吗?发生这种情况的原因是位置和通知在DispatchQueueok谢谢,Davidev,但你能帮我如何使用这个调度队列吗?哇,这很酷,谢谢David,真的很感激)但只有当用户允许访问位置和通知时,这才有效,如果用户不允许,那么它将不会显示所选的请求结果,非常感谢您使用图标和屏幕查看我的解决方案,我很高兴您喜欢它,Баааааааааааааааа1072。谢谢你的回复。如果我找到了解决方案,我会再次检查并编辑/返回给你。太棒了嗨,大卫,我发现了一个有趣的错误,不知道如何修复它。问题是,当我移动到通知权限,然后不允许它,然后复选标记的图像没有出现,例如,用户将关闭应用程序,然后再次打开它,它只显示位置复选标记。请帮助)哇,这很酷,谢谢David,真的很感谢)但只有当用户允许访问位置和通知时,这才有效,如果用户不允许,那么它就不会显示所选的请求结果,真的感谢您使用图标和屏幕查看我的解决方案,我很高兴您喜欢它, Дякую Ласкаво просимо. 谢谢你的回复。如果我找到了解决方案,我会再次检查并编辑/返回给你。太棒了嗨,大卫,我发现了一个有趣的错误,不知道如何修复它。问题是,当我移动到通知权限,然后不允许它,然后复选标记的图像没有出现,例如,用户将关闭应用程序,然后再次打开它,它只显示位置复选标记。(请帮忙)