I got local notifications to work. Asking permission, setting, removing, etc. What I can't seem to get right is navigating to a (detail) view upon clicking the notification.
I have the following code:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { gotoview }
Related
In our current swiftUI app, if user closes the app they will be asked to login again - we are trying to change this behaviour by using userDefaults.
Without userDefaults code changes below LoginView directs to the home screen after login/registration using the Navigation link.
Issue:
We made below code changes to the View and LoginViewModel and when login / register button is pressed the LoginView is not taking to the home screen. It just shows the login/signup page with the login successful or registration successful message.
I have debugged in Xcode and loginOrSignUpSuccessful variable value is set to true after login button press.
Why is navigationlink not directing to MainLandingTabView Screen after login or registration?
Code Changes made to the LoginViewModel
Variable & View Code Snippet
#Published var loginOrSignUpSuccessful = UserDefaults.standard.bool(forKey: "loginOrSignUpSuccessful")
{ didSet{ UserDefaults.standard.set(self.loginOrSignUpSuccessful, forKey:"loginOrSignUpSuccessful") } }
self.loginOrSignUpSuccessful = true
UserDefaults.standard.set(true, forKey: "loginOrSignUpSuccessful")
View Code Snippet:
struct SignUpLogin: View {
private var showMainLandingTabViewLink: some View
{
NavigationLink(destination: MainLandingTabView().environmentObject(signupLoginViewModelStateObject).navigationBarBackButtonHidden(true), isActive: $signupLoginViewModelStateObject.loginOrSignUpSuccessful, label: {EmptyView()})
}
var body: some View
{
NavigationView {
ZStack(alignment: .leading) { // all the email & password, button fields.
}
.background(showMainLandingTabViewLink)
}
.environmentObject(signupLoginViewModelStateObject) .navigationBarHidden(true)
// Used to hide the back buttom if coming from NavigationView
}
I am building custom long-look notifications in an Apple Watch app, but for some reason the title defined in the notification's UNMutableNotificationContent is not showing on the long-look notification, and the custom sashColor I’m defining is not used.
In the “Transition to the Long-Look Interface” section of Presenting Notifications on Apple Watch on Apple’s website, there is a screenshot that shows what I would expect to see: a title for the notification in the sash, and a custom sash color.
I built an example app (code below) to isolate the issue.
Here is a screenshot of the notification in my app:
I expect to see the title (“Take Action!”) where the line is, and the sashColor as the background color for the circled region, based on my code.
The short-look of the notification does show the title briefly before it transitions to the long-look (it was hard to get a good screenshot, but here is one as it was animating into the long-look):
Showing or hiding the notification title is not mentioned anywhere that I can find in the documentation, so I expect that to show up automatically since it’s part of the notification.
For the sashColor override, I referred to Customizing Your Long-Look Interface on Apple’s website.
Is there something else specific I need to do to show the title on my customized long-look notification, and get sashColor to work?
Example App
To recreate the issue, create a watchOS app with companion iOS app in Xcode. I called it CustomWatchNotifications.
I updated the main iOS app file to this, with a simple class to request notification permission and send a test notification, which gets passed into the view:
import SwiftUI
import UserNotifications
#main
struct CustomWatchNotificationsApp: App
{
let notifications = NotificationController()
var body: some Scene
{
WindowGroup
{
ContentView(notifications: notifications)
}
}
}
class NotificationController
{
func requestPermissions()
{
Task {
try await UNUserNotificationCenter.current()
.requestAuthorization(
options: [.alert, .sound])
}
}
func scheduleNotification()
{
let content = UNMutableNotificationContent()
content.title = "Take Action!"
content.categoryIdentifier = "takeActionCategory"
content.sound = .default
// Schedule a new notification 5 seconds from now,
// so there is enough time to lock the phone screen
// to deliver notification to Apple Watch.
let trigger = UNTimeIntervalNotificationTrigger(
timeInterval: 5,
repeats: false)
let request = UNNotificationRequest(
identifier: "takeAction",
content: content,
trigger: trigger)
UNUserNotificationCenter.current()
.add(request)
}
}
This is the ContentView for the iOS app, which just includes the two buttons:
import SwiftUI
struct ContentView: View
{
let notifications: NotificationController
var body: some View
{
NavigationView
{
Form
{
// Request notification permissions
Button
{
notifications.requestPermissions()
} label: {
Text("Request Notification Permissions")
}
// Schedule notification
Button
{
notifications.scheduleNotification()
} label: {
Text("Schedule Notification")
}
}
}
}
}
On the watchOS side, I updated the main app file to include a custom View for the notification, inside a WKUserNotificationHostingController for this specific notification category:
import SwiftUI
import UserNotifications
#main
struct CustomWatchNotifications_Watch_AppApp: App
{
var body: some Scene
{
WindowGroup
{
ContentView()
}
WKNotificationScene(
controller: TakeActionNotificationController.self,
category: "takeActionCategory"
)
}
}
struct TakeActionNotificationView: View
{
var body: some View
{
Text("This is a test.")
}
}
class TakeActionNotificationController:
WKUserNotificationHostingController<TakeActionNotificationView>
{
// This does not seem to have an effect on sashColor.
override class var sashColor: Color?
{
return .red
}
override var body: TakeActionNotificationView
{
return TakeActionNotificationView()
}
// This has to be here for custom notification to show up.
override func didReceive(_ notification: UNNotification)
{}
}
When you build and run on real devices, make sure the watchOS app is installed before schedule the test notification. Once you schedule the test notification, lock the iPhone screen immediately so the notification gets delivered to Apple Watch.
I'm trying to create a navigation link in SwiftUI that logs in a user and navigates to the next screen.
I've tried using .simultaneousGesture as shown below, based on this solution. When I have it perform a simple action (e.g. print("hello")), the code works fine and navigates to the next page, but when I have it perform my authState.signUp function (which is async), it calls the function but doesn't navigate to the next page.
Is there a different way I should be approaching this?
NavigationLink(destination: NextView()) {
Text("Create account")
}
.simultaneousGesture(TapGesture().onEnded {
authState.signUp(user: user)
})
you can use a selection arg of navigationLink
add this var
#State var trigger: Bool? = nil
and use
NavigationLink(destination: NextView(), tag: true,
selection: $trigger ) {
}
when ever your other job (login) is done toggle() the trigger and navigationLink fires.
I have a Table View Controller as a tab. In it, there's a button that takes you to a Facebook profile:
func didTapFacebook() {
let url = URL(string: "http://www.facebook.com/" + myFacebookId)
if UIApplication.shared.canOpenURL(url!) {
UIApplication.shared.open(url!, options: [:], completionHandler: nil)
}
}
Works fine every time you press the button.
From another tab, there's a button that presents modally a navigation controller with Table View Controller as its root:
func segueToTable(_ sender: UIViewController, tvc: MyTableViewController, completion: #escaping ((_ done: Bool) -> Void)) {
let nc = MyNavigationController(rootViewController: tvc)
sender.present(nc, animated: true, completion: {
completion(true)
})
}
…
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tvc = storyboard.instantiateViewController(withIdentifier: "TVC") as! MyTableViewController
segueToTable(self, tvc: tvc, completion: { done in
print(“segue complete”)
})
Now once you tap the same button to go to Facebook profile (or Twitter button, basically anything that causes the app to go to background), this warning occurs (there is no keyboard displayed on the screen at this time):
Cannot snapshot view (>) with afterScreenUpdates:NO, because the view is not in a window. Use afterScreenUpdates:YES.
Returning to the app and pressing the button again causes this warning to appear (and every attempt after that):
Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates.
I realize this is related to snapshotting a view that automatically occurs prior to entering the background. It appears for some reason it doesn't like doing that within a view that was presented modally. I've tried a variety of things based on other posts but cannot get the warnings to go away.
Any help is appreciated.
I use SWRevealViewController for my project. My problem is that I can click on the SWRevealView Toggle button at first time and that button does not work after I click on Back button from another view controller and back to that view. Here is the screenshot of my project.
I click on NavigationLeft button from Service View Controller at first and left menu shows up. After that, I click on "Imageview" from that view controller to go to next page.
When I reached to detailed next page, I click on "Back button" and it goes to Service view controller.
At that time , I click on RevealView Toggle button , it does not work. I got error fatal error: unexpectedly found nil while unwrapping an Optional value.
My codes from Service View Controllers are;
override func viewDidLoad() {
super.viewDidLoad()
if self.revealViewController() != nil {
debugPrint("Menu Click")
btnBack.target = self.revealViewController()
btnBack.action = #selector(SWRevealViewController.revealToggle(_:))
self.revealViewController().panGestureRecognizer()
}
else
{
debugPrint("nil")
btnBack.target = self.revealViewController()
btnBack.action = #selector(SWRevealViewController.revealToggle(_:))
// self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
self.revealViewController().panGestureRecognizer()
}
}
When the project runs first time, the code passes to "self.revealViewController() != nil" and I go to detailed view controller and click on Back button. At that time the code passes to " debugPrint("nil")" and it does not show up menu or work.
Codes from Detailed view controller are;
#IBAction func btnBack(_ sender: UIBarButtonItem) {
debugPrint("BtnBack")
self.dismiss(animated: true, completion:nil)
}
Is my code is something wrong or logic wrong ? I have been trying to solve this problem since one week. Please help me ..
On click event of image view in serviceViewController,
let detailVC = self.storyboard?.instantiateViewController(withIdentifier: "Detail") as! DetailVc
self.navigationController?.pushViewController(detailVC, animated: true)
Attach a navigation controller to detail viewcontroller. No need for manual connection between service and detail.
Add a barbutton for goingback event.
self.navigationController?.popViewController(animated: true)
Also add self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer()) instead of ur code in the lastline of adding target for menubutton.
Check whether this works.