Passing Data from one view controller to another (Image) - swift

Error:
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
Destination Controller
var getImage = UIImage()
var name = String()
var gender = String()
var house = String()
var ancestry = String()
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = (charData.image)as! UIImage
nameLabel.text! = name
houseLabel.text! = house
// Do any additional setup after loading the view.
}
Source Controller
var charactersData = [Character]()
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func loadData()
{
DispatchQueue.main.async {
Alamofire.request("http://hp-api.herokuapp.com/api/characters").responseJSON(completionHandler: {
(response) in
switch response.result
{
case.success(let value):
let json = JSON(value)
print(json)
json.array?.forEach({
(character) in
let character = Character(name: character["name"].stringValue, house:character["house"].stringValue,image:character["image"].stringValue, gender: character["gender"].stringValue, ancestry: character["ancestry"].stringValue)
self.charactersData.append(character)
})
self.tableView.reloadData()
case.failure(let error):
print(error.localizedDescription)
}
})
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return charactersData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CharTableViewCell
cell.nameLabel.text = "Name: " + charactersData[indexPath.row].name
cell.houseLabel.text = "House: " + charactersData[indexPath.row].house
if let imageURL = URL(string: self.charactersData[indexPath.row].image) {
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
cell.charImageView.image = image
}
}
}
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let hpc = storyboard?.instantiateViewController(withIdentifier: "CharDetails") as? CharDetailsViewController
hpc?.getImage = (charactersData[indexPath.row].image) as! UIImage
hpc?.name = charactersData[indexPath.row].name
hpc?.house = charactersData[indexPath.row].house
self.navigationController?.pushViewController(hpc!, animated: true)
}
Im trying to pass an image to another controller but it seems im getting that error, could someone kindly help me. All the other data like name and house is passing properly other than the image. Kindly please let me know where to make changes

This might work:
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = getimage // change this in view did load method
}

You are doing wrong(this is not UIImage, this is URL string so you can use this url string to download the image):
hpc?.getImage = (charactersData[indexPath.row].image) as! UIImage
Please replace UIImage to String because this is not UIImage, This is String URL to download the image
So For this You have to change getImage variable UIImage to String and pass this String to this variable
var getImage = String()
hpc?.getImage = (charactersData[indexPath.row].image) as! String
After that again download this image from URL in another controller but this way is not good so you to follow below way:
Or
You have another option like when you download the image than save it Character Struct and pass it when didselect
Process:
Add a new variable like image in Character Struct/Model
Assign downloaded image when API call in the Struct/Model
And pass this image to another controller when didSelect

Related

Is there any way to list user defaults datas with swift?

I am trying to do save datas like name, address with user default at main view controller. After saving data, I wanted to list all user default data at second view controller with tableview (PeopleViewController). I managed to save user default data with main view controller. But only last saved user default data can be showed at tableview while I would like to list all data at tableview. What am I doing wrong? Can you give an idea? Thank you in advance..
My second view controller / PeopleViewController
import UIKit
class PeopleListViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
var names = [String]()
var address = [String]()
let storage = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
let peopleData = storage.value(forKey: "people") as? Data
if peopleData != nil {
let decoder = JSONDecoder()
do {
let person = try decoder.decode(People.self, from: peopleData!)
names.append(person.name)
address.append(person.address)
}catch {
print(error)
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LocationCell",for: indexPath)
cell.textLabel?.text = names[indexPath.row]
cell.detailTextLabel?.text = address[indexPath.row]
return cell
}
}
My main view controller
import UIKit
class ViewController: UIViewController {
#IBOutlet var TextFields: [UITextField]!
let storage = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
let peopleData = storage.value(forKey: "people") as? Data
if peopleData != nil {
let decoder = JSONDecoder()
do {
TextFields[0].text = ""
TextFields[1].text = ""
}catch {
print(error)
}
}
}
#IBAction func createButtonTapped(_ sender: UIButton) {
let name = TextFields[0].text!
let address = TextFields[1].text!
let people = People(
name: name,
address: address
)
let encoder = JSONEncoder()
do {
let peopleData = try encoder.encode(people)
storage.setValue(peopleData, forKey: "people")
TextFields[0].text! = ""
TextFields[1].text! = ""
}catch {
print(error)
}
}
}
My People Model
import Foundation
struct People: Codable{
var name: String
var address: String
}
Right now, you are only storing one person in UserDefaults. Every time you create a new person, you are overwriting the previous value. To store many people, you need an [People]. When you decode and encode, you can encode the whole [People].
// main VC
let encoder = JSONEncoder()
let decoder = JSONDecoder()
do {
// first decode the array from the storage
let array = storage.data(forKey: "people").map { try decoder.decode([People].self, from: $0) } ?? []
array.append(people) // add the newly created person
let newArrayData = try encoder.encode(array)
storage.set(newArrayData, forKey: "people")
TextFields[0].text! = ""
TextFields[1].text! = ""
}catch {
print(error)
}
// table VC
// you don't need two parallel arrays
var people: [People] = []
let storage = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
do {
people = try storage.data(forKey: "people").map { try decoder.decode([People].self, from: $0) } ?? []
} catch let error {
print(error)
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LocationCell",for: indexPath)
cell.textLabel?.text = people[indexPath.row].name
cell.detailTextLabel?.text = people[indexPath.row].address
return cell
}

