How to send a notification from UIKit to a view in SwiftUI? - swift

I am trying to send a notification from UIViewcontroller to SwiftUI View after the user did pull to refresh.
#objc private func fetchScheduleData(_ sender: UIRefreshControl) {
NotificationCenter.default.post(name: Notification.Name(rawValue: "didPullToRefreash"), object: nil)
}
On SwiftUI view i trying to set this method .onchange()
NotificationCenter.default.addObserver(self, selector: #selector(didPullToRefreashHelper), name: Notification.Name(rawValue: "didTapNotification"), object: nil)
But onChange it's not working. I am wondering i how i will do this.

The simplest way of doing this would be to first, create the custom notification like this:
extension Notification.Name {
static let didPullToRefresh = Notification.Name("didPullToRefresh")
}
That now lets you address it with dot notation. Next, in your VC:
#objc private func fetchScheduleData(_ sender: UIRefreshControl) {
NotificationCenter.default.post(name: .didPullToRefresh, object: nil)
}
Lastly, in your SwiftUI view:
.onReceive(NotificationCenter.default.publisher(for: .didPullToRefresh)) { _ in
// If you are passing an object, this can be "notification in"
// Do something here as a result of the notification
}
edit:
If you want to send a message in a SwiftUI view when a variable changed, then you could use .onChange(of:) like this:
.onChange(of: watchedStateVar) { value in
NotificationCenter.default.post(name: .didPullToRefresh, object: value)
}

Related

NotificationCenter - addObserver not called

I am trying a very simple code with NotificationCenter. But the addObserver is not getting called. Can any one of you check and let me know what i am missing. There are 2 simple class, one which post notification and another which listens to it. When i run the program, i just see "sending notification" in the console.
Thanks in advance.
Class 1:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("sending notification")
NotificationCenter.default.post(name: Notification.Name("test"), object: nil)
}
}
Class 2:
class secondvc: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("second vc")
NotificationCenter.default.addObserver(self,
selector: #selector(doThisWhenNotify(_:)),
name: Notification.Name("test"),
object: nil)
}
#objc func doThisWhenNotify(_ notification: Notification) {
print("inside notification")
}
}
If, at the time ViewController comes into existence, secondvc does not yet exist, then there is no one there to receive the posted notification and that is why you don't see the notification being received later when secondvc does come into existence.

Swift NSNotification Observers not working

I have 2 view controllers, one with a switch that when toggled should post the following notification. In the other view controller I have Observers which should trigger the following function which just toggles a boolean. I am not able to get the observers to work and make that function call, am I doing something wrong here? I have another Notification (Doesn't trigger with user input) that is being sent in the opposite direction which works fine.
#IBAction func switchAction(_ sender: Any) {
if switchUI.isOn {
print("Collecting Data ")
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Collect"), object: self)
}
else
{
print("Not Collecting Data")
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Do Not Collect"), object: self)
}
}
func collectDataObserver () {
//Add an Observer
NotificationCenter.default.addObserver(self, selector: #selector(CentralViewController.toggleData(notification:)), name: Notification.Name(rawValue: "Collect"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(CentralViewController.toggleData(notification:)), name: Notification.Name(rawValue: "Do Not Collect"), object: nil)
}
#objc func toggleData(notification: NSNotification) {
let isCollectData = notification.name.rawValue == "Collect"
if(isCollectData){
IsCollectingData = true
}
else
{
IsCollectingData = false
}
}
You need to call collectDataObserver() in viewDidLoad() of CentralViewController, i.e.
override func viewDidLoad() {
super.viewDidLoad()
collectDataObserver()
}

What is the best way of updating a variable in a view controller from scene delegate?

I am using Spotify SDK. I want to change labels in some view controllers when a user changes his/her player state. Here is my scene delegate:
var playerViewController = MatchViewController()
func playerStateDidChange(_ playerState: SPTAppRemotePlayerState) {
playerViewController.stateChanged(playerState)
}
A view controller:
func stateChanged(_ playerState: SPTAppRemotePlayerState) {
// aLabel.text = playerState.track.name
}
The problem is labels or other outlets are nil when the state is changed because the view controllers are not loaded at that time. How can I fix that? (I tried isViewLoaded)
If you have a more than a few places to update according to a change that occurs at one place use observers. Here's how,
Post notification in SceneDelegate like this:
func playerStateDidChange(_ playerState: SPTAppRemotePlayerState) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "stateChanged"), object: nil, userInfo: ["playerState": playerState])
}
Observe in ViewControllers like this:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(stateChanged), name: NSNotification.Name("stateChanged"), object: nil)
}
#objc func stateChanged(_ notification: Notification) {
if let playerState = notification.userInfo?["playerState"] as? SPTAppRemotePlayerState {
print(playerState)
}
}
}

notification center observer to check an NSTextView wont work right

