How to access album of photos for each row in UITableView - ios

I have a UITableView with few rows. When I hold on a cell the Camera pop-up and I can take photos and to store them in an album of photos.
Each row can have an album of photos. The problem is that when I click on an album, then every time will open me the album with the last picture made and I don't know how to fix this issue with the indexPath.
Here is my code:
class CustomImg: UIImageView {
var indexPath: IndexPath?
}
class ChecklistVC: UIViewController {
lazy var itemSections: [ChecklistItemSection] = {
return ChecklistItemSection.checklistItemSections()
}()
var lastIndexPath: IndexPath!
var currentIndexPath: IndexPath!
...
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.checklistCell, for: indexPath) as! ChecklistCell
let itemCategory = itemSections[indexPath.section]
let item = itemCategory.checklistItems[indexPath.row]
if item.imagesPath!.isEmpty{
cell.defectImageHeightConstraint.constant = 0
}
else{
let thumbnailImage = loadImageFromDiskWith(fileName: item.imagesPath?.last ?? String())
cell.defectImageView.indexPath = indexPath
cell.defectImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapOnDefectImageView(_:))))
cell.defectImageHeightConstraint.constant = 100
cell.defectImageView.isUserInteractionEnabled = true
cell.defectImageView.image = thumbnailImage
print("For section \(indexPath.section + 1) - row \(String(describing: indexPath.row + 1)) the album photos are: \(String(describing: item.imagesPath))")
}
return cell
}
#objc func tapOnDefectImageView(_ sender: UITapGestureRecognizer){
guard let img = sender.view as? CustomImg, let indexPath = img.indexPath else { return }
currentIndexPath = indexPath
let listImagesDefectVC = storyboard?.instantiateViewController(withIdentifier: "ListImagesDefectID") as! ListImagesDefectVC
let item = itemSections[indexPath.section].checklistItems[indexPath.row]
listImagesDefectVC.listImagesPath = item.imagesPath
listImagesDefectVC.isPhotoAccessedFromChecklist = true
listImagesDefectVC.delegate = self
navigationController?.pushViewController(listImagesDefectVC, animated: true)
}
// A menu from where the user can choose to take pictures for "Vehicle Damage/Defects" or "Trailer Damage/Defects"
func showOptionsForAddPhoto(_ indexPath: IndexPath){
let addPhotoForVehicle = UIAlertAction(title: "Add photo for Vehicle", style: .default) { action in
self.lastIndexPath = indexPath // Get the position of the cell where to add the vehicle photo
self.showCamera(imagePicker: self.imagePicker)
}
let addPhotoForTrailer = UIAlertAction(title: "Add photo for Trailer", style: .default) { action in
self.lastIndexPath = indexPath
self.showCamera(imagePicker: self.imagePicker)
}
let actionSheet = configureActionSheet()
actionSheet.addAction(addPhotoForVehicle)
actionSheet.addAction(addPhotoForTrailer)
self.present(actionSheet, animated: true, completion: nil)
}
// Get the list of the images from ListImagesDefectVC
extension ChecklistVC: ListImagesDefectDelegate {
func receiveListImagesUpdated(imagesFromList: [String]?) {
print("Received Array: \(imagesFromList ?? [])")
let item = itemSections[currentIndexPath.section].checklistItems[currentIndexPath.row]
item.imagesPath = imagesFromList
}
}
}
Here is a GIF with my actual issue. In this capture I click only on Photo 1 and Photo 3. And every time Photo 2 take the value of what I clicked before:
http://g.recordit.co/VMeGZbf7TF.gif
Thank you if you are reading this.

