How to pass a pfimageview from one controller to another in storyboard with swift and parse.com - swift

I was hoping I could have some advice. I'm making a quiz app, which contains images. The quiz has 4 answer boxes with a single image view. I can download the images into the quiz via parse.com, however I now want to be able to click on the image which then goes to a separate view controller so you can zoom into the image in full screen. I have been able to call the pffile and segue it, however my app crashes when I try and getdatainbackgroundwithblock in the second view controller.
How do you think I should segue the image?
EDIT
Yes sorry. So the crash I was getting was an error at code=1 address=0x48, which occurred at line +55 of getdatainbackgroundwithblock.
I have pasted snipped of the code I was using below.
I used this to call the image in the first view and make it into an image in my PFImageView.
`for object : PFObject in objectsArray as! [PFObject] {
let image = object["Image"] as! PFFile
let transimage = image
image.getDataInBackgroundWithBlock {
(imageData:NSData?, error:NSError?) -> Void in
if error == nil {
let finalimage = UIImage(data: imageData!)
// self.imageView.append(finalimage!)
self.imageView.image = final image`
I then passed it to my second view controller like this.
`override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "ZoomImage"){
// Get the new view controller using [segue destinationViewController].
var detailScene = segue.destinationViewController as! ZoomImageViewController
detailScene.currentObject = transimage as! PFFile
NSLog("checkone")
}
}`
In my second view controller I tried to make it back into an image like this.
` if let object = currentObject {
NSLog("(currentObject)")
var initialThumbnail = UIImage(named: "question")
NSLog("check1")
imageView.image = initialThumbnail
NSLog("check2")
NSLog("check3")
object.getDataInBackgroundWithBlock {
(imageData:NSData?, error:NSError?) -> Void in
if error == nil {
NSLog("check4")
let finalimage = UIImage(data: imageData!)
// self.imageView.append(finalimage!)
self.imageView.image = finalimage
NSLog("check5")`
I would get an NSLog of 3, and then the code would break. The NSLog for the currentObject also brought back a PFFile, so I believed it to be true. Where am I going wrong? And is there a better way to pass it?

You were right, I didn't need getdata. I got rid of that and instead of seguing my pffile I segued the pfobject carrying the image and the text. I then called the object as a file and put it in the uiimageview. I'll post the code I used in a bit incase anyone wants it.

Why you call getDataInBackgroundWithBlock in both view controllers ?
You already fetch the data in your PFFile instance so no need to call it twice. It's could be the issue.

Related

Autolayout images inside cells in tableview. Correct layout but only once scrolling down and back up?

Im trying to get tableview cells with auto resizing images to work. Basically I want the image width in the cell to always be the same, and the height to change in accordance with the aspect ratio of the image.
I have created a cell class, which only has outlets for a label, imageView and a NSLayoutConstraint for the height of the image. I have some async methods to download an image and set it as the image for the cell imageView. Then the completion handle gets called and I run the following code to adjust the height constraint to the correct height:
cell.cellPhoto.loadImageFromURL(url: photos[indexPath.row].thumbnailURL, completion: {
// Set imageView height to the width
let imageSize = cell.cellPhoto.image?.size
let maxHeight = ((self.tableView.frame.width-30.0)*imageSize!.height) / imageSize!.width
cell.cellPhotoHeight.constant = maxHeight
cell.layoutIfNeeded()
})
return cell
And here is the UIImageView extension I wrote which loads images:
func loadImageFromURL(url: String, completion: #escaping () -> Void) {
let url = URL(string: url)
makeDataRequest(url: url!, completion: { data in
DispatchQueue.main.async {
self.image = UIImage(data: data!)
completion()
}
})
}
And the makeDataRequest function which it calls:
func makeDataRequest(url: URL, completion: #escaping (Data?) -> Void) {
let session = URLSession.shared
let task = session.dataTask(with: url, completionHandler: { data, response, error in
if error == nil {
let response = response as? HTTPURLResponse
switch response?.statusCode {
case 200:
completion(data)
case 404:
print("Invalid URL for request")
default:
print("Something else went wrong in the data request")
}
} else {
print(error?.localizedDescription ?? "Error")
}
})
task.resume()
}
This works for all the cells out of frame, but the imageviews in the cells in the frame are small. Only when I scroll down and then back up again do they correctly size. How do I fix this? I know other people have had this issue but trying their fixes did nothing.
I had to sorta recreate the problem to understand what was going on. Basically you need to reload the tableview. I would do this when a picture finishes downloading.
In the view controller that has the table view var. Add this to the viewDidLoad() function.
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
//Create a notification so we can update the list from anywhere in the app. Good if you are calling this from an other class.
NotificationCenter.default.addObserver(self, selector: #selector(loadList), name: NSNotification.Name(rawValue: "loadList"), object: nil)
}
//This function updates the cells in the table view
#objc func loadList(){
//load data here
self.tableView.reloadData()
}
Now, when the photo is done downloading, you can notify the viewcontroller to reload the table view by using the following,
func loadImageFromURL(url: String, completion: #escaping () -> Void) {
let url = URL(string: url)
makeDataRequest(url: url!, completion: { data in
DispatchQueue.main.async {
self.image = UIImage(data: data!)
completion()
//This isn't the best way to do this as, if you have 25+ pictures,
//the list will pretty much freeze up every time the list has to be reloaded.
//What you could do is have a flag to check if the first 'n' number of cells
//have been loaded, and if so then don't reload the tableview.
//Basically what I'm saying is, if the cells are off the screen who cares.
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "loadList"), object: nil)
}
})
}
Heres something I did to have better Async, see below.
My code as follows, I didn't do the resizing ratio thing like you did but the same idea applies. It's how you go about reloading the table view. Also, I personally don't like writing my own download code, with status code and everything. It isn't fun, why reinvent the wheel when someone else has done it?
Podfile
pod 'SDWebImage', '~> 5.0'
mCell.swift
class mCell: UITableViewCell {
//This keeps track to see if the cell has been already resized. This is only needed once.
var flag = false
#IBOutlet weak var cellLabel: UILabel!
#IBOutlet weak var cell_IV: UIImageView!
override func awakeFromNib() { super.awakeFromNib() }
}
viewController.swift (Click to see full code)
I'm just going to give the highlights of the code here.
//Set the image based on a url
//Remember this is all done with Async...In the backgorund, on a custom thread.
mCell.cell_IV.sd_setImage(with: URL(string: ViewController.cell_pic_url[row])) { (image, error, cache, urls) in
// If failed to load image
if (error != nil) {
//Set to defult
mCell.cell_IV.image = UIImage(named: "redx.png")
}
//Else we got the image from the web.
else {
//Set the cell image to the one we downloaded
mCell.cell_IV.image = image
//This is a flag to reload the tableview once the image is done downloading. I set a var in the cell class, this is to make sure the this is ONLY CALLED once. Otherwise the app will get stuck in an infinite loop.
if (mCell.flag != true){
DispatchQueue.main.asyncAfter(deadline: .now() + 0.025){ //Nothing wrong with a little lag.
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "loadList"), object: nil)
mCell.flag = true
}
}
}
}