I'm using a notification center observer to check an NSTextView
NotificationCenter.default.addObserver(self, selector: #selector(testest), name: NSTextView.didEndEditingNotification, object: nil)
it works but the thing is, i got two nstextview and it automaticly picks up on one
even when i specify its name in obejct?? why is that?
like this
NotificationCenter.default.addObserver(self, selector: #selector(testest), name: NSTextView.didEndEditingNotification, object: BoxText)
Update: It acutlay picks up on both of my NSTextViews how do i specify one?
If you pass nil all NSTextView in your view controller will receive the notification. Just create an IBOutlet to each NSTextView, cast the notification object to NSTextView and check if it is equal to each of them. If you would like to have a selector to fire just for a certain text view you need to pass it as the object parameter:
import Cocoa
class ViewController: NSViewController {
#IBOutlet var textViewLeft: NSTextView!
#IBOutlet var textViewRight: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
// If you pass nil all NSTextView in your view controller will receive the notification
NotificationCenter.default.addObserver(self, selector: #selector(didEndEditing), name: NSTextView.didEndEditingNotification, object: nil)
// If you would like to have a selector to fire just for a certain text view you need to pass it as the object parameter
NotificationCenter.default.addObserver(self, selector: #selector(didEndEditingTextViewLeft), name: NSTextView.didEndEditingNotification, object: textViewLeft)
NotificationCenter.default.addObserver(self, selector: #selector(didEndEditingTextViewRight), name: NSTextView.didEndEditingNotification, object: textViewRight)
}
#objc func didEndEditing(_ obj: Notification) {
if (obj.object as? NSTextView) == textViewLeft {
print(#function, "textViewLeft")
} else if (obj.object as? NSTextView) == textViewRight {
print(#function,"textViewRight")
}
}
#objc func didEndEditingTextViewLeft(_ obj: Notification) {
print(#function)
}
#objc func didEndEditingTextViewRight(_ obj: Notification) {
print(#function)
}
}

Swift - Error passing data between protocols / delegates (found nil)

I am developing an application with swift 3.0. Where what I want to do is, from the "MainMapVC" class, which is the view where you have a map with a date slider (see the attached image). I want to move the slider and send that slider position (1,2 or 3) to LeftSideViewController which is the side view (the legend) updating the content depending on the selected date.
View of MainMapVC:
View of MainMapVC with Legend:
Well, and I've come to the point where I have to pass a value between the two view controllers. But problem is that I get the error "fatal error: unexpectedly found nil while unwrapping an optional value". Basically I have a "nil" delegate.
But do not find where the error is, because the definition of the delegate is like "var delegate: MainMapVCDelegate!" And I call it "delegate.moveSliderDates (datePos: Int (roundedValue))" in the "MainMapVC" class.
Does anyone know where I failed in the statement of the delegate?Thanks :)
I attach the code of the two classes so that you see the whole code.
Class MainMapVC (first way):
import UIKit
protocol MainMapVCDelegate: class {
func moveSliderDates(datePos: Int)
}
class MainMapVC: UIViewController, UISearchBarDelegate, CLLocationManagerDelegate, GMSMapViewDelegate {
//MARK: VARIABLES
weak var delegate: MainMapVCDelegate? = nil
let step: Float = 1
#IBAction func moveSliderDates(_ sender: UISlider) {
let roundedValue = round(sender.value / step) * step
sender.value = roundedValue
delegate?.moveSliderDates(datePos: Int(roundedValue))
}
}
The delegate value inside the moveSliderDates function is "nil":
delegate?.moveSliderDates(datePos: Int(roundedValue))
Class LeftSideViewController (first way):
import UIKit
class LeftSideViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, customCellDelegate, MainMapVCDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MainMapVC" {
let secondViewController = segue.destination as! MainMapVC
secondViewController.delegate = self
}
}
func moveSliderDates(datePos: Int){
print(datePos)
print("/////////////")
tableSideLeft.reloadData()
}
not enter inside this function because the delegate of "MainVC" is "nil":
Class MainMapVC (second way):
let step: Float = 1
#IBAction func moveSliderDates(_ sender: UISlider) {
let roundedValue = round(sender.value / step) * step
sender.value = roundedValue
let data:[String: Int] = ["data": Int(roundedValue)]
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: data)
}
Class LeftSideViewController (second way):
func listnerFunction(_ notification: NSNotification) {
if let data = notification.userInfo?["data"] as? String {
print(data)
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(listnerFunction(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)
}
Never goes into the function listnerFunction
You get the error because you defined your delegate as force unwrapped noy-nil version by this code var delegate: LeftSideDelegate!
Instead, you need to change it like this. You should not create strong reference cycle for delegate.
weak var delegate: LeftSideDelegate? = nil
Then for all your delegate calles, do the wrapped version delegate call
delegate?.changeZindexDelivery()
Other than that, change your line protocol LeftSideDelegate { into protocol LeftSideDelegate : class {
Passing data between view controllers using delegate
First, in the class where you want to pass the data to another view controller, declare protocol in this way
protocol SampleDelegate: class {
func delegateFunctionCall(data: String)
}
Then, create delegate variable as optional with type weak var. Call delegate method with you want to pass data or trigger action
class SecondViewController: UIViewController {
weak var delegate: SampleDelegate? = nil
#IBAction func sendTextBackButton(sender: AnyObject) {
delegate?.delegateFunctionCall(data: textField.text!)
}
}
Finally in your view controller that you want to receive action or data, implement the protocol. When you are initiating the second view controller, set it's delegate variable to be the current view controller
class FirstViewController: UIViewController, SampleDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destination as! SecondViewController
secondViewController.delegate = self
}
}
func delegateFunctionCall(data: String) {
label.text = data
}
}
Passing data between view controllers using notification
In the destination view controller, register a handler function that is ready to be called. You can add this registration code in view did load
NotificationCenter.default.addObserver(self, selector: #selector(listnerFunction(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)
func listnerFunction(_ notification: NSNotification) {
if let data = notification.userInfo?["data"] as? String {
// do something with your data
}
}
Then in another view controller, if you want to pass data, simply call this
let data:[String: String] = ["data": "YourData"]
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: data)