I guess in tapOnDefectImageView you should use the clicked indexPath for the cell not lastIndexPath which is the reason why clicking a row shows photos of last clicked indexPath
so either add this gesture inside the cell and in the action method do
delegate?.tapOnDefectImageView(self) //// self = cell
and use
#objc func tapOnDefectImageView(_ gest:ChecklistCell){
guard let indexPath = tableView.indexPath(cell) else { return }
let listImagesDefectVC = storyboard?.instantiateViewController(withIdentifier: "ListImagesDefectID") as! ListImagesDefectVC
let item = itemSections[indexPath.section].checklistItems[indexPath.row]
listImagesDefectVC.listImagesPath = item.imagesPath
listImagesDefectVC.isPhotoAccessedFromChecklist = true
listImagesDefectVC.delegate = self
navigationController?.pushViewController(listImagesDefectVC, animated: true)
}
or create
class CustomImg:UIImageView {
var indexPath:IndexPath?
}
with this inside cellForRowAt
cell.defectImageView.indexPath = indexPath
cell.defectImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapOnDefectImageView)))
then assign the class to the imageView of the cell and now you can do
#objc func tapOnDefectImageView(_ sender:UITapGestureRecognizer){
guard let img = sender.view as? CustomImg , let indexPath = img.indexPath else { return }
let listImagesDefectVC = storyboard?.instantiateViewController(withIdentifier: "ListImagesDefectID") as! ListImagesDefectVC
let item = itemSections[indexPath.section].checklistItems[indexPath.row]
listImagesDefectVC.listImagesPath = item.imagesPath
listImagesDefectVC.isPhotoAccessedFromChecklist = true
listImagesDefectVC.delegate = self
navigationController?.pushViewController(listImagesDefectVC, animated: true)
}

Related

Saved object array not showing up in new ViewController

Every time my add button is pressed I am attempting to append that specific item into an array of Item. It prints in the console for each new cell, however when I push to a new ViewController, which will be a summary of all the items added, it does not print the items. Only an empty array is printed.
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, PostCellDelegate {
var finalList = [Item]()
#objc func addTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.removeButton.isHidden = false
let item = itemsArr[indexPath.row]
finalList.append(item)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! PostCell
cell.delegate = self
let item = itemsArr[indexPath.row]
cell.set(name: item.name, brand: item.brand, price: item.price)
print(finalList)
return cell
}
#objc private func handleNext() {
let nextIndex = min(pageControl.currentPage + 1, itemsArr.count - 1)
let indexPath = IndexPath(item: nextIndex, section: 0)
if pageControl.currentPage == 4{
let checkoutView = FinishViewController()
self.navigationController?.pushViewController(checkoutView, animated: true)
checkoutView.modalPresentationStyle = .overCurrentContext
present(checkoutView, animated: true)
print("last item")
}else {
print("not last")
}
pageControl.currentPage = nextIndex
collectionView?.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
lazy var pageControl: UIPageControl = {
let pc = UIPageControl()
pc.currentPage = 0
pc.numberOfPages = 4
pc.currentPageIndicatorTintColor = .red
pc.pageIndicatorTintColor = UIColor(red: 249/255, green: 207/255, blue: 224/255, alpha: 1)
return pc
}()
class FinishViewController: UIViewController {
let cV = CollectionViewController()
override func viewDidLoad() {
print(cV.finalList)
super.viewDidLoad()
view.backgroundColor = .red
}
The issue is that in the FinishViewController you are initializing a new instance of CollectionViewController, so the property has the default empty array value.
You need to pass in the array to the FinishViewController when you present or segue to it.
In FinishViewController add the following:
var finalList = [Item]()
And in handleNext make sure you set finalList correctly:
let checkoutView = FinishViewController()
checkoutView.finalList = self.finalList
#objc func addTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else
{return}
hiddenRows.insert(indexPath.row)
cell.removeButton.isHidden = false
let item = itemsArr[indexPath.row]
finalList.append(item)
collectionView?.reloadData() <---- add this
}

Passing selected Image in cell image