Swift UITableViewCell - Cell Image changes if I scroll down too fast, but only on the first attempt

I am parsing a JSON within my viewDidLoad method. One of the keys within this JSON is the image URL, which goes into a a string array called "allCImages"
This is just a string. Therefore to populate the image into the cell, in my cellForRowAt method, I have the following:
cell.vcCellImage.downloadImage(from: allCImages[indexPath.section])
Note: vcCellImage is the IBOutlet of my cell image view.
The "downloadImage" method is part of the following extension:
extension UIImageView {
func downloadImage(from imgURL: String!) {
let theUrl = URLRequest(url: URL(string: imgURL)!)
// set initial image to nil so it doesn't use the image from a reused cell
image = nil
// check if the image is already in the cache
if let imageToCache = vc1ImageCache.object(forKey: imgURL! as NSString) {
self.image = imageToCache
print("Image is in Cache")
return
}
// download the image asynchronously
let task = URLSession.shared.dataTask(with: theUrl) { (data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
// create UIImage
let imageToCache = UIImage(data: data!)
// add image to cache
vc1ImageCache.setObject(imageToCache!, forKey: imgURL! as NSString)
self.image = imageToCache
}
}
task.resume()
}
This is working almost perfectly. For example:
1) If I scroll down my tableview slowly, all the cells contain the correct image
2) If I scroll up my tableview, slowly or quickly, all the cells contain the correct image. This is proven by the fact that my console is printing the following:
Image is in Cache
Image is in Cache
Image is in Cache
I.e, the tableview is getting my image from the cache (since to scroll up, I must have scrolled down before)
3) The issue is if I scroll down my tableview really quickly, on the first attempt. Since the image has not cached yet, the cell will display the wrong image, before changing to the correct image. Classic problem
Therefore I am missing this small piece of logic. How to resolve this?
EDIT: I tried this but the issue remains:
class VCTableViewCell: UITableViewCell {
override func prepareForReuse() {
super.prepareForReuse()
vcCellImage.image = nil
}
This occurs because of
1- cell dequeueing : cells are re-used inside the tableView
2- when you scroll before a request happens it may cause a new 1 with same url
Best option is using SDWebImage
I have faced the similar issue.
I have fixed this issue by cancelling the image request in the prepareForReuse method.
Can you try the same?
first off all if you are appending your api or any data like this just remove this
var arr = [string]()
viewDidLoad()
{
arr.append("s","sd","sd)
}
accept this
var arr = [string]()
viewWillAppear()
{
arr.removeAll()
//call api
//append Data
arr.append("s","sd","sd)
}
I have similar problem then I solve it like this, may be it helpful for you also.

Updating thumbnail of UIDocument in iCloud

I have a UITableViewController which displays a list of NSMetaDataItems which are connected to a StoryDocument which subclasses UIDocument. Each StoryDocument has a UIImage property called image. Each row shows it's thumbnail.
I've overridden fileAttributesToWrite(to: URL, for: UIDocumentSaveOperation) to save the thumbnail as follows:
override func fileAttributesToWrite(to url: URL, for saveOperation: UIDocumentSaveOperation) throws -> [AnyHashable : Any] {
let thumbnailSize = CGSize(width: 1024, height: 1024)
if let thumbnail = image?.at(targetSize: thumbnailSize) {
return [
URLResourceKey.hasHiddenExtensionKey : true,
URLResourceKey.thumbnailDictionaryKey : [
URLThumbnailDictionaryItem.NSThumbnail1024x1024SizeKey : thumbnail
]
]
} else {
return [:]
}
}
I set the thumbnail in func tableView(_:, cellForRowAt:) where url is the url metadataItems.value(forAttribute: NSMetadataItemURLKey)
do {
var thumbnailDictionary: AnyObject?
let nsurl = url as NSURL
try nsurl.getPromisedItemResourceValue(&thumbnailDictionary, forKey: URLResourceKey.thumbnailDictionaryKey)
cell.thumbnailImageView.image = thumbnailDictionary?[URLThumbnailDictionaryItem.NSThumbnail1024x1024SizeKey] as? UIImage
} catch {
cell.thumbnailImageView.image = nil
}
If I change the StoryDocument's image in a detail view and then return to the UITableViewController, the original thumbnail (unchanged) is still shown, even though I'm telling the UITableView to reloadData(). The image only updates about 10 seconds later, when the next .NSMetadataQueryDidFinishGathering or .NSMetadataQueryDidUpdate notification comes in. Is there a good way to get the recently changed thumbnail for the cell rather than the old one?
I'm using Swift 4, although I expect I could adapt suggestions in Swift 3 too.
Many thanks in advance.

Getting Image from URL for UIView [Swift]

Alright so I've made some handlers and classes to grab data from a URL, its all returning fine, I've checked the URLs are valid and everything.
Anyways, I'm trying to do an NSData(contentsOfURL) on a stored URL in my class for a UIViewController. I'm successfully printing out String Variables like name, type, description, but I'm having difficulty displaying an UIImage into a Image View on the ViewController.
Here is my Code, it's run when the View loads:
func configureView() {
// Update the user interface for the detail item.
if let card = detailCard {
title = card.cardTitle
//Assign Elements
detailDescriptionLabel?.text = card.description
typeLabel?.text = card.cardType
cardTitleLabel?.text = card.cardTitle
costLabel?.text = card.cardCost
print(card.cardImageURL)
if let url = NSURL(string: card.cardImageURL){
let data = NSData(contentsOfURL: url)
imageView.image = UIImage(data: data!)// <--- ERROR HERE: EXC_BAD_INSTRUCTION
}
//Print Log
//print(card.logDescription)
}
}
Like the comment says above, I get the error on the imageView.image = UIImage(data: data!) line:
Thread 1: EXC_BAD_INSTRUCTION
Here is the code for cardImageURL:
var cardImageURL: String {
return String(format: "http://testyard.example.net/display/images/cards/main/%#", image)
}
//This is a computed variable when a "Card" class object is created.
It returns the correct url in a string:
http://testyard.example.net/display/images/cards/main/armor-of-testing.jpg
I've used this code elsewhere, and it worked fine, why is it throwing errors here?
Thanks in advance.
Are you sure the data being downloaded is an image? You should probably use this line:
if let image = UIImage(data: data)`
This should stop the error, though you might need to test that your data actually contains an image.

Create UITableView like instagram to display name, image and text

how can i create a UITableView like Instagram as?
I am thinking something like, each cell contains:
Cell 1:
Uploader Name
Image
Image Text
Cell 2:
Uploader Name
Image
Image Text
And so it goes on... Any ideas on how to do something like this?
First thing you need to do is create a subclass of UITableViewCell. There is a good tutorial on that here: https://www.youtube.com/watch?v=FfZGKx4BYVU
After properly creating the UITableViewCell subclass, you need to set it up as a reusable cell on your UITableView. You can do that by selecting a prototype cell in the storyboard and assigning your custom class to it.
I believe the youtube link above explain the whole process.
The function below queries the image from a Parse database. If you do not know how to use Parse, check www.parse.com. They have a lot of tutorials.
func queryImageDataInBackgroundForFile(file: PFFile, AndHandleWith handler: (data: NSData) -> ()) {
file.getDataInBackgroundWithBlock { (data: NSData?, error: NSError?) -> Void in
if error == nil {
if data != nil {
handler(data: data!)
}
} else {
println("ERROR -> ParseDAO.queryImageDataInBackgroundForFile")
}
}
}
You would put inside the UITableViewDelegate method that loads the cells the following call to the function:
parseDAO.queryImageDataInBackgroundForFile(place.coverImageFile, AndHandleWith: { (data) -> () in
cell.imageView.image = UIImage(data: data)
})
If you are loading the image from a local assets file you can do the following
cell.imageView.image = UIImage(named: "name of your image file")