I have an iOS project I'm working on using Xcode7 and Swift2. I have a PDF that is saving to Parse. It is saving to a Parse Class called IncomingRecipe. In this Class is a FileName column with type as a String. It also has a column called PDFData and is type PFFile. I want it so when the user clicks on a TableViewCell it segues to a new View Controller and displays the PDF in a WebView.
Currently these fileNames are in a TableView. I have a segue that goes to the View Controller with the WebView. It passes along the name of the fileName from the TableViewCell as a Global variable.
My query for the data for the TableView code for parse is:
var fileName = [String]()
var PDFData = [PFFile]()
var getRecipeQuery = PFQuery(className: "IncomingRecipe")
// Match the query with only items that the current user uploaded
getRecipeQuery.whereKey("userId", equalTo: appUserId)
getRecipeQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
// Check to see if 'objects' exist
if let objects = objects {
for object in objects {
// An AnyObject that needs to be cast as a String
var recipeName = object["fileName"] as! String
self.fileName.append(object["fileName"] as! String)
self.objectid.append(object["objectid"] as! String)
self.userId.append(object["userId"] as! String)
self.PDFData.append(object["PDFData"] as! PFFile)
self.myFilesTable.reloadData()
}
}
}
The TableView loads the fileName as:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "PDFTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! PDFTableViewCell
cell.textLabel?.text = fileName[indexPath.row]
return cell
}
I have the code for passing the selected cell fileName to a Global variable as:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ItemView" {
let savedRecipedView = segue.destinationViewController as! PDFItemViewController
if let selectedRecipeCell = sender as? PDFTableViewCell {
let indexPath = myFilesTable.indexPathForCell(selectedRecipeCell)!
viewingPDFRecipe = fileName[indexPath.row]
print("Sending Click: \(viewingPDFRecipe)")
}
}
}
How can I get the PFFile of the PDA and display it in the WebView on the other View Controller? I can't figure out how to get all of this into a URL to be used with the WebView. I looked here and tried to implement this with mine, with no success. Thank you.
Don't just save the file name with
self.fileName.append(object["fileName"] as! String)
save the whole list of PFObjects. These objects will contain the file name (that you can use for the table view) and the PFFile reference (that you can use on drill down). Also, you don't appear to, but you shouldn't pass by global. It just looks like you're passing the file name in the segue.
Instead of passing the file name you should pass the whole PFObject. Then, in the destination view controller you can extract the PFFile reference and the URL it contains.
Related
I have a table view controller that is filled with data that is being pulled from a JSON file. This table view controller segues to another table view controller that is pulling from the same JSON file. I want the information that loads into the second view controller to change based on what table cell was clicked on in the first table view controller.
For example: If my first table view controller listed states (Alabama, Alaska, Arizona, etc) and Alabama was clicked on, it would return a list of cities in Alabama. However, if Alaska is clicked on, then the second table view controller would show cities that are in Alaska instead.
I am not exactly sure how to even begin here but here is my code first table view controller didSelectRowAtIndexPath function:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var industry: Industry!
if inSearch{
industry = filteredSearch[indexPath.row]
}
else{
industry = industryOfMifi[indexPath.row]
}
performSegueWithIdentifier("IndustryPush", sender: industry)
}
And here is the code that is loading the appropriate information in the second table view controller:
func parseJSON(){
do{
let data = NSData(contentsOfURL: NSURL(string: "https://jsonblob.com/api/jsonBlob/580d0ccce4b0bcac9f837fbe")!)
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
for anItem in jsonResult as! [Dictionary<String, AnyObject>]{
let industry = anItem["mediaIndustry"] as! String
if industry == "Interactive Media" {
let mifiIndustry = anItem["name"] as! String
print(mifiIndustry)
let mifiId = anItem["employeeId"] as! Int
let newIndustry = Name(mifiName: mifiIndustry, mifiId: mifiId)
industryOfMifi.append(newIndustry)
}
else if industry == "Newspaper" {
let mifiIndustry = anItem["name"] as! String
print(mifiIndustry)
let mifiId = anItem["employeeId"] as! Int
let newIndustry = Name(mifiName: mifiIndustry, mifiId: mifiId)
industryOfMifi.append(newIndustry)
}
else if industry == "Radio" {
let mifiIndustry = anItem["name"] as! String
print(mifiIndustry)
let mifiId = anItem["employeeId"] as! Int
let newIndustry = Name(mifiName: mifiIndustry, mifiId: mifiId)
industryOfMifi.append(newIndustry)
}
}
}
catch let error as NSError{
print(error.debugDescription)
}
}
It's a little difficult to tell what's going on here, but in essence what you want to do is pass a reference to the selected Industry value to the 2nd Table View Controller.
Here's one way to do that. First, create a class-level variable for Industry in the 2nd table VC.
class SecondTableViewController: UITableViewController {
var industry: Industry?
}
Second, make use of the prepareForSegue() method in your 1st Table VC to pass the instance of Industry to the new view controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "IndustryPush" {
let secondTableViewController = segue.destinationViewController as! SecondTableViewController
secondTableViewController.industry = sender as! Industry
}
}
Alternatively, you can create a reference to the selected industry in your first VC. (You set that in the didSelectRowAtIndexPath function.) And then you can pass THAT to the second VC in prepareForSegue().
From there, it's on you to figure out how to use the Industry instance to filter your JSON. It's a little hard to tell from what you've posted.
Hope that helps.
So I am building a notes app and have tried everything but can not figure this issue out. I have 3 UIViewController's. When you click a button on the first UIViewController, it shoots you over to the third one which consist of a UITextView, UITextField and a Static UILabel which gets updated with some information. When you fill out these fields and tap the back button it brings you to the second view controller which is a table that gets updated with this information.
The issue is: when I tap the UITableViewCell it loads the information back to the third view controller so the user can edit his notes but when I come back to the UITableView it creates a brand new cell instead of just updating the old one.
If I could just update my array with the same object I sent back to be edited by the user I think this issue would be solved but I have no idea how to do this. Thanks for the help!
VC2 - this is how I am sending my data from the tableView to the textView Back
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let nextView = segue.destinationViewController as! TextViewController
guard let indexPath = self.tableView.indexPathForSelectedRow else {
print("Didnt work")
return
}
let dataToSendBackToBeEdited = textViewObjectData[indexPath.row]
print(dataToSendBackToBeEdited.dreamText)
print(dataToSendBackToBeEdited.dreamTitle)
print(dataToSendBackToBeEdited.dreamType)
print(dataToSendBackToBeEdited.description)
print(dataToSendBackToBeEdited)
nextView.objectSentFromTableViewCell = dataToSendBackToBeEdited
}
This is how I am saving the information the the user taps back to go to the tableView
func determineSave() {
guard var savedDreamArray = NSKeyedUnarchiver.unarchiveObjectWithFile(TextViewController.pathToArchieve.ArchiveURL.path!) as? [Dream] else {
//First dream object saved
let dreamObject = Dream(dreamTitle: titleForDream.text!, dreamType: typeOfDreamLabel.text!, dreamText: textView.text, currentDate: NSDate())
dreamArray.append(dreamObject)
NSKeyedArchiver.archiveRootObject(dreamArray, toFile: pathToArchieve.ArchiveURL.path!)
return
}
//print(savedDreamArray.count)
//print(savedDreamArray.first!.dreamTitle)
let dreamObject = Dream(dreamTitle: titleForDream.text!, dreamType: typeOfDreamLabel.text!, dreamText: textView.text!, currentDate: NSDate())
savedDreamArray.append(dreamObject)
NSKeyedArchiver.archiveRootObject(savedDreamArray, toFile: pathToArchieve.ArchiveURL.path!)
}
I was having this issue as well. Came in here, and got the answer. The array!
I was appending the array as well, which apparently was causing duplicate cells to appear.
I just reset my array back to empty before I retrieved the data again and reloaded the table.
I'm using Firebase so my code looks like this:
DataService.instance.dateProfileRef.observeEventType(FIRDataEventType.Value) { (snapshot: FIRDataSnapshot)
in
//have to clear the array here first before reloading the data. otherwise you get duplicates
self.posts = [] //added this line
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
let snapUserID = (snap.childSnapshotForPath("userID").value!)
if snapUserID as? String == USER_ID {
if let profileDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let post = Post(postKey: key, postData: profileDict)
self.posts.append(post)
You do a .append() on your array, this will add a new cell.
You must find the index of the object you want to update and then assign it:
array[index] = newObject
I have a one to many relationship from Set to Card for a basic Flashcard App modelled in my Core Data.
Each Set has a set name, set description, and a relationships many card1s. Each Card1 has a front, back, and photo. In my table view, I've managed to retrieve all saved Sets from core data and display them. Now I want to fetch each Set's cards when a user clicks on the appropriate cell in my next view controller.
This is my code for the table view controller:
// MARK: Properties
var finalArray = [NSManagedObject]()
override func viewDidLoad() {
getAllSets()
println(finalArray.count)
}
func getAllSets() {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName:"Set")
var error: NSError?
let fetchedResults = managedContext.executeFetchRequest(fetchRequest,error: &error) as? [NSManagedObject]
println("Am in the getCardSets()")
if let results = fetchedResults {
finalArray = results
println(finalArray.count)
}
else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}
// MARK: Displaying the data
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return finalArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! SetTableViewCell
let sets = finalArray[indexPath.row]
cell.setName.text = sets.valueForKey("setName")as? String
cell.setDescription.text = sets.valueForKey("setDescription")as? String
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" {
let dest = segue.destinationViewController as! Display
// Get the cell that generated this segue.
if let selectedCell = sender as? SetTableViewCell {
let indexPath = tableView.indexPathForCell(selectedCell)!
let selectedSet = finalArray[indexPath.row]
dest.recievedSet = selectedSet
}
}
}
In my destination view controller, how would I go about retrieving all the cards in that the recievedSet? I've tried converting the NSSet to an array and casting it to a [Card1] array but when I attempt to display the first Card1's front String property onto the label, the app crashes, giving me the error
CoreData: error: Failed to call designated initializer on NSManagedObject class 'NSManagedObject'
fatal error: Array index out of range
This is my code for the detailed viewController.
#IBOutlet weak var front: UILabel!
var finalArray = [Card1]()
finalArray = retrievedSet.allObjects as![Card1]
front.text = finalArray[0].front
Give your detail controller a property of type CardSet (I use "CardSet" because "Set" is a Swift built-in type name). You pass the selected set to this controller.
You could have a property by which you sort, or generate an array without a particular order with allObjects.
var cardArray = [Card1]()
var cardSet: CardSet?
viewDidLoad() {
super.viewDidLoad()
if let validSet = cardSet {
cardArray = validSet.cards.allObjects as! [Card1]
}
}
Your code is not working because finalArray is of type [CardSet], so finalArray[indexPath.row] is of type CardSet which is not transformable into type NSSet. Rather the relationship to Card1s is the NSSet you are looking for.
Finally, I recommend to give the detail controller a NSFetchedResultsController, have an attribute to sort by and use the passed CardSet in the fetched results controller's predicate.
I have an UITableView, this is its cellForRowAtIndexPath and its numberOfRowsInSection:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("customTableViewCell") as! UITableViewCell
let task = frc.objectAtIndexPath(indexPath) as! Task
cell.textLabel?.text = task.summary
var detail = task.detail
var context = task.context
var due = task.date
var status = task.status
var responsible = task.responsable
var folder = task.folder
cell.detailTextLabel?.text = "Contexte: \(context), Detail: \(detail), Status: \(status), Ending date: \(due)"
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let numberOfRowsInSection = frc.sections?[section].numberOfObjects
return numberOfRowsInSection!
}
What I'm trying to do is , when I click on a line, it opens a detail view of the line, so I try to pass datas with a prepareForSegue but I only succeed to send on the segue datas from my database and not datas from the selected line, like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier{
//On vérifie que l'identifier est le bon, permet d'envoyer qu'à la View qu'on veut si a le risque d'envoyer à plusieurs. Si on veut envoyer ailleurs, il suffit de créer la vue en question et de rajouter un "case" avec le nom du nouvel identifier.
switch identifier {
case "Show Detail":
//Creation du lien vers la base SQLite
let entityDescription = NSEntityDescription.entityForName("Task", inManagedObjectContext: self.context!)
let request = NSFetchRequest()
request.entity = entityDescription
var cell: UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("customTableViewCell") as! UITableViewCell
var error: NSError?
var objects = self.context?.executeFetchRequest(request, error: &error)
let match = objects![0] as! NSManagedObject
let editTaskVC = segue.destinationViewController as! EditTaskViewController
if let indexPath = self.tableView.indexPathForCell(sender as! UITableViewCell){
editTaskVC.Name = match.valueForKey("summary") as! String
editTaskVC.Detail = match.valueForKey("detail") as! String
editTaskVC.Status = match.valueForKey("status") as! String
editTaskVC.Owner = match.valueForKey("responsable") as! String
editTaskVC.Context = match.valueForKey("context") as! String
editTaskVC.IdValue = match.valueForKey("id") as? String
editTaskVC.Field = match.valueForKey("folder") as! String
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
let date = dateFormatter.dateFromString(match.valueForKey("date") as! String)
editTaskVC.EndDatePicker?.date = date!
}
default: break
}
}
}
What I try to do, is sending to the destinationViewController datas from the just clicked row and not the database, something like this:
editTaskVC.Name = cell.textLabel?.text
I have searched on the net and saw some solutions, like using the didSelectRowAtIndexPath but without success.
You have a number of problems with your approach.
For starters you should not be calling dequeueReusableCellWithIdentifier outside of your cellForRowAtIndexPath method.
Next, you basic approach is wrong.
View objects do not store state, they display it and collect input from the user. A table view cell can be scrolled off the screen at any time and it's current settings will be lost. When the user makes changes to the values of a cell you need to save that to a model object that stores the current state of you data.
Normally this means saving changes to your database if that's what you're using as a model. If you want the changes to stay pending until the user clicks a save button, or for some other reason are not ready to save the changes to your database then you need to save them to some data model specific to the table view. An array managed by the view controller works well for this.
In any case, when the user taps a cell, you should be looking in the data for your table view for the data to pass on to the detail controller, not trying to read it from the cell.
What you want to do is to save the indexPath of the tapped cell to an instance variable. Then in prepareForSegue, if you figure out that this is a segue triggered by the user tapping a cell, look at that indexPath and fetch the appropriate data for that indexPath to pass on to the next view controller.
I think that you destination view controller is not affected because you do:
if let indexPath = self.tableView.indexPathForCell(sender as! UITableViewCell){
//code
}
This condition is always false.
Remove this condition and normally it should work.
NB: see Duncan C post.
Im writing a table view controller using an array from Parse. I need to send data through the segue but I dont know why its always returning nil. I will have to send images and text, but for now im just taking the text from the label title1 to insert it into a variable type String named example.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toDetail" {
var detailScene = segue.destinationViewController as! detailViewController
let cell = sender as! UITableViewCell
let indexPath = tableView?.indexPathForCell(cell)
detailScene.example = self.timelineData[indexPath!.row].title1?.text
}
}
For the array I have used:
var timelineData : NSMutableArray = NSMutableArray()
And the function loaddata, that is used with the function viewDidAppear
func loaddata () {
timelineData.removeAllObjects()
var findTimelineData:PFQuery = PFQuery(className: "ii")
findTimelineData.findObjectsInBackgroundWithBlock{
(objects, error)->Void in
if error == nil{
for object in objects!{
let ii:PFObject = object as! PFObject
self.timelineData.addObject(ii)
}
let array:NSArray = self.timelineData.reverseObjectEnumerator().allObjects
self.timelineData = NSMutableArray(array: array)
self.tableView.reloadData()
}
}
}
Try this. It should at least get you to the point where you're grabbing the information from the correct cell. I'm assuming your timelineData array is full of text, so that's how I accessed it, by casting it as a String. If it is full of a different type of object you need to cast it differently.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toDetail" {
if let detailScene = segue.destinationViewController as? detailViewController {
if let indexPath = tableView.indexPathForSelectedRow()?.row {
var object = self.timelineData[indexPath] as! PFObject
var title = object["title"] as! String
detailScene.example = title
}
}
}
}
It could be that the return value from indexPathForCell is returning nil or a bad value. There are quite a few articles about this function being unreliable.
A common suggestion is to use indexPathForRowAtPoint(cell.center) instead.
There is definitely something wrong with your last line of code:
detailScene.example = self.timelineData[indexPath!.row].title1?.text
I assume that self.timelineData array that holds some data.
If your array contain String class you don't need to append .title1?.text part.
If your array contain UILabel class you need change line to
detailScene.example = self.timelineData[indexPath!.row].text
because UILabel have no property called title1.
In general I think this happened because class in self.timelineData array have no property called title1.