How do I put my images from Core Data into a UICollectionView?

I'm relatively new to Xcode and I want to put images I have in CoreData to be shown in an image slider(uicollectionview). The image slider works just fine with some images I reference within an array, but I want to use the images from core data. How do I do that?
var arrAllpost = [post]()
struct post{
var Images:[UIImage]
}
func fetchData(){
let context = (UIApplication.shared.delegate as!
AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName:
"PhotoData")
do {
let results = try context.fetch(fetchRequest)
for dict in results
{
let imagestr = dict["ImagesName"] as! String
let array = imagestr.components(separatedBy: ", ") // store image
in directory and save image name with comma separated in one string
in core data and then that string split by comma and store in
name array then load that imagename's image from directory
let arrPostImages = NSMutableArray()
for i in array
{
let postimage = loadImageFromDirectory(str: i) //get image from
directory
arrPostImages.add(postimage)
}
arrAllPost.append(post(Images: arrPostImages))
}
}catch let err as NSError {
print(err.debugDescription)
}
}
// tableview delegate method
func tableView(_ tableView: UITableView, numberOfRowsInSection section:
Int) -> Int {
return arrAllPost.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tablePost.dequeueReusableCell(withIdentifier: "Cell",
for: indexPath)
let item = arrAllPost[indexPath.row] as! Post
for img in item.Images
{
cell.postimageView.image = img
}
return cell;
}

Core Data prints saved data but not showing up on table viewcontroller

I would like to show my saved data in core data on a table view controller, the data is saved properly and prints out the details on the console but the data is noot showing up on the tableview cell, kindly please help
var charData = [SavedCharacters]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var name = String()
var house = String()
var ancestry = String()
var getImage = UIImage()
override func viewDidLoad() {
super.viewDidLoad()
getData()
}
func getData(){
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "SavedCharacters")
request.returnsObjectsAsFaults = false
do{
let result = try? context.fetch(request)
for data in result as! [NSManagedObject]
{
name = data.value(forKey: "name") as! String
print(data.value(forKey: "name")as! String)
}
}
tableView.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return charData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "dataCell", for: indexPath) as! SavedDataTableViewCell
cell.nameLabel.text = charData[indexPath.row].name!
cell.house = charData[indexPath.row].house!
cell.ancestry = charData[indexPath.row].ancestry!
// displaying image in table view cell
if let imageURL = URL(string: self.charData[indexPath.row].image!) {
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
cell.charImageView.image = image
}
}
}
}
return cell
}
Definitely you've forgotten to init charData in getData() method
var charData: [CharData]
func getData(){
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "SavedCharacters")
request.returnsObjectsAsFaults = false
====> charData = []
do{
let result = try? context.fetch(request)
for data in result as! [CharData]
{
name = data.value(forKey: "name") as! String
print(data.value(forKey: "name")as! String)
===> charData.append(data)
}
}
tableView.reloadData()
}

Populating UITableView from JSON