My application consists of 2 tap bars.
First tap is TableView and second - VC.
When I'm on first VC, I selected picture and press the button, which should transfer the selected picture on cell image.
I'm trying to save image in UserDefaults but then it turns out that all lines are with the same picture
In my VC, I save TextField by this method:
func saveButton() {
let itemsObject = UserDefaults.standard.object(forKey: "items")
var items:[String]
if let tempItems = itemsObject as? [String] {
items = tempItems
items.append(nameTextField.text!)
} else {
items = [nameTextField.text!]
}
UserDefaults.standard.set(items, forKey: "items")
nameTextField.text = ""
navigationController?.popViewController(animated: true)
}
And choose the image
Then I tap "Save" button
In my Table view:
override func viewDidAppear(_ animated: Bool) {
// Load Item Func
let itemsObject = UserDefaults.standard.object(forKey: "items")
if let tempItems = itemsObject as? [String] {
items = tempItems
}
table.reloadData()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CellPrototype
// Configure the cell...
cell.pointNameLabel.text = items[indexPath.row]
return cell
}
You can pass image by creating a variable in Second VC like this
UITableView Delegate Method
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as? YourCell
if let DetailVC = self.storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as? DetailViewController{
DetailVC.Selected_image = cell.YourImageView.image
self.navigationController?.pushViewController(DetailVC,animated: true)
}
}
Create Variable in Second VC
class DetailViewController: UIViewController {
var Selected_image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
print(Selected_image)
}
}
You can pass the image from cell to detailViewController here is partail pseudo Code
tableView (cell , didSelectCellAtIndexPath) {
if let cell = cell as? CustomCell {
let image = cell.image
let detailVC = DetailViewController(..) // instantiate from storyboard
detailVC.selectedImage = image
navigationViewController.push(detailVC)
}
}
Class DetailVC: UIViewController {
var selectedImage: UIImage!
// can access image anywhere in this class after passing
}

How to update Core Data assigned to indexpath in Custom cell via button

