I'm trying to pass data, using delegate between two VC, but I can't get why it's not working.
My first VC
class ViewController: UIViewController {
#IBOutlet weak var profileImage: UIImageView!
func downloadPhoto() {
self.performSegue(withIdentifier: "showPhotoFromInternet", sender: nil)
}
}
extension ViewController: IPresentaionPhotoFormInternetDelegate {
func setNewImage(imageToShow: UIImage) {
profileImage.image = imageToShow
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PresentPhotoFromInternetViewController {
destination.delegate = self
}
}
My second VC
class PresentPhotoFromInternetViewController: UIViewController {
var imageToSend: UIImage?
var delegate: IPresentaionPhotoFormInternetDelegate?
#IBOutlet weak var photoCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
photoCollectionView.allowsMultipleSelection = false
}
#IBAction func sendPhotoToPreviousController(_ sender: Any) {
delegate?.setNewImage(imageToShow: iamgeToSend!)
performSegue(withIdentifier: "sendPhoto", sender: nil)
}
extension PresentPhotoFromInternetViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! photoCollectionViewCell
print("Cell is selected")
iamgeToSend = cell.photoImageView.image
cell.selectedView.backgroundColor = UIColor.green
}
protocol IPresentaionPhotoFormInternetDelegate {
func setNewImage(imageToShow: UIImage)
}
I use present modaly segue for from the first VC to the second, and show form the second to the first
When I perform segue from the second VC there is no updates in my first one, although it passes all breakpoints.
The problem is instead of popping controller you are loading completely new controller in your button action
#IBAction func sendPhotoToPreviousController(_ sender: Any) {
delegate?.setNewImage(imageToShow: iamgeToSend!)
//Comment or remove the performSegue
//performSegue(withIdentifier: "sendPhoto", sender: nil)
//Now simply pop this controller
_ = self.navigationController?.popViewController(animated: true)
// If you are presenting this controller then you need to dismiss
self.dismiss(animated: true)
}
Note: If you are segue is kind of Push then use popViewController or is it kind of Modal than you need to use dismiss.
Related
I am trying to perform a segue from a UITableView with news. If you push one of the news, it performs a segue to the specific news you selected.
It is easy and I have done it a few times... but I don't know what am I doing wrong this time.
The NewsDetailViewController is like this:
class NewsDetailViewController: UIViewController {
#IBOutlet weak var newsImage: UIImageView!
#IBOutlet weak var newsTitle: UILabel!
#IBOutlet weak var newsDate: UILabel!
#IBOutlet weak var newsText: UILabel!
var newsLink: String?
override func viewDidLoad() {
super.viewDidLoad()
// Hides the navigation bar.
self.navigationController?.setNavigationBarHidden(true, animated: false)
}
#IBAction func closeNews(_ sender: UIButton) {
navigationController?.popViewController(animated: true)
}
}
And the segue in the NewsTableViewController is like this:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("you selected the row: \(indexPath.row)")
tableView.deselectRow(at: indexPath, animated: true)
self.performSegue(withIdentifier: "goToNewsDetail", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToNewsDetail" {
if let destination = segue.destination as? NewsDetailViewController {
destination.newsLink = "whateverlink"
destination.newsTitle.text = "whatevertitle"
}
}
}
And the line: destination.newsLink = "whateverlink"
Works perfectly.
But the line: destination.newsTitle.text = "whatevertitle"
Throws a
fatal error: Unexpectedly found nil while implicitly unwrapping an
Optional value.
And I have no idea of what if going on. The same problem happens when trying to initialise the rest of the labels in the destination.
This line is the problem
destination.newsTitle.text = "whatevertitle"
don't access outlets of the destination vc as they not yet loaded send a string and assign it to the label in the destination vc
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToNewsDetail" {
if let destination = segue.destination as? NewsDetailViewController {
destination.newsLink = "whateverlink"
destination.toSend = "whatevertitle"
}
}
}
class NewsDetailViewController: UIViewController {
#IBOutlet weak var newsTitle:UILabel!
var toSend = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.newsTitle.text = toSend
}
}
In the prepareForSegue method, the newsTitle label is still not initialised, so it is nil.
Generally, you shouldn't set the target VC's view's properties in prepareForSegue. You should declare a newsTitleText property in NewsDetailViewController:
var newsTitleText: String!
And set this property instead:
destination.newsTitleText = "whatevertitle"
Then set the newsTitle.text in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
// Hides the navigation bar.
self.navigationController?.setNavigationBarHidden(true, animated: false)
newsTitle.text = newsTitleText
}
When the prepare(for:sender:) method is called, NewsDetailViewController has not loaded the view yet so you can't set the text on a label. What you want to do is create another property on NewsDetailViewController such as var newsTitleText: String?. Then in viewDidLoad you can call newsTitle.text = newsTitleText.
Im using autolayout and for the life of me cannot set the proceeding view controller image view with the previous view controller collection view cell's image. Im using
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "openDetailView", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "openDetailView" {
let cell = sender as? ImageCollectionViewCell
let detailVC = segue.destination as! DetailedImageViewController
detailVC.imageToPresent = cell?.imageView.image
}
}
This is the class that I have already set up to receive the image
class DetailedImageViewController : UIViewController{
var imageToPresent: UIImage!
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = imageToPresent
}
}
The segue performs as expected but the image DOES NOT show in detailedImageViewController. Thanks in advance.
You need to set the imageToPresent into a UIImageview in order to show the image.
class DetailedImageViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var imageToPresent : UIImage!
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = imageToPresent
}
...
It's not a best practice to avoid optional unwrapping in this case. Try to use this code and check the errors if they happen.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "openDetailView" {
guard let cell = sender as? ImageCollectionViewCell else {
assertionFailure("Failed to unwrap sender. Try to set a breakpoint here and check what sender is")
return
}
let detailVC = segue.destination as! DetailedImageViewController
guard let cellImage = cell.imageView.image else {
assertionFailure("The cell has no image in image view")
return
}
detailVC.imageToPresent = cellImage
}
}
Here I am trying to pass value from one class LanguageSelectionTVC to another RegistrationVC by using a Protocol.
When I try and call the method self.delegate?.setSelectedLangauges(self.languagesSpokenArray) inside LanguageSelectionTVC it doesn't call the method setSelectedLangauges inside the class Registration VC could someone please suggest where I am going wrong ?
protocol LanguageSelectionTVCProtocol {
func setSelectedLangauges(_ valueSent: [String])
}
class LanguageSelectionTVC: UITableViewController {
var delegate : LanguageSelectionTVCProtocol? = nil
func saveAndClose() {
self.delegate?.setSelectedLangauges(self.languagesSpokenArray)
dismiss()
}
}
class RegistrationVC: UIViewController,
UITableViewDelegate,
UITableViewDataSource,
LanguageSelectionTVCProtocol{
func setSelectedLangauges(_ valueSent: [String]){
self.showLanguagesSpoken(valueSent)
}
}
Moving to LanguageSelectionTVC from RegistrationVC . The below tableView method is in my RegistrationVC
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let row = indexPath.row
let section = indexPath.section
let currentCell = tableView.cellForRow(at: indexPath) as! UITableViewCell
if section == 4 && row == 0 {
// The user has clicked on languages spoken cell
self.performSegue(withIdentifier: "LanguageSelectionTVC", sender: self)
}
}
You need to access LanguageSelectionTVC from preparForSegue to set delegate of it. So override the prepare(for:sender:) in your RegistrationVC.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "LanguageSelectionTVC" {
let vc = segue.destination as! LanguageSelectionTVC
vc.delegate = self
}
}
You need to set the delegate in RegistrationVC to LanguageSelectionTVC, for example you could set it when performing a segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let targetVC = segue.destination as! LanguageSelectionTVC
targetVC.delegate = self
}
You need to set delegate. can you please see below ready example for pass data between two view controller.
import UIKit
protocol SenderViewControllerDelegate
{
func messageData(str1:NSString)
}
class tableViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource
{
var delegate: tableViewController?
override func viewDidLoad()
{
super.viewDidLoad()
self.delegate?.messageData(str1: “Hello world”)
}
}
receive data in below controller
import UIKit
class HomeViewController: UIViewController ,SenderViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let objtableview = tableViewController(nibName: "tableViewController",bundle: nil)
objtableview.delegate = self
self.navigationController?.pushViewController(objtableview, animated: true)
}
func messageData(str1:NSString){
print(str1)
}
}
I have read this thread (and similar others) from bottom to top, but it doesn't fit my needs at all.
I have a UIViewController inside UIPageViewController within a UINavigationController. Navigating to a 2nd ViewController. Navigating to a 3rd ViewController and want to pop back to 2nd ViewController delivering data.
My code currently:
protocol PassClubDelegate {
func passClub(passedClub: Club)
}
class My3rdVC: UIViewController {
var clubs: [Club] = []
var passClubDelegate: PassClubDelegate?
....
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let club = clubs[indexPath.row]
self.passClubDelegate?.passClub(club)
navigationController?.popViewControllerAnimated(true)
}
My 2nd VC:
class My2ndVC: UIViewController, PassClubDelegate {
var club = Club()
func passClub(passedClub: Club) {
SpeedLog.print("passClub called \(passedClub)")
club = passedClub
}
passClub is not called. I'm sure it's because I didn't set the delegate to the My2ndVC, but how would I do that? All the solutions I have found wanting me to use a) segue or b) instantiate a My2ndVC new, what doesn't make any sense since it's still in memory and I want to pop back to go back in hierarchy. What am I missing? What are my possibilities? Help is very appreciated.
PS: I'm not using any segues. My3rdVC is called by:
let vc = stb.instantiateViewControllerWithIdentifier("My3rdVC") as! My3rdVC
self.navigationController?.pushViewController(vc, animated: true)
You can set the delegate of My3rdVC in the prepareForSegue method of My2ndVC.
class My2ndVC: UIViewController, PassClubDelegate {
...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
super.prepareForSegue(segue, sender: sender)
switch segue.destinationController {
case let controller as My3rdVC:
controller.passClubDelegate = self
}
}
}
This is assuming you have created a segue in your storyboard that pushes My3rdVC from My2ndVC onto the navigation controller stack, which I'm assuming you have. So just try simply pasting this prepareForSegue method into My2ndVC and see if it works.
UPDATE
let vc = stb.instantiateViewControllerWithIdentifier("My3rdVC") as! My3rdVC
vc.passClubDelegate = self
navigationController?.pushViewController(vc, animated: true)
When pop one VC to another you can pass data using protocol by declare delegate variable as static. Here in the following example we pop SecondVC to FirstVC and we pass a string.
class FirstVC: UIViewController,getDataDelegateProtocol {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
SecondVC.delegate = self
}
func getData(tempStr: String) {
label.text = tempStr
}
#IBAction func buttonClick(_ sender: UIButton){
let nav = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondVC") as! SecondVC
self.navigationController?.pushViewController(nav, animated: true)
}
}
Another View Controller
protocol getDataDelegateProtocol{
func getData(tempStr: String)
}
class SecondVC: UIViewController {
#IBOutlet weak var label: UILabel!
static var delegate: getDataDelegateProtocol?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonClick(_ sender: UIButton){
SecondVC.delegate?.getData(tempStr: "Received data from SecondVC")
self.navigationController?.popViewController(animated: true)
}
}
i'm trying to start writing Swift and i'm trying to get a value from a modal view controller with no luck.
I have two controllers, the ViewController and modalViewController.
In ViewController i have a UITableView and with a press of a button i open the modalViewController.
Then from a UITextField i pass the value.
I have implement a protocol with delegate and func but somewhere i'm missing something or had it wrong.
ViewController.swift
import UIKit
class ViewController: UIViewController,UITableViewDelegate,modalViewControllerDelegate{
#IBOutlet var table: UITableView!
var tableData = ["First Row","Second Row","Third Row"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(animated: Bool) {
table.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(table:UITableView?,numberOfRowsInSection section: Int) -> Int
{
return tableData.count
}
func tableView(table:UITableView?,cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
let cell:UITableViewCell = UITableViewCell(style:UITableViewCellStyle.Default,reuseIdentifier:"cell")
cell.textLabel?.text = tableData[indexPath.row]
return cell
}
func sendText(text: NSString) {
tableData.append(text)
} }
modalViewController.swift
import UIKit
protocol modalViewControllerDelegate {
func sendText(var text: NSString)
}
class modalViewController: UIViewController{
let delegate: modalViewControllerDelegate?
#IBOutlet var textField: UITextField?
#IBAction func cancelButton(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func saveButton(sender: AnyObject) {
delegate?.sendText(self.textField!.text)
dismissViewControllerAnimated(true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
}
I have no errors in the code, the delegate is not working, it's always nil.
Thanks.
You have to assign the delegate in your first view controller.
Also, you have to change let delegate: modalViewControllerDelegate? to a var, or else you can't change it.
Right now your delegate is empty.
It's unclear how you're accessing ModalViewController. If you're using segues:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "modalViewControllerSegue" {
var destination = segue.destinationViewController as CategoryViewController
destination.delegate = self
}
}
Or if you're doing it programmatically:
var modalViewController = ModalViewController(parameters)
modalViewController.delegate = self
presentViewController(modalViewController, animated: true, completion: nil)
Storyboard identifier:
let destination = UIStoryboard.mainStoryboard().instantiateViewControllerWithIdentifier("ModalViewController") as ModalViewController
delegate = self
showViewController(destination, sender: nil)
EDIT:
If you want to access ModalViewController by selecting a cell you need the tableView: didSelectRowAtIndexPath method:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("modalViewControllerSegue", sender: self)
}
Using this, you'll need the method prepareForSegue to set the delegate.
You have to set your modalViewController's delegate property before presenting it. If you're using segues, you can do this in prepareForSegue(_:).
Also, class names should begin with uppercase letters (modalViewController should be ModalViewController). Only instances should begin with lowercase letters.
Another option, instead of using delegation, is to use an unwind segue. Here's a tutorial: http://www.cocoanetics.com/2014/04/unwinding/
In your case, in your presenting view controller you could have the method:
func returnFromModalView(segue: UIStoryboardSegue) {
// This is called when returning from the modal view controller.
if let modalVC = segue.sourceViewController as? ModalViewController
where segue.identifier == "mySegueID" {
let text = modalVC.textField.text
// Now do stuff with the text.
}
}
And then just link up everything in the Interface Builder as shown in the tutorial.