I'm trying to use a JSON file to populate a UITableView in my app. Previously I was hard coding an array of sample data, but need to move to using my JSON file. This is a mishmash of various tutorials and answers found on SO, so I apologize if the syntax conventions are a little off.
import UIKit
import os.log
class BonusTableViewController: UITableViewController {
//MARK: Properties
var bonuses = [Bonus]() // Used for old sample data
var jBonuses = [Bonuses]() // Used with JSON based data
override func viewDidLoad() {
super.viewDidLoad()
//MARK: Confirm JSON file was loaded and log the Bonus Codes
let loadedBonuses = loadJson(filename: "BonusData")
for bonus in loadedBonuses! {
print(bonus.bonusCode)
}
}
// Load the JSON file from the bundled file.
func loadJson(filename fileName: String) -> [Bonuses]? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(JSONData.self, from: data)
print("loadJson loaded JSON")
return jsonData.bonuses
} catch {
print("error:\(error)")
}
}
return nil
}
// MARK: Data Structures
// Bonus Data Structs
struct JSONData: Decodable {
let name: String
let version: String
let bonuses: [Bonuses]
}
struct Bonuses: Decodable {
let bonusCode: String
let category: String
let name: String
let value: Int
let city: String
let state: String
let flavor: String
let imageData: String
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return jBonuses.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "BonusTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? BonusTableViewCell else {
fatalError("The dequeued cell is not an instance of BonusTableViewCell.")
}
// Now using JSON file
let jBonus = jBonuses[indexPath.row]
print("Setting labels using JSON file")
cell.bonusCodeLabel.text = jBonus.bonusCode
cell.categoryLabel.text = jBonus.category
cell.nameLabel.text = jBonus.name
cell.valueLabel.text = "\(jBonus.value)"
cell.cityLabel.text = "\(jBonus.city),"
cell.stateLabel.text = jBonus.state
cell.flavorText.text = jBonus.flavor
cell.primaryImage.image = jBonus.photo
return cell
}
From the console, I can confirm that it is able to see the JSON data and it does spit out the list of bonus codes. I can't pinpoint why this isn't working, but the result is a blank just a tableview with a bunch of empty rows.
Replace viewDidLoad with
override func viewDidLoad() {
super.viewDidLoad()
jBonuses = loadJson(filename: "BonusData")!
tableView.reloadData()
}
You have to assign the loaded data to the data source array and reload the table view.
Or if loadedBonuses could really be nil (it cannot in this case):
override func viewDidLoad() {
super.viewDidLoad()
if let loadedBonuses = loadJson(filename: "BonusData") {
jBonuses = loadedBonuses
tableView.reloadData()
}
}
Notes:
Delete the method numberOfSections, 1 is the default.
Force unwrap the cell, the code must not crash if everything is hooked up properly
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! BonusTableViewCell
You are populating Tableview using jBonuses array of Type Bonuses, but where you are filling the jBonuses array.
It seems that you are not filling jBonuses Array. Fill the jBonuses array once you get API response and call tableview reloadData method.
yourTableView.reloadData()

Can not display image in table view cell using firebase

I can not display image using firebase in table view cell, I don't know why because my code seems to work, but not there, may anyone help me?
Note: The label Works. I created a custom cell using a cocoa touch class, then I have linked that using the notation to cell as you can see.
import UIKit
import Firebase
class PlacesTableViewController: UIViewController,
UITableViewDataSource, UITableViewDelegate{
let ref = Database.database().reference()
var PlacesRef:DatabaseReference! = nil
var SelectedRef:DatabaseReference! = nil
let storage = Storage.storage()
var postdata:[String] = [String]()
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
print("Here Variables")
print("Here translated Names")
PlacesRef = ref.child("Places")
let storageRef = self.storage.reference()
PlacesRef.observe(.value) { (snapshot) in
let postDict = snapshot.value as? [String : AnyObject] ?? [:]
if snapshot.exists() {
for a in ((snapshot.value as AnyObject).allKeys)!{
var now:String = ""
self.postdata.append(a as! String)
DispatchQueue.main.async{
self.tableview.reloadData()
}
}
} else {
print("we don't have that, add it to the DB now")
}
print(self.postdata) //add key to array
}
tableview.delegate = self
tableview.dataSource = self
}
func tableView(_ _tableView: UITableView, numberOfRowsInSection selection: Int) ->Int{
print(postdata)
return postdata.count
}
func tableView( _ tableview: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableview.dequeueReusableCell(withIdentifier: "cell") as! CustomTableViewCell
var image=UIImage()
let StorageRef = storage.reference()
let NationRef = StorageRef.child("Nations/\(postdata[indexPath.row]).jpg")
print("\(postdata[indexPath.row]).jpg")
NationRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
print(error)
} else {
// Data for "images/island.jpg" is returned
image = UIImage(data: data!)!
print("image downloaded!")
}
}
cell.DetailLabel.text = postdata[indexPath.row]
cell.DetailImage.image = image
return cell
}
}
Let's see what happens in
func tableView( _ tableview: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableview.dequeueReusableCell(withIdentifier: "cell") as! CustomTableViewCell
var image=UIImage()
let StorageRef = storage.reference()
let NationRef = StorageRef.child("Nations/\(postdata[indexPath.row]).jpg")
// HERE we starting async web request for image data (tag 1)
NationRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
print(error)
} else {
// HERE we getting image data from async request and storing it in image var (tag 3)
image = UIImage(data: data!)!
print("image downloaded!")
}
}
cell.DetailLabel.text = postdata[indexPath.row]
// HERE we setting UIImage to cell's imageView (tag 2)
cell.DetailImage.image = image
return cell
}
if printing tags we'll get tag 1, tag 2 and tag 3. It means that firstly you create empty uiimage, then set this image to cell's imageView and at last you change image to image from request (image, not cell's imageView)
I think the answer can be - replace image = UIImage(data: data!)! with cell.DetailImage.image = UIImage(data: data!)
and there are many ways to improve this method