I have an app of type "to do list" where I want to check my item on the list with custom button checkmark. Structure of app looks like that: main list uses UITableViewController which has custom TableViewCell with UIButton "checkmarkButton" and Label "cellLabel". Their values are saved to core data after creating, with checkmarkButton default state as false, and are assigned to a cell in cellForRowAtindexPath using value(forKeyPath:). In TableViewCell class I have a checkmarkBtnTapped function which changes displayed image for a button (check/uncheck), tint this image to a specified color, and update button "state" as bool in CoreData attribute for its key path, fetch CoreData and reloadTableView. Some functions that use my list array and other stuff from core data, or table view come from UITableViewController so I implemented delegate for them.
The problem is when I tap the checkmarkButton and it uses updateBtnState new row is created with changed state (i.e. i have tapped row with button in state "false", my actual row with label is still on "false" and new row is added with empty label and button in state "true") i guess this is due to updateBtnState() method that reference only to managedObjectContext and not to indexPath. But when I try to reference item as a point of indexPath and not NSManagedObject i cannot pass this function to TableViewCell class due to IndexPath parameter. Below in TableViewController.swift i left updateBtnState2() that i think could solve my problem but is unusable in TableViewCell checkmarkBtnTapped() function
TableViewController.swift
import UIKit
import CoreData
class TableViewController: UITableViewController, ButtonSelectionDelegate {
var list: [NSManagedObject] = []
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "List"
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addTapped))
let cellNib = UINib(nibName: "TableViewCell", bundle: nil)
self.tableView.register(cellNib, forCellReuseIdentifier: "cell")
}
override func viewWillAppear(_ animated: Bool) {
UIApplication.shared.statusBarStyle = .lightContent
fetch()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
UIApplication.shared.statusBarStyle = UIStatusBarStyle.default
}
func save(name: String, state: Bool) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedObjectContext = appDelegate.persistentContainer.viewContext
let entity =
NSEntityDescription.entity(forEntityName: "Item", in: managedObjectContext)!
let Item = NSManagedObject(entity: entity, insertInto: managedObjectContext)
Item.setValue(name, forKeyPath: "name")
Item.setValue(state, forKeyPath: "isChecked")
do{
try managedObjectContext.save()
list.append(Item)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
func fetch(){
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedObjectContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Item")
do{
list = try managedObjectContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
func updateBtnState(state: Bool){
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedObjectContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Item", in: managedObjectContext)!
let Item = NSManagedObject(entity: entity, insertInto: managedObjectContext)
Item.setValue(state, forKeyPath: "isChecked")
do{
try managedObjectContext.save()
} catch let error as NSError {
print("Couldnt update. \(error)")
}
}
func updateBtnState2(indexPath: IndexPath, state: Bool){
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedObjectContext = appDelegate.persistentContainer.viewContext
let item = list[indexPath.row]
item.setValue(state, forKeyPath: "isChecked")
do{
try managedObjectContext.save()
list[indexPath.row] = item
} catch let error as NSError {
print("Couldnt update. \(error)")
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return(list.count)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
let item = list[indexPath.row]
cell.selectionDelegate = self
cell.cellLabel.text = item.value(forKeyPath: "name") as? String
cell.checkmarkButton.isSelected = item.value(forKeyPath: "isChecked") as! Bool
return cell
}
func updateTableView(){
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
#objc func addTapped(){
let alert = UIAlertController(title: "New Name", message: "Add a new name", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Save", style: .default) {
[unowned self] action in
guard let textField = alert.textFields?.first,
let nameToSave = textField.text else {
return
}
self.save(name: nameToSave, state: false)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default)
alert.addTextField()
alert.addAction(cancelAction)
alert.addAction(saveAction)
present(alert, animated: true)
}
TableViewCell.swift
import UIKit
protocol ButtonSelectionDelegate: class {
func fetch()
func updateTableView()
func updateBtnState(state: Bool)
}
class TableViewCell: UITableViewCell {
weak var selectionDelegate: ButtonSelectionDelegate!
#IBOutlet var checkmarkButton: UIButton!
#IBOutlet var cellLabel: UILabel!
#IBAction func checkmarkBtnTapped(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected {
selectionDelegate?.updateBtnState(state: true)
let image: UIImage? = UIImage(named: "done_icon.png")?.withRenderingMode(.alwaysTemplate)
checkmarkButton.setImage(image, for: .normal)
checkmarkButton.tintColor = UIColor( red: CGFloat(21/255.0), green: CGFloat(126/255.0), blue: CGFloat(251/255.0), alpha: CGFloat(1.0))
selectionDelegate?.fetch()
selectionDelegate?.updateTableView()
print("checkmarkButton pressed to done")
} else {
selectionDelegate?.updateBtnState(state: false)
let image: UIImage? = UIImage(named: "undone_icon.png")?.withRenderingMode(.alwaysTemplate)
checkmarkButton.setImage(image, for: .normal)
checkmarkButton.tintColor = UIColor.gray
selectionDelegate?.fetch()
selectionDelegate?.updateTableView()
print("checkmarkButton pressed to undone")
}
}
override func layoutSubviews() {
super.layoutSubviews()
let image: UIImage? = UIImage(named: "undone_icon.png")?.withRenderingMode(.alwaysTemplate)
checkmarkButton.setImage(image, for: .normal)
checkmarkButton.tintColor = UIColor.gray
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
extension UIButton {
func hasImage(named imageName: String, for state: UIControlState) -> Bool {
guard let buttonImage = image(for: state), let namedImage = UIImage(named: imageName) else {
return false
}
return UIImagePNGRepresentation(buttonImage) == UIImagePNGRepresentation(namedImage)
}
}
The best way to work with table and collection view cells is to have the cell take care of all configuration of its UI. To do this you pass the data in to the cell and it then puts the right data in the right data formatted as desired. You already have a custom UITableViewCell so this will be pretty easy to do…
// Make the to-do item's property names into strings so you can't mistype them later.
// The other option would be to create the subclass of NSManagedObject so the properties are directly accessible.
let isChecked = "isChecked"
let name = "name"
class TableViewCell: UITableViewCell {
// Add a property to hold the actual to-do item
var item: NSManagedObject? {
didSet {
updateCell()
}
}
// Make all outlets private so you aren't tempted to touch them from outside
#IBOutlet private var checkmarkButton: UIButton!
#IBOutlet private var cellLabel: UILabel!
// Create both images once for each cell rather than every time the image changes
let doneImage = UIImage(named: "done_icon.png")?.withRenderingMode(.alwaysTemplate)
let notDoneImage = UIImage(named: "undone_icon.png")?.withRenderingMode(.alwaysTemplate)
let doneColor = UIColor( red: CGFloat(21/255.0), green: CGFloat(126/255.0), blue: CGFloat(251/255.0), alpha: CGFloat(1.0))
let notDoneColor = UIColor.gray
private func updateCell() {
guard let item = item else { return }
cellLabel?.text = item.value(forKeyPath: name) as? String
checkmarksButton?.isSelected = item.value(forKeyPath: isChecked) as! Bool
}
#IBAction private func checkmarkBtnTapped(_ sender: UIButton) {
// Safely unwrap the to-do item
guard let item = item else { return }
sender.isSelected = !sender.isSelected
let selected = sender.isSelected
item.setValue(selected, forKeyPath: "isChecked")
checkmarkButton.setImage(selected ? doneImage : notDoneImage, for: .normal)
checkmarkButton.tintColor = selected ? doneColor : notDoneColor
print("checkmarkButton pressed to \(selected ? "done" : "undone")")
}
…
}
This way the cell can update the managed object directly rather than trying to reconnect to it through the view controller.
Also, the code in layoutSubviews shouldn't really be needed but if it is awakeFromNib is the better place to put it.
Once you cell is done you can get rid of those update button functions and change cellForRowAt to…
class TableViewController: UITableViewController {
…
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
let item = list[indexPath.row]
cell.item = item
return cell
}
…
}

addTarget to button action within nib of collection view

I have a keyboard extension in iOS 11 that includes a collection view of articles coming in from JSON. I have a button in the prototype cell that I would like to allow a user to press to open the article in Safari external to the keyboard. I can get it to open all links in a static URL, but I cant get it to open each article's URL. What am I missing?
I've put an example of the working simple static action and also included what I have tried but doesn't work in this code:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if(collectionView == self.key.colImages)
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gifCollectionViewCell", for: indexPath) as! gifCollectionViewCell
cell.lblTitle.text = self.articles[indexPath.row].headline
let prefix: String = "https://res.cloudinary.com/djvbbwrnm/image/fetch/"
let options: String = "w_0.2/"
if let imageURL = self.articles[indexPath.row].imageURL
{
let articleURL = self.articles[indexPath.row].url
let url = URL(string: articleURL!)
let urlAppended = prefix+options+imageURL
cell.imgView.sd_setImage(with: URL(string: urlAppended), completed: nil)
//This works
cell.shareButton.addTarget(self, action: #selector(openLink), for: .touchUpInside)
//This doesn't
cell.shareButton.addTarget(self, action: #selector(openUrl(url: url)), for: .touchUpInside)
}
return cell
}
else
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "catCollectionViewCell", for: indexPath) as! catCollectionViewCell
cell.imgView.image = buttPics[indexPath.row]
cell.imgView.layer.cornerRadius = 2
cell.imgView.layer.masksToBounds = true
return cell
}
}
#objc func openLink(){
let articleURL = "http://google.com"
let url = URL(string: articleURL)
openUrl(url: url)
}
#objc func openUrl(url: URL?) {
let selector = sel_registerName("openURL:")
var responder = self as UIResponder?
while let r = responder, !r.responds(to: selector) {
responder = r.next
}
_ = responder?.perform(selector, with: url)
}
You cant add any other DataTypes as arguments. Because, you are adding addTarget for UIButton.
#objc func openLink(){
}
#objc func openLink(sender: UIButton){ // URL is not possible.
}
The above two codes are same. In second one, you can access that UIButton's property.
Runnable Code
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if(collectionView == self.key.colImages)
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gifCollectionViewCell", for: indexPath) as! gifCollectionViewCell
cell.lblTitle.text = self.articles[indexPath.row].headline
let prefix: String = "https://res.cloudinary.com/djvbbwrnm/image/fetch/"
let options: String = "w_0.2/"
if let imageURL = self.articles[indexPath.row].imageURL
{
//let articleURL = self.articles[indexPath.row].url
//let url = URL(string: articleURL!)
let urlAppended = prefix+options+imageURL
cell.imgView.sd_setImage(with: URL(string: urlAppended), completed: nil)
//This works
cell.shareButton.addTarget(self, action: #selector(openLink), for: .touchUpInside)
//This doesn't
//cell.shareButton.addTarget(self, action: #selector(openUrl(url: url)), for: .touchUpInside)
cell.shareButton.tag = indexPath.row // SET TAG TO UIBUTTON
}
return cell
}
else
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "catCollectionViewCell", for: indexPath) as! catCollectionViewCell
cell.imgView.image = buttPics[indexPath.row]
cell.imgView.layer.cornerRadius = 2
cell.imgView.layer.masksToBounds = true
return cell
}
}
#objc func openLink(sender: UIButton){ // USE THIS.
let buttonTag : Int = sender.tag
let articleURL = self.articles[buttonTag].url
let url = URL(string: articleURL!)
// You can achieve by this way.
// Since I am in a keyboard extension, I added the follwoing code and it is working now.
let selector = sel_registerName("openURL:")
var responder = self as UIResponder?
while let r = responder, !r.responds(to: selector) {
responder = r.next
}
_ = responder?.perform(selector, with: url)
}

