I want to change label from another viewController.
First viewcontroller is MenuController. Second one is LoginViewController.
I want to change MenuController's Label.text from LoginViewController.
In LoginViewController:
let viewController = MenuController()
viewController.changeLabel("logout")
In MenuController:
class MenuController: UITableViewController {
var attractionImages = [String]()
var attractionNames = [String]()
var webAddresses = [String]()
#IBOutlet weak var loginLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
loginLabel.text = "Login"
print(loginLabel.text)
}
func changeLabel(Log: String)O {
self.loginLabel.text = log
print (log)
}
But an error occur.
fatal error: unexpectedly found nil while unwrapping an Optional value
How can I solve it?
Thanks for your help.
Another way to achieve that is you can use NSNotificationCenter. Blow is the example for that:
In your MenuController add this code:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "refreshLbl:", name: "refresh", object: nil)
}
Also add this helper method:
func refreshLbl(notification: NSNotification) {
print("Received Notification")
lbl.text = "LogOut"
}
Now in your LoginViewController your back button action will look like:
#IBAction func back(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("refresh", object: nil, userInfo: nil)
self.dismissViewControllerAnimated(true, completion: nil)
}
Now when ever you press back button from LoginViewController your refreshLbl method will call from MenuController.
For more info refer THIS example.
Swift 3 version:
In your MenuController (where the label needs to be changed) add this code:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(refreshLbl),
name: NSNotification.Name(rawValue: "refresh"),
object: nil)
}
Also add this helper method:
#objc func refreshLbl() {
print("Received Notification")
lbl.text = "LogOut"
}
Now in your LoginViewController your back button action will look like:
#IBAction func backButton(_ sender: Any) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "refresh"), object: nil)
// Any additional code...
}
Now when ever you press back button from LoginViewController your refreshLbl() method will call from MenuController.
Related
Hai guys I am new to iOS development and still learning
I have a three View controllers ,viewController, SViewController, TViewController
in SviewController I have notification sender.post method on clicking a button
in viewController, TViewController viewdidload() methods I have .addobserver() methods
when I click a button on SViewController and post the notification
View Controller's selector is executed and not the TviewController
I have loaded the TviewController in FirstVC i.e ViewController viewdidload() method only
since segue performs again a viewdidload() from SviewController to TviewController with another button ,I tried to allocate a completion handler string to global variable so that it value remains same and print it(changed value) when view is loaded again, then I came to know that the completion handler is not at all executing here is the code
ViewController
import UIKit
extension Notification.Name{
static var fnote = Notification.Name("fnote")
}
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(fhandle(notification:)), name: .fnote, object: nil)
let storyboad = UIStoryboard(name: "Main", bundle: nil)
let tvc = storyboad.instantiateViewController(identifier: "1") as! TViewController
let _ = tvc.view
print(tvc.isViewLoaded)
print("journey")
}
#objc func fhandle(notification:Notification){
label.text = "Haii welcome to the hyderabad"
}
}
SViewController
import UIKit
var temp:String = "HHH"
class SViewController: UIViewController {
#IBAction func click(_ sender: Any) {
NotificationCenter.default.post(name: .fnote, object: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
TviewController
import UIKit
class TViewController: UIViewController {
#IBOutlet weak var label2: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(handler(notification:)) , name: .fnote, object: nil)
print(temp)
print("happy")
}
#objc func handler(notification:Notification)
{
print("jackson")
label2.text = "Hurray"
temp = label2.text ?? "Nothing"
}
}
Can Some one please help me with this
You haven't shown or explained where / when / how you're going to display the view from TViewController, but to explain the first thing you're doing wrong...
In this code:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(fhandle(notification:)), name: .fnote, object: nil)
let storyboad = UIStoryboard(name: "Main", bundle: nil)
let tvc = storyboad.instantiateViewController(identifier: "1") as! TViewController
let _ = tvc.view
print(tvc.isViewLoaded)
print("journey")
}
as soon as viewDidLoad() finishes (that is, immediately after the print("journey") line), your tvc instance of TViewController is destroyed. That is, it's dumped from memory and no code inside it can execute, because it no longer exists.
That's called "going out of scope."
So, when you try to post a notification from SViewController, there is no TViewController in existence so its code is not running to "observe" the notification.
Here is a very simple example of multiple view controllers observing a notification:
When you push from ViewController to TViewController, you get a new instance of TViewController and the original instance of ViewController still exists.
When you push from TViewController to SViewController, you get a new instance of SViewController and both ViewController and TViewController still exist.
When you tap the "Post" button, ViewController and TViewController will each execute the func you assigned to observe the notification.
When you then go Back to TViewController, you'll see its label text has changed, and when you go back to ViewController you'll see its label text has also changed.
Note that if you then push to TViewController again, you get a new instance and its label will be back at its original text.
extension Notification.Name{
static var fnote = Notification.Name("fnote")
}
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(fhandle(notification:)), name: .fnote, object: nil)
print("journey")
}
#objc func fhandle(notification:Notification) {
print("Got it!")
label.text = "Haii welcome to the hyderabad"
}
}
class TViewController: UIViewController {
#IBOutlet weak var label2: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(handler(notification:)), name: .fnote, object: nil)
print("happy")
}
#objc func handler(notification:Notification) {
print("jackson")
label2.text = "Hurray"
}
}
class SViewController: UIViewController {
#IBOutlet var infoLabel: UILabel!
#IBAction func click(_ sender: Any) {
NotificationCenter.default.post(name: .fnote, object: nil)
infoLabel.text = "Notification Posted"
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
let storyboad = UIStoryboard(name: "Main", bundle: nil)
let tvc = storyboad.instantiateViewController(identifier: "1") as! TViewController
let _ = tvc.view
print(tvc.isViewLoaded)
This is not the way to test the notification. Push your "TViewController" controller from the "ViewController" controller. Then post notification from your target controller.
I have a mainViewController, press a button to show vc1 of Navigation VC, then press a button to go to vc2 then vc3, after pressing a button on vc3 of Navigation VC, I want to dismiss whole Navigation VC and use information on vc3 to set things on mainViewController.
Following is my vc3, guess I should add codes before navigationController?.dismiss in #IBAction func onContiuneEditing?
Thank you in advance!
class PhotoViewController: UIViewController {
var photo: Photo! = nil
#IBOutlet var imageView: UIImageView!
#IBOutlet var dateLabel: UILabel!
#IBOutlet var filterLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = UIImage(data: photo.currentImage)
dateLabel.text = String(photo.date.description.split(separator: " ")[0])
filterLabel.text = photo.filter
// Do any additional setup after loading the view.
}
#IBAction func onShare(_ sender: Any) {
let activityController = UIActivityViewController(activityItems: [imageView.image!], applicationActivities: nil)
self.present(activityController, animated: true, completion: nil)
}
#IBAction func onContiuneEditing(_ sender: Any) {
navigationController?.dismiss(animated: true, completion: nil)
}
You can do this by setting up and using an unwind segue.
You can use NotificationCenter or Protocol Delegates if you want to pass data to vc3 to vc1.
Because you have 3 layer in navigation I suggest you use NotificationCenter.
Post local notification using NotificationCenter and set a listener in your vc1. And when that listener is execute you can perform your operation.
For ex:
in PhotoViewController
#IBAction func onContiuneEditing(_ sender: Any) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue:CONTINUE_EDITING), object: nil)
navigationController?.dismiss(animated: true, completion: nil)
}
in vc1
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(continueEditing), name: Notification.Name(rawValue: CONTINUE_EDITING), object: nil)
}
func continueEditing() {
}
There are two view controller and two view controller class in my project. I want to change the first view controller background colour from the second view controller using notification and observer. But it's not working.
I have noticed that the "changeViewControllerColor(_:)" method is not calling.
First View Controller:
import UIKit
let colorChangeNotificationKey = "changeFirstVcColor"
class FirstViewController: UIViewController {
let notiName = Notification.Name(rawValue: colorChangeNotificationKey)
deinit {
NotificationCenter.default.removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
observer()
}
func observer() {
NotificationCenter.default.addObserver(self, selector: #selector(FirstViewController.changeViewControllerColor(_:)), name: self.notiName, object: self)
}
#objc func changeViewControllerColor(_: NSNotification) {
self.view.backgroundColor = UIColor.white
}
#IBAction func button(_ sender: UIButton) {
let vc = storyboard?.instantiateViewController(identifier: "secondViewController") as! SecondViewController
navigationController?.pushViewController(vc, animated: true)
}
}
Second View Controller:
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = "First VC colour is white now"
let notiName = Notification.Name(rawValue: colorChangeNotificationKey)
NotificationCenter.default.post(name: notiName, object: nil)
}
}
When you are adding your observer, you are passing it the object self.
You probably want to pass it a nil.
From the documentation:
anObject
that is, only notifications sent by this sender are delivered to the
observer.
If you pass nil, the notification center doesn’t use a notification’s
sender to decide whether to deliver it to the observer.The object whose notifications the observer wants to receive;
So the only thing that it will accept notifications from is itself, which is not likely what you want.
Also, I agree with Harish, you should just use a delegate.
In SecondViewController:
NotificationCenter.default.post(name: Notification.Name("changeFirstVcColor"), object: nil)
In FirstViewController:
NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("changeFirstVcColor"), object: nil)
#objc func methodOfReceivedNotification(notification: Notification) {}
I have 2 UIViewControllers and I try to hide an UILabel from the second UIViewController using Notifications and Observer.
Is the first time when I use this design pattern and I'm a little bit confused. What I'm doing wrong ?
I want to specify that I'm getting the message from that print for the first time only when I click the back button from the second ViewController.
And after that I'm getting the message normal when I click Go Next but the UILabel is not hidden or colour changed.
Here is my code for first UIViewController:
class ReviewPhotosVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.post(name: Notification.Name("NotificationOfReviewMode"), object: nil)
}
#IBAction func goNextTapped(_ sender: UIButton) {
let fullscreenVC = storyboard?.instantiateViewController(withIdentifier: "FullscreenPhoto") as! FullscreenPhotoVC
self.present(fullscreenVC, animated: true, completion: nil)
}
}
Here is my code for second UIViewController:
class FullscreenPhotoVC: UIViewController {
#IBOutlet weak var customLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(hideCustomLabel),
name: Notification.Name("NotificationOfReviewMode"),
object: nil)
}
#IBAction func goBackTapped(_ sender: UIButton) {
let reviewPhotosVC = storyboard?.instantiateViewController(withIdentifier: "ReviewPhotos") as! ReviewPhotosVC
self.present(reviewPhotosVC, animated: true, completion: nil)
}
#objc func hideCustomLabel(){
customLabel.isHidden = true
customLabel.textColor = .red
print("My func was executed.")
}
}
Here is my Storyboard:
Thanks if you read this.
The problem is that you are posting the notification before the next controller is initialised and has started observing. Also, there is no need for the notification you can do it directly. In this case I have used an extra variable shouldHideLabel as you cannot call the function hideCustomLabel() directly because this will lead to crash as the outlets are only initialised after view is loaded.
class ReviewPhotosVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//NotificationCenter.default.post(name: Notification.Name("NotificationOfReviewMode"), object: nil)
}
#IBAction func goNextTapped(_ sender: UIButton) {
let fullscreenVC = storyboard?.instantiateViewController(withIdentifier: "FullscreenPhoto") as! FullscreenPhotoVC
fullscreenVC.shouldHideLabel = true
self.present(fullscreenVC, animated: true, completion: nil)
}
}
class FullscreenPhotoVC: UIViewController {
var shouldHideLabel = false
#IBOutlet weak var customLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if shouldHideLabel {
hideCustomLabel()
}
/*
NotificationCenter.default.addObserver(self,
selector: #selector(hideCustomLabel),
name: Notification.Name("NotificationOfReviewMode"),
object: nil)
*/
}
#IBAction func goBackTapped(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
#objc func hideCustomLabel() {
customLabel.isHidden = true
customLabel.textColor = .red
print("My func was executed.")
}
}
I have my project setup like this:
I have a custom Tab Navigation Controller. In the top bar of the Custom Tab Navigation Controller is a text field. This text field changes according to the 4 main views of the app (4 tab buttons).
What I am trying to do is use the text field as a search bar by passing whatever is typed into the text field into the searchView However, I am having trouble passing the textfield.text from the Navigation Controller into searchView. I have a picture below that illustrates this more clearly.
The searchView has the search function taken care of. All I am trying to do is pass with textfieled.text value to the searchView whenever it is changed
// global
let handleTextChangeNotification = "handleTextChangeNotification"
your FirstViewController
override func viewDidLoad() {
super.viewDidLoad()
textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
}
func textFieldDidChange(textField: UITextField){
NotificationCenter.default.post(name: NSNotification.Name(rawValue: handleTextChangeNotification), object: nil, userInfo: ["text":textField.text])
let search_screen = SearchViewController()
self.navigationController?.pushViewController(search_screen, animated: true)
}
SeacrchViewController's
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(SeacrchViewController.handleTextChange(_:)),
name: NSNotification.Name(rawValue: handleTextChangeNotification),
object: nil)
}
func handleTextChange(_ myNot: Notification) {
if let use = myNot.userInfo {
if let text = use["text"] {
// your search with 'text' value
}
}
}
}
You can do something like that.
In your FirstViewController
func textFieldDidChange(textField: UITextField){
let search_screen = SearchViewController()
search_screen.search_string = self.textField.text
self.navigationController?.pushViewController(search_screen, animated: true)
}
In case of storyboards
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if (segue.identifier == "Your_SearchViewController_identifier") {
let search_screen = segue.destinationViewController as!
SearchViewController
search_screen.search_string = self.textField.text
}
}
And In your SeacrchViewController's viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.textField.text = self.search_string
}