I am passing data(image and Label) from one view controller to another. I am storing them in a NSDictionary, and the data is being passed to the new viewcontroller. I have connected a image view as well as a label. How do I assign the image and the label the values in the NSDictionary? Below is the code for transferring the data from one view controller to another.
var dicSelected : NSDictionary!
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("Cell \(indexPath.item) selected")
print(arrayOfFriendsNames[indexPath.item])
self.dicSelected = ["friendname" : arrayOfFriendsNames[indexPath.item], "friendimage" : arrayOfFriends[indexPath.item]]
self.performSegueWithIdentifier("friendaccess", sender: dicSelected)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "friendaccess"){
let nextViewOBJ = segue.destinationViewController as! FriendProfilePages
nextViewOBJ.dicData = self.dicSelected;
}
}
Below is the code for the FriendProfilePages.
#IBOutlet weak var friendimage: UIImageView!
#IBOutlet weak var friendname: UILabel!
var dicData : NSDictionary?
override func viewWillAppear(animated: Bool) {
print("Dictionary: \(self.dicData)")
}
Or you can create a model class with properties
var image: UIImage?
var text: String?
and then pass that class's object to next view controller and assign those values to your friendimage.image and friendname.text
Code for FriendProfilePages:
#IBOutlet weak var friendimage: UIImageView!
#IBOutlet weak var friendname: UILabel!
var dataModel : DataModel?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
friendimage.image = dataModel.image
friendname.text = dataModel.text
}
Now For performing Segue
var selectedData = DataModel()
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("Cell \(indexPath.item) selected")
print(arrayOfFriendsNames[indexPath.item])
self.selectedData.text = arrayOfFriendsNames[index] as? String
self.selectedData.image = arrayOfFriends[index] as? UIImage
self.performSegueWithIdentifier("friendaccess", sender: dicSelected)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "friendaccess"){
let nextViewOBJ = segue.destinationViewController as! FriendProfilePages
nextViewOBJ.dataModel = self.selectedData;
}
}
Declare it anywhere
class DataModel : NSObject {
var image: UIImage?
var text: String?
}
just to get you an idea...
You should use proper types to transfer data rather than dictionaries. You could define an appropriate type like this
struct FriendProfileData {
let name: String
let image: UIImage
}
You'll need to update your selection code to create one of these new structs like this
var selected: FriendProfileData
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selected = FriendProfileData(name: arrayOfFriendsNames[indexPath.item], image: arrayOfFriends[indexPath.item])
performSegueWithIdentifier("friendaccess", sender: self)
}
Your prepareForSegue needs to then change to
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "friendaccess"){
guard let selected = selected else {
return
}
let nextViewOBJ = segue.destinationViewController as! FriendProfilePages
nextViewOBJ.dicData = selected;
}
}
Then in the viewDidAppear
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
friendImage.image = selected.image
friendName.text = selected.name
}
Related
well my problem is that idk how to pass the info values from the uitableviewcell to the anotherviewcontroller by the segue, could you please help, im unwraped the value, cause the data is already loaded idk what to do to pass the info to the popup controller without crashed
Here is my class model where i set the values
class MovieCell: UITableViewCell {
//
// MARK: - Class Constants
//
static let identifier = "MovieCell"
let urlImage = "https://image.tmdb.org/t/p/w500"
//
// MARK: - IBOutlets
//
#IBOutlet weak var title: UILabel!
#IBOutlet weak var rating: RatingView!
#IBOutlet weak var releaseDate: UILabel!
#IBOutlet weak var poster: UIImageView!
var titlePopUp: String = ""
func configure(movieDictionary: [String: Any]) {
title.text = (movieDictionary["title"] as! String)
titlePopUp = movieDictionary["title"] as! String
releaseDate.text = (movieDictionary["release_date"] as! String)
do {
let url = URL(string: "\(self.urlImage)" + "\(movieDictionary["backdrop_path"]!)")
let data = try Data(contentsOf: url!)
self.poster!.image = UIImage(data: data)
}
catch{
print(error)
}
}
}
heres is the viewcontroller where i get the error in the line 53
class ViewController: UIViewController,UITableViewDataSource, UICollectionViewDataSource, UITableViewDelegate {
var jsonArray: [Any] = []
let movieService = MovieService()
let popUpVC = popUpViewController()
#IBOutlet weak var moviesTableView: UITableView!
#IBOutlet weak var postersView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
movieService.fetchMovies { jsonArray in
if let jsonArray = jsonArray {
self.jsonArray = jsonArray
self.moviesTableView.reloadData()
self.postersView.reloadData()
}
}
self.moviesTableView.delegate = self
self.moviesTableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.jsonArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MovieCell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath) as! MovieCell
cell.configure(movieDictionary: jsonArray[indexPath.row] as! [String: Any])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
present(popUpVC, animated: true, completion: nil)
moviesTableView.deselectRow(at: indexPath, animated: true)
self.performSegue(withIdentifier: "popUp", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "popUp" {
let destinationVC = segue.destination as! popUpViewController
let data = MovieCell()
destinationVC.movieN = data.title.text!
}
}
}
and the popup controller is this one
class popUpViewController: UIViewController {
#IBOutlet weak var poster: UIImageView!
#IBOutlet weak var movieName: UILabel!
#IBOutlet weak var releaseDate: UILabel!
#IBOutlet weak var descriptionMovie: UILabel!
var movieN = String()
override func viewDidLoad() {
super.viewDidLoad()
movieName.text = movieN
// Do any additional setup after loading the view.
}
#IBAction func closePop(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
im getting crash passing the label and image, the clasical error, unexpected nil value, but idk why the data is already loaded in the tableview main screen
You're not getting the data from the cell is because in the prepare method, you're creating a new instance of the cell.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "popUp" {
let destinationVC = segue.destination as! popUpViewController
let data = MovieCell() // This is where the issue is
destinationVC.movieN = data.title.text!
}
}
The newly created cell instance has no relationship with the one that's already displaying the data. In the new cell, all the properties are either empty or nil. That's why you're getting the 'unexpected nil value' error.
To get that cell's values, you should get a reference to that cell.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) as? MovieCell else { return }
performSegue(withIdentifier: "popUp", sender: cell.titlePopUp)
}
Here you get the cell the user taps on and casts it to the MovieCell to access its titlePopUp property.
The you pass it to the prepare method in the sender parameter.
Finally in the prepare method, you cast it back to String (because the sender parameter is of type Any) and pass it to the popup view.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "popUp" {
let destinationVC = segue.destination as! popUpViewController
destinationVC.movieN = sender as? String
}
}
A few final notes: This way of passing around JSON as dictionaries will get the job done but will cause you massive headaches for you down the line if you have to change/maintain your code. What if later you have to pass another value of a movie along with the title to the popup? It's going to become harder and harder to scale this.
Do look in to object oriented way of programming. For example, create a class/struct called Movie to hold all the data (title, release date, rating etc). In the movie service, parse your JSON and create instances of that object and pass them up to the view controller.
i already solved sender was: cell not self
let cell = tableView.cellForRow(at: indexPath) as! MovieCell
self.performSegue(withIdentifier: "popUp", sender: cell)
and the data passed in the segue
if segue.identifier == "popUp" {
if let cell = sender as? MovieCell{
let destinationVC = segue.destination as! popUpViewController
destinationVC.movieN = cell.title.text!
let url = cell.urlImage + cell.posterPath
destinationVC.posterUrl = url
destinationVC.descriptionText = cell.descriptionMovie
destinationVC.releaseDateText = cell.releaseDate.text!
}
}
This is my first time using swift or Xcode.
I'm trying to make a simple transaction register app
The first view has a table, in which each row represents an account and it's balance.
When you click on a row, it opens up a second view, via segue, which contains a table of all transactions for that account. At the top of this view there is an 'Add Transaction' button, which opens up a third view, that has a form and an 'Add' button. When the 'Add' button is pressed, I use the .reloadData() on the table in the second view and the third view is dismissed.
But, the table, visually, does not have an additional row in it. Which is because after the 3rd view closes, the newly added transaction is no longer in the transactions array.
Am I doing something wrong? My attempt and images are below.
First view
import UIKit
class AccountsViewController: UIViewController {
#IBOutlet weak var newAccountNameUITextField: UITextField!
#IBOutlet weak var newAccountBalanceUITextField: UITextField!
#IBOutlet weak var addNewAccountUIButton: UIButton!
#IBOutlet weak var accountsUITableView: UITableView!
var selectedAccount: Account = Account(name: "", balance: "")
var accounts = [Account(name: "PNC", balance: "45.93")]
override func viewDidLoad() {
super.viewDidLoad()
accountsUITableView.delegate = self
accountsUITableView.dataSource = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let transactionsViewController = segue.destination as? TransactionsViewController {
transactionsViewController.modalPresentationStyle = .fullScreen
transactionsViewController.account = selectedAccount
}
}
}
extension AccountsViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedAccount = accounts[indexPath.row]
performSegue(withIdentifier: "trasactionsSegue", sender: self)
}
}
extension AccountsViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return accounts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "account", for: indexPath) as! AccountCell
cell.selectionStyle = .none
cell.nameUILabel?.text = accounts[indexPath.row].name
cell.balanceUILabel?.text = accounts[indexPath.row].balance
return cell
}
}
Second View
import UIKit
class TransactionsViewController: UIViewController {
#IBOutlet weak var nameUILabel: UILabel!
#IBOutlet weak var TransactionsUITableView: UITableView!
#IBOutlet weak var balanceUILabel: UILabel!
var account: Account = Account(name: "", balance: "", transactions: [])
override func viewDidLoad() {
super.viewDidLoad()
TransactionsUITableView.dataSource = self
nameUILabel.text = account.name
balanceUILabel.text = account.balance
}
//Pass data to newTransactionViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let newTransactionViewController = segue.destination as? NewTransactionViewController {
newTransactionViewController.account = account
}
}
//Dismiss this view when Accounts button is pressed
#IBAction func backToAccountsTouchUpInside(_ sender: UIButton) {
self.dismiss(animated: true, completion: {
self.presentingViewController?.dismiss(animated: true, completion: nil)
})
}
#IBAction func addTransactionTouchUpInside(_ sender: UIButton) {
performSegue(withIdentifier: "addTransactionSegue", sender: self)
}
#IBAction func unwindToViewControllerA(segue: UIStoryboardSegue) {
DispatchQueue.global(qos: .userInitiated).async {
DispatchQueue.main.async {
//At this point the newly added transaction is missing
self.TransactionsUITableView.reloadData()
}
}
}
}
extension TransactionsViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return account.transactions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "transaction", for: indexPath) as! TransactionCell
cell.selectionStyle = .none
cell.descriptionUILabel.text = account.transactions[indexPath.row].description
cell.amountUILabel.text = account.transactions[indexPath.row].amount
cell.balanceUILabel.text = account.transactions[indexPath.row].balanceAfterAmount
return cell
}
}
Third View
import UIKit
class NewTransactionViewController: UIViewController {
#IBOutlet weak var clearedUISegmentedControl: UISegmentedControl!
#IBOutlet weak var depositingUISegmentedControl: UISegmentedControl!
#IBOutlet weak var descriptionUITextField: UITextField!
#IBOutlet weak var amountUITextField: UITextField!
#IBOutlet weak var addTransactionUIButton: UIButton!
var account: Account? = nil
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addTransactionTouchUpInside(_ sender: UIButton) {
let depositing = depositingUISegmentedControl.selectedSegmentIndex == 0 ? true : false
let cleared = clearedUISegmentedControl.selectedSegmentIndex == 0 ? true : false
let description = descriptionUITextField.text
let amount = amountUITextField.text
let balanceAfterAmount = operationOnCurrency(depositing: depositing, amount: amount!, balance: account!.balance)
let newTransaction = Transaction(depositing: depositing, amount: amount!, balanceAfterAmount: balanceAfterAmount, description: description!, cleared: cleared)
account?.transactions.append(newTransaction)
self.performSegue(withIdentifier: "backToTransactions", sender: self)
}
}
func operationOnCurrency (depositing: Bool, amount: String, balance: String) -> String {
//Return empty string for now
return ""
}
The problem is that you are appending a new Transaction in the Account instance that was created in your NewTransactionViewController, rather than updating the data in the instance held by the TransactionsViewController or the root data source in the AccountsViewController (assuming that is the root data source). You need to pass the updated data backwards when the add button is pressed. You can create a delegate protocol to take care of this. Using your transition from NewTransactionViewController to TransactionsViewController example, first create the protocol:
protocol NewTransactionDelegate {
func transactionAddedToAccount(account: Account)
}
Then inside of your NewTransactionViewController you will want to create a delegate property:
class NewTransactionViewController: UIViewController {
#IBOutlet weak var clearedUISegmentedControl: UISegmentedControl!
#IBOutlet weak var depositingUISegmentedControl: UISegmentedControl!
#IBOutlet weak var descriptionUITextField: UITextField!
#IBOutlet weak var amountUITextField: UITextField!
#IBOutlet weak var addTransactionUIButton: UIButton!
var account: Account? = nil
**var delegate: NewTransactionDelegate?**
override func viewDidLoad() {
super.viewDidLoad()
}
And inside of your addTransactionTouchUpInside method call the delegate method:
#IBAction func addTransactionTouchUpInside(_ sender: UIButton) {
let depositing = depositingUISegmentedControl.selectedSegmentIndex == 0 ? true : false
let cleared = clearedUISegmentedControl.selectedSegmentIndex == 0 ? true : false
let description = descriptionUITextField.text
let amount = amountUITextField.text
let balanceAfterAmount = operationOnCurrency(depositing: depositing, amount: amount!, balance: account!.balance)
let newTransaction = Transaction(depositing: depositing, amount: amount!, balanceAfterAmount: balanceAfterAmount, description: description!, cleared: cleared)
account?.transactions.append(newTransaction)
**delegate?.transactionAddedToAccount(account: account)**
self.performSegue(withIdentifier: "backToTransactions", sender: self)
}
Now back in your TransactionsViewController you will want to conform to the NewTransactionDelegate protocol and implement the required method declared in the protocol:
class TransactionsViewController: UIViewController, NewTransactionDelegate {
func transactionAddedToAccount(account: Account) {
self.account = account
tableView.reloadData()
}
Then when you perform the segue to transition from TransactionsViewController to NewTransactionViewController you will want to set the destination view controller's delegate property to self:
//Pass data to newTransactionViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let newTransactionViewController = segue.destination as? NewTransactionViewController {
**newTransactionViewController.delegate = self**
newTransactionViewController.account = account
}
}
Now when the add button is tapped the delegate method is called and passed the new instance of account, which is then passed back to the previous view controller and updated.
Note that this will only update in account instance in the TransactionsViewController and you will also need to update the data for this account at the source or it will be lost when TransactionsViewController is deallocated. Pass the new account back to AccountsViewController, save to device, update database, etc.
I am looking for ideas how to fix a problem I'm having with transposing data from my first view controller to the second view controller. The second view controller is being called when the user selects a table cell.
Code that populates the first tableview
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = mtgRates.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ViewControllerTableViewCell
cell.fiName.text = fetchedFiName[indexPath.row].fiName
cell.oneYear.text = fetchedFiName[indexPath.row].oneYear
cell.twoYear.text = fetchedFiName[indexPath.row].twoYear
cell.threeYear.text = fetchedFiName[indexPath.row].threeYear
cell.fourYear.text = fetchedFiName[indexPath.row].fourYear
cell.fiveYear.text = fetchedFiName[indexPath.row].fiveYear
return (cell)
}
I've watched many youtube videos but they all take a simple approach when setting up the data using an array set globally.
Code that I have been working but does nothing at this point.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
oneYearFound = self.fetchedFiName[indexPath.row].oneYear
twoYearFound = self.fetchedFiName[indexPath.row].twoYear
threeYearFound = self.fetchedFiName[indexPath.row].threeYear
fourYearFound = self.fetchedFiName[indexPath.row].fourYear
fiveYearFound = self.fetchedFiName[indexPath.row].fiveYear
performSegue(withIdentifier: "segue", sender: self)
}
I am thinking my issues is sending the fetched results to the second view controller
Thank you for any help!
More info based on the reply. You are correct I do have two view controllers on the storyboard. The code I have this far my UIViewController is
class SegueViewController: UIViewController {
#IBOutlet weak var V2TwoYear: UILabel!
#IBOutlet weak var V2FiveYear: UILabel!
#IBOutlet weak var V2FourYear: UILabel!
#IBOutlet weak var V2ThreeYear: UILabel!
#IBOutlet weak var V2OneYear: UILabel!
#IBOutlet weak var V2FiName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
V2FiName.text = foundFi[myIndex].fiName
V2TwoYear.text = foundFi[myIndex].twoYear
V2OneYear.text = foundFi[myIndex].oneYear
V2ThreeYear.text = foundFi[myIndex].threeYear
V2FourYear.text = foundFi[myIndex].fourYear
V2FiName.text = foundFi[myIndex].fiveYear
}
Why don't you pass an instance of your fetchedFiName?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedFiName = self.fetchedFiName[indexPath.row]
performSegue(withIdentifier: "segue", sender: selectedFiName)
}
Then, cast your sender as YOUR_FETCHED_FI_NAME_CLASS and pass it to your destination view controller in prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let selectedFiName = sender as? YOUR_FETCHED_FI_NAME_CLASS,
destVC = segue.destination as? SegueViewController {
destVC.passedFiName = selectedFiName
}
}
Then, in your destination viewController and after ViewDidLoad (since your labels will not be loaded before that) you may use your passedFiName to populate your labels.
override func viewDidLoad() {
super.viewDidLoad()
updateLabels()
}
func updateLabels() {
V2FiName.text = passedFiName.fiName
V2TwoYear.text = passedFiName.twoYear
V2OneYear.text = passedFiName.oneYear
V2ThreeYear.text = passedFiName.threeYear
V2FourYear.text = passedFiName.fourYear
V2FiName.text = passedFiName.fiveYear
}
Update:
I continue to have problems with this. I think I am getting closer
Controller one code
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedFiName = self.fetchedFiName[indexPath.row].fiName
let selectedOneYear = self.fetchedFiName[indexPath.row].oneYear
let selectedTwoYear = self.fetchedFiName[indexPath.row].twoYear
let selectedThreeYear = self.fetchedFiName[indexPath.row].threeYear
let selectedFourYear = self.fetchedFiName[indexPath.row].fourYear
let selectedFiveYear = self.fetchedFiName[indexPath.row].fiveYear
passData = [SecondTable(passedFIName: selectedFiName, passedOneYear: selectedOneYear, passedTwoYear: selectedTwoYear, passedThreeYear: selectedThreeYear, passedFourYear: selectedFourYear, passedFiveYear: selectedFiveYear)]
performSegue(withIdentifier: "SecondViewController", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let selectedFiName = sender as! ,
let destVC = segue.destination as? SecondViewController {
destVC.fiName = selectedFiName
}
}
Second View Controller Code`
struct SecondTable {
var passedFIName: String = ""
var passedOneYear: String = ""
var passedTwoYear: String = ""
var passedThreeYear: String = ""
var passedFourYear: String = ""
var passedFiveYear: String = ""
}
class SecondViewController: UIViewController {
#IBOutlet weak var fiName: UILabel!
#IBOutlet weak var sometext: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let fiDetails = SecondTable()
fiName.text = .passedFIName
sometext.text = "Some Text"
}
}
I am getting error messages at` override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
I am nit sure what to put after "Sender as "Missing value"
I have been searching for hours. One I solve this problem. my project will start to move along. Thank you for all the great help!`
I need to make a few assumptions:
You have a storyboard with a seque with the identifier "seque"
You have a view controller at the end of that seque.
You need to send oneYearFound.... all 5 of those values to the SecondViewController. Lets just say that view controller has 5 UILabels that display the five values you want to send.
Sounds like SecondViewController needs some sort of object to hold those values. Define one on that view controller, and then create a prepareForSeque method on your FirstViewController. Source
In prepareForSeque, get the destination view controller off the seque and then pass it the object.
Then use the object to populate the values for the labels in ViewDidAppear or ViewDidLoad.
Edit:
It looks like you're really close. Make sure you dont forget to include the PrepareForSeque method in the first view controller. Inside that method override you can access the second view controllers instance and set those array values.
Here is some code that should help get you thinking.
class LabelsClass {
var str1:String;
var str2:String;
init(firstName:String, secondName:String) {
str1 = firstName;
str2 = secondName;
}
}
class SegueViewController: UIViewController {
var firstLabelString:String = ""
var secondLabelString:String = ""
func setValues(labels:LabelsClass) {
self.firstLabelString = labels.str1;
self.secondLabelString = labels.str2;
}
}
class MessageListViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var first = "Game"
var second = "maniac"
var localLabelsVar = LabelsClass.init(firstName: first,secondName: second);
var destVC = segue.destination as? SegueViewController
destVC?.setValues(labels: localLobalsVar)
}
}
Sources:
How do I use prepareForSegue in swift?
How to pass prepareForSegue: an object
I have looked up many examples and tried to incorporate but have been unsuccessful. In my CollectionView (That has been placed in a ViewController), I'd like to select a cell and push the cell image to another ViewController. The images have been placed in an Array of Dictionaries. I'm not sure, how i should edit both my prepareForSegue or my func collectionView...didSelectItemAtIndexPath. Also, any detailed explanation to go along with your code will be helpful as I'm still learning swift and its syntax.
Below is what i think all the information you need but please let me know if you need more:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "ShowToStory") {
var story = sender as! UICollectionViewCell, indexPath = collectionView.indexPathForCell(story)
}
}
private func initStoryImages() {
var storyArchives = [StoryImages]()
let inputFile = NSBundle.mainBundle().pathForResource("StoryArchive", ofType: "plist")
let inputDataArray = NSArray(contentsOfFile: inputFile!)
for inputItem in inputDataArray as! [Dictionary<String, String>] {
let storyImage = StoryImages(dataDictionary: inputItem)
storyArchives.append(storyImage)
}
storyImages = storyArchives
}
ADDITIONAL CLASS: CollectionViewCell class
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var cellImage: UIImageView!
func setStoryImage(item:StoryImages){
cellImage.image = UIImage(named:item.itemImage)
}
}
ADDITIONAL CLASS: UIViewController
class StoryView: UIViewController{
#IBOutlet weak var ImageToStory: UIImageView!
var story: StoryImages?
override func viewDidLoad() {
super.viewDidLoad()
ImageToStory.image = UIImage(named: (story?.itemImage)!)
}
}
ADDITIONAL CLASS: StoryImages
class StoryImages{
var itemImage: String
init(dataDictionary:Dictionary <String,String>) {
itemImage = dataDictionary["ItemImage"]!
}
class func newStoryImage(dataDictionary:Dictionary<String,String>) -> StoryImages {
return StoryImages(dataDictionary: dataDictionary)
}
}
}
First pass the indexPath of selectedItem in didSelectItemAtIndexPath
Now pass the image in prepareForSegue method like this
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "ShowToStory") {
let cell = sender as! UICollectionViewCell //Change UICollectionViewCell with CustomCell name if you are using CustomCell
let indexPath = self.collectionView?.indexPathForCell(cell)
let story = storyImages[indexPath.row]
let destVC = segue.destinationViewController as! StoryView
destVC.selectedStory = story
}
}
Now declare one StoryImages types of object in your StoryView where you want to pass image and use that object in viewDidLoad to assign image
var selectedStory: StoryImages?
override func viewDidLoad() {
super.viewDidLoad()
ImageToStory.image = selectedStory.itemImage // or use NSData if it contain url string
}
I have one table view with many cell.Each cell have one image and one label.And what i need is when ever user press any cell it have to go to detail viewcontroller.And there i need to show the respective image and label name in my detail view controller.How to do that.
I have done all segue.But in my detail view controller i have one image and label.Now how can i show the image and label name - when i select the any cell from my table view ??
import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet var tableView: UITableView!
var valueToPass:String!
var selectedIndexPath: NSIndexPath = NSIndexPath()
var tableData: [String] = ["Christ Redeemer", "Great Wall of China", "Machu Picchu","Petra","Pyramid at Chichén Itzá","Roman Colosseum","Taj Mahal"]
var arrImageName: [String] = ["ChristRedeemer", "GreatWallOfChina", "MachuPicchu","Petra","PyramidChichenItza","RomanColosseum","TajMahal"]
var tableRate: [String] = ["$120", "$100", "$222","$1000","$500","$900","$2000"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func numberOfSectionsInTableView(tableView: UITableView) ->Int
{
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableData.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CustomTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("CustomTableViewCell") as! CustomTableViewCell
cell.imageVW.image = UIImage(named:self.arrImageName[indexPath.row])
cell.lblName.text = self.tableData[indexPath.row]
cell.rateName.text = self.tableRate[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.selectedIndexPath = indexPath
performSegueWithIdentifier("DetailView", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
let indexPath = self.selectedIndexPath
if (segue.identifier == "DetailView") {
var viewController = segue.destinationViewController as! DetailVC
viewController.data = UIImagePNGRepresentation(UIImage(named:self.arrImageName[indexPath.row])!)!
//viewController.name = self.tableData[[indexPath.row]]
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is my detailvc.swift
import UIKit
class DetailVC: UIViewController {
var data: NSData = NSData()
var name: String = String()
#IBOutlet weak var ImgView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
self.ImgView.image = UIImage(data: data)
//self.detailLabelNamee.text = name
}
}
change your did select with something like this
Declare one gloabal indexPath in ViewContrller like this
var selectedIndexPath: NSIndexPath = NSIndexPath()
Change your didselect like this
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.selectedIndexPath = indexPath
self.performSegueWithIdentifier("DetailView", sender: self)
}
Now in prepareForSegue method add this
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
let indexPath = self.selectedIndexPath
if (segue.identifier == "DetailView") {
var viewController = segue.destinationViewController as! DetailVC
viewController.data = UIImagePNGRepresentation(UIImage(named:self.arrImageName[indexPath.row]))
viewController.name = self.tableData[indexPath.row]
}
}
Now add two global identifier in DetailVC like below
var data: NSData = NSData()
var name: String = String()
Now assign this data and string to imageview and label in viewdid load
change the viewdidload of detailVC like this
override func viewDidLoad() {
super.viewDidLoad()
self.ImgView.image = UIImage(data: data)
self.detailLabelNamee.text = name
}
Hop this will help.
In your didSelectRowAtIndexPath you can just perform the segue and in prepareForSegue you can get the indexPath with self.tableView.indexPathForSelectedRow. And of course it returns an optional and you have to check it first for safety. I think this is the easiest way.
In your didSelectRowAtIndexPath you can get the index path of the selected row and then you can performSegue and pass index path in sender.
In prepareForSeque , from the index path , you can get the image , tableData and tableRate . Which you can pass to detail view . Something like below
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("DetailView", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
let selectedIndexPath = sender as! NSIndexPath
if (segue.identifier == "DetailView") {
var viewController = segue.destinationViewController as! DetailVC
viewController . setValues(tableData:tableData[selectedIndexPath] , rate: tableRate[selectedIndexPath] , imageName:arrImageName[selectedIndexPath])
}
class DetailVC: UIViewController {
var imageName:String!
var tableData:String!
var tableRate:String!
func setValues(tableData:String , rate:String , imageName:String){
imageName = tableData
tableRate = rate
imageName = imageName
}
or
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
let selectedIndexPath = sender as! NSIndexPath
if (segue.identifier == "DetailView") {
var viewController = segue.destinationViewController as! DetailVC
viewController.imageName = self.arrImageName[selectedIndexPath.row]
viewController.tableData = self.tableData[selectedIndexPath.row]
viewController.tableRate = self.tableRate[selectedIndexPath.row]
}
You can declare a variable for store indexpath in that .
var selectedItemIndex = Int()
on click of cell write below lines in didselectowatndexaPath
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
selectedItemIndex = indexPath.row
self .performSegueWithIdentifier(“YourViewController”, sender: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let yourVC = segue.destinationViewController as! YourViewController
yourVC.text = tableData[selectedItemIndex]
yourVC.image = UIImage(named:arrImageName[selectedItemIndex])
}
You are almost there! The line:
performSegueWithIdentifier("DetailView", sender: self)
needs changing. 'self' is passing a reference of the current view controller to the prepareForSegue method. If you change 'self' to 'indexPath', then prepareForSegue will see the cell index path in the 'sender' object and you take what you want from that.
You could do something like:
if (segue.identifier == "yourSegueIdentifer") {
var viewController = segue.destinationViewController as! DetailVC
let indexPath = sender as! NSIndexPath
viewController.passedImageName = self.arrImageName[indexPath.row]
viewController.passedData = self.tableData[indexPath.row]
viewController.passedRate = self.tableRate[indexPath.row]
and in DetailVC:
class DetailVC: UIViewController {
var passedImageName:String!
var passedData:String!
var passedRate:String!