Swift Passing Data in a tableview cell to a view controller

I seem to be running into an issue and don't know where I am going wrong. I am trying to send the user's post to another view controller through a button, and my app keeps crashing. Below is the code for the tableview cell and the the prepare for segue. I am still new to the app development world. So I am sorry if my code is a little messy. Also I am using Parse as my backend. I don't know if that makes a difference.
thank you in advanced!
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TimelineTableViewCell
let userPost: PFObject = self.userObjects.objectAtIndex(indexPath.row) as! PFObject
cell.backgroundColor = UIColor.darkGrayColor().colorWithAlphaComponent(0.2)
cell.layer.borderColor = UIColor.lightGrayColor().CGColor
cell.layer.borderWidth = 0.5
cell.reportPost.tag = indexPath.row
cell.reportPost.addTarget(self, action: "buttonAction", forControlEvents: .TouchUpInside)
// Display Users Post's
var userText = userPost["text"] as? String
cell.TextLabel!.text = userText
// Display Date and Time
var Date: NSDateFormatter = NSDateFormatter()
Date.dateFormat = "MM-dd HH:mm"
cell.dateLabel.text = Date.stringFromDate(userPost.createdAt!)
// Display username
var findUser:PFQuery = PFUser.query()!
let Id = userPost.objectForKey("username")?.objectId as NSString!
findUser.whereKey("objectId", equalTo: Id)
findUser.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil{
var user:PFUser = (objects as NSArray!).lastObject as! PFUser
cell.userLabel.text = user.username
// Getting User's Profile Image
var initialThumbnail = UIImage(named: "PAHS_Logo")
cell.userImage.image = initialThumbnail
if let PhotoFile = user["ProfileImage"] as? PFFile {
PhotoFile.getDataInBackgroundWithBlock{
(ImageData:NSData?, error:NSError?)->Void in
if error == nil{
let Image:UIImage = UIImage(data: ImageData!)!
cell.userImage.image = Image
}else{
cell.userImage.image = initialThumbnail
}
}
}
}
}
return cell
}
func buttonAction(sender: UIButton!){
let titleString = self.userObjects.objectAtIndex(sender.tag) as? String
let firstActivityItem = "\(titleString)"
let activityViewController: UIActivityViewController = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil)
self.presentViewController(activityViewController, animated: true, completion: nil)
}
var valueToPass: String!
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
valueToPass = currentCell.textLabel?.text
performSegueWithIdentifier("reportPost", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "reportPost")
{
var viewController: reportViewController = segue.destinationViewController as! reportViewController
let indexPath = self.tableView!.indexPathsForSelectedRows();
let titleString = userObjects.objectAtIndex(indexPath!.count) as! String
viewController.titleString = titleString
viewController.titleLabel.text = valueToPass //fatal error: unexpectedly found nil while unwrapping an Optional value
self.presentViewController(viewController, animated: true, completion: nil)
}
}
You can change performSegueWithIdentifier("reportPost", sender: self) to performSegueWithIdentifier("reportPost", sender: indexPath) Then, in prepareForSegue(), the sender is going to be the indexPath. You can change your prepareForSegue() to this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "reportPost")
{
var viewController: reportViewController = segue.destinationViewController as! reportViewController
let indexPath = sender as! NSIndexPath
let titleString = userObjects[indexPath.row] as! String // I am assuming userObjects is the object containing the data you want
viewController.titleString = titleString
let cell = tableView.cellForRowAtIndexPath(indexPath) as! UITableViewCell
let valueToPass = cell.textLabel?.text
viewController.titleLabel.text = valueToPass
self.presentViewController(viewController, animated: true, completion: nil)
}
}
If you do it this way, you can get rid of var valueToPass: String! above tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)

Resources