This would be a super noob question but I have no idea how to convert URL to data and data to UIimage. The text labels are work really beautifully but the UI images are not working
I figured out there are some codes to convert them but don't know how to use them. could you help me out.
override func viewDidLoad() {
super.viewDidLoad()
let manager = AFHTTPSessionManager()
let url = mainURL + "sample.php"
manager.get(url, parameters: nil, progress: nil, success: { (task, res) in
guard let json = res as? [String: Any] else {
print ("not [String: Any]]")
return
}
if let array = json["data"] as? [Any] {
for i in 0 ..< array.count {
if let row = array [ i ] as? [String: Any] {
let model = Model(t: row ["title"] as! String,
n: row ["user"] as! String,
d: row ["regdate"] as! String,
imgUrl: row ["img"] as! String)
self.models.append(model)
}
self.tableView.reloadData()
}
}
}) { (task, error) in
print("error = \(error)")
}
self.tableView.estimatedRowHeight = 500
}
Model
class Model : NSObject {
//?
let data = try? Data(contetsOf: url!)
let img = UIImage(data: data!)
let imgView = UIImageView(image: img)
var title : String
var name : String
var date : String
var image : UIImage!
init(t: String, n: String, d: String, imgUrl: String){
title = t
name = n
date = d
image = UIImage(named: imgUrl)
}
and the outlets.
mainCell.titleLabel.text = m.title //labels work fine
mainCell.nameLabel.text = m.name
mainCell.mainImgView.image = m.image
Try this
Just store image url in model class not image. Update your model class with below code
class Model : NSObject {
var title : String
var name : String
var date : String
var image : String
init(t: String, n: String, d: String, imgUrl: String){
title = t
name = n
date = d
image = imgUrl
}
}
// past code inside cellforRowAt
// download image from image URL
let url = URL(string: image)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
mainCell.mainImgView.image = UIImage(data: data!)
}
}).resume()
Related
I have 2 files. First - TransactionsViewController. Second - GetTransactions. When I open TransactionsViewController the table from that view loads faster than the date from GetTransactions. Therefore, it is displayed blank. How to fix it?
Here is the code of TransactionsViewController viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
GetTransactions().getTransactions()
clientsCount = GetTransactions.transactions.count
setupNavBar()
createTable()
refreshSetup()
}
Here is the code of GetTransactions:
class GetTransactions {
static var transactionsArr = [[String : Any]]()
static var transactions = [TransactionInfo]()
let URL_GET_TRANSACTIONS = "https://mashkov.dev/sites/default/BankApp/Transactions/Transactions.php"
func getTransactions(){
GetTransactions.transactionsArr.removeAll()
AF.request(URL_GET_TRANSACTIONS).responseJSON{ (response) in
if (response.value as? [[String : Any]]) != nil {
GetTransactions.transactionsArr = response.value as! [[String : Any]]
}
self.convertData()
print(GetTransactions.transactions)
}
}
func convertData() {
GetTransactions.transactions.removeAll()
for transaction in GetTransactions.transactionsArr {
let cl = TransactionInfo(id: transaction["id"] as! String,
payee: transaction["payee_account_id"] as! String,
sender: transaction["sender_account_id"] as! String,
transDate: transaction["trans_date"] as! String,
amount: transaction["amount"] as! String,
isSuccessfully: Bool((transaction["isSuccessfully"] as! String)) ?? true)
GetTransactions.transactions.append(cl)
}
}
}
What you need is a closure / block to be passed as an argument
class GetTransactions {
static var transactionsArr = [[String : Any]]()
static var transactions = [TransactionInfo]()
let URL_GET_TRANSACTIONS = "https://mashkov.dev/sites/default/BankApp/Transactions/Transactions.php"
func getTransactions(completion block: () -> ()){
GetTransactions.transactionsArr.removeAll()
AF.request(URL_GET_TRANSACTIONS).responseJSON{ (response) in
if (response.value as? [[String : Any]]) != nil {
GetTransactions.transactionsArr = response.value as! [[String : Any]]
}
self.convertData()
print(GetTransactions.transactions)
block()
}
}
func convertData() {
GetTransactions.transactions.removeAll()
for transaction in GetTransactions.transactionsArr {
let cl = TransactionInfo(id: transaction["id"] as! String,
payee: transaction["payee_account_id"] as! String,
sender: transaction["sender_account_id"] as! String,
transDate: transaction["trans_date"] as! String,
amount: transaction["amount"] as! String,
isSuccessfully: Bool((transaction["isSuccessfully"] as! String)) ?? true)
GetTransactions.transactions.append(cl)
}
}
}
And you call it using,
GetTransactions().getTransactions {
clientsCount = GetTransactions.transactions.count
//reload your tableView here
}
My server returns an array of photo informations in JSON like that :
"pics":[{"ID":182,"ID_member":39,"fn":"b69ea6f6c88b58c67a331aa3c5eaff81.jpg"}, ...]
I have a struct init function made to handle one photo json raw array (from type [String:Any]) :
init?(fromRaw _img:[String:Any]?)
{
guard
let img = _img,
let id = img["ID"] as? Int,
let idm = img["ID_member"] as? Int,
let fn = img["fn"] as? String
else
{
OOTLog.info("Warning : unable to init from photo raw array")
return nil ;
}
self.id = id
self.idMembre = idm
self.fileName = fn
}
My question is : lets say we have a json from server (of type [[String:Any]], array of n raw photos), is there any way to "overload" as? [Photo] with my init?(fromRaw:) within Photo struct, so we could just code :
guard let arrayPhoto = jsonRaw as? [Photo] else ..
Instead of :
guard let arrayPhotoRaw = jsonRaw as [[String:Any]] else ..
let photoArray:[Photo] = []
for p in jsonRaw {
guard let p = Photo(fromRaw:p) else { continue }
photoArray.append(p)
}
It's better to use
struct Root: Codable {
let pics: [Pic]
}
struct Pic: Codable {
let id, idMember: Int
let fn: String
enum CodingKeys: String, CodingKey {
case id = "ID"
case idMember = "ID_member"
case fn
}
}
let res = try! JSONDecoder().decode(Root.self, from:data)
print(res.pics)
I built my app to have news feed like Facebook. My problem is that I don't know how to fetch child images in Post and show it in a collectionView. Please show me how to do it. Appreciate any help.
Here is the db structure:
Posts
d7j3bWMluvZ6VH4tctQ7B63dU4u1:
20181112101928:
avatar: "https://platform-lookaside.fbsbx.com/platform/p..."
content: "Funny image"
images:
-LR4vaEIggkGekc-5ZME:
"https://firebasestorage.googleapis.com/v0/b/hon..."
-LR4vaENC-IsePibQYxY:
"https://firebasestorage.googleapis.com/v0/b/hon..."
name: "Thành Trung"
time: 1541992768776.3628
type: "Funny"
Here is my code:
func getDataFromPostFirebase() {
let getPostData = databaseReference.child("Posts")
getPostData.observe(.childAdded) { (snapshot) in
getPostData.child(snapshot.key).observe(.childAdded, with: { (snapshot1) in
getPostData.child(snapshot.key).child(snapshot1.key).observe(.value, with: { (snapshot2) in
self.arrayImageUrl = [String]()
if let dict = snapshot2.value as? [String : Any] {
guard let avatar = dict["avatar"] as? String else {return}
guard let content = dict["content"] as? String else {return}
guard let name = dict["name"] as? String else {return}
guard let time = dict["time"] as? Double else {return}
guard let type = dict["type"] as? String else {return}
if let images = dict["images"] as? [String : String] {
for image in images.values {
self.arrayImageUrl.append(image)
}
let newPost = Post(avatarString: avatar, contentString: content, nameString: name, timeDouble: time, typeString: type)
self.arrayPost.append(newPost)
DispatchQueue.main.async {
self.feedTableView.reloadData()
}
} else {
let newPost = Post(avatarString: avatar, contentString: content, nameString: name, timeDouble: time, typeString: type)
self.arrayPost.append(newPost)
DispatchQueue.main.async {
self.feedTableView.reloadData()
}
}
}
})
})
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayImageUrl.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! TrangChu_CollectionViewCell
cell.imgContent.layer.cornerRadius = CGFloat(8)
cell.imgContent.clipsToBounds = true
cell.imgContent.layer.borderWidth = 2
cell.imgContent.layer.borderColor = #colorLiteral(red: 0.4756349325, green: 0.4756467342, blue: 0.4756404161, alpha: 1)
let url = URL(string: arrayImageUrl[indexPath.row])
cell.imgContent.sd_setImage(with: url, completed: nil)
return cell
}
Model object
import Foundation
class Post {
var avatar : String
var content : String
var images : [String]?
var name : String
var time : Double
var type : String
init(avatarString : String, contentString : String, nameString : String, timeDouble : Double, typeString : String) {
avatar = avatarString
content = contentString
// images = imagesString
name = nameString
time = timeDouble
type = typeString
}
}
As what I've said your db is not well structured. I suggest you re structure it like this.
Posts
d7j3bWMluvZ6VH4tctQ7B63dU4u1:
avatar: "https://platform-lookaside.fbsbx.com/platform/p..."
content: "Funny image"
images:
-LR4vaEIggkGekc-5ZME: "https://firebasestorage.googleapis.com/v0/b/hon..."
-LR4vaENC-IsePibQYxY: "https://firebasestorage.googleapis.com/v0/b/hon..."
name: "Thành Trung"
time: 1541992768776.3628
type: "Funny"
timestamp: 1540276959924
I removed the timestamp node and transferred it along the children node. Now you can fetch the posts with this.
ref.child("Posts").observe(.childAdded) { (snapshot) in
var post = Post()
let val = snapshot.value as! [String: Any]
post.name = val["name"] as? String
self.ref.child("Posts").child(snapshot.key).child("images").observeSingleEvent(of: .value, with: { (snap) in
post.imagesString = [String]()
for image in snap.children.allObjects as! [DataSnapshot] {
post.imagesString?.append(image.value as! String)
print("images \(image.value)")
}
list.append(post)
print("post \(post)")
})
If you want to order the posts you can achieve it using queryOrderedByChild("timestamp")
Add this to access your images:
guard let images = dict["images"] as? [[String: Any]] { return }
let imagesString: [String] = []
for imageDict in images {
for key in imageDict.keys {
if let imageName = imageDict[key] as? String else {
// here you access your image as you want
imagesString.append(imageName)
}
}
}
Then when creating the post object you use imagesString that we created:
let newPost = Post(avatarString: avatar, contentString: content, imagesString: imagesString, nameString: name, timeDouble: time, typeString: type)
You can fetch the images values using this
ref.child("Posts").observe(.childAdded) { (snapshot) in
self.ref.child("Posts").child(snapshot.key).observe(.childAdded, with: { (snapshot1) in
self.ref.child("Posts").child(snapshot.key).child(snapshot1.key).child("images").observe(.childAdded, with: { (snap) in
let post = new Post()
for rest in snap.children.allObjects as! [DataSnapshot] {
//append images
post.imagesString.append(rest.value)
}
post.avatarString = snapshot1.value["avatar"] as? String
...
})
})
I suggest you change the structure of your db because its nested. Refer here
how to use random string to let or var to url link
i want to make random string for url
let url = URL(string:"https://www.pallive.net/random.json")
or see the code when i change values in the site linke in the app do not changed,but if i chnage name of url it change
the code not reload if i change the value in json and keep same file
if i want to reload i have to change the name of file how to do some thange
auotmatic change the url and keep the orginal in the ftp server
import Foundation
class Episode
{
var title: String?
var description: String?
var thumbnailURL: URL?
var url: URL?
var episodes = [Episode]()
init(title: String, description: String, thumbnailURL: URL, createdAt: String, author: String)
{
self.title = title
self.description = description
self.thumbnailURL = thumbnailURL
}
init(espDictionary: [String : AnyObject])
{
self.title = espDictionary["title"] as? String
// description = espDictionary["description"] as? String
thumbnailURL = URL(string: espDictionary["thumbnailURL"] as! String)
self.url = URL(string: espDictionary["link"] as! String)
}
static func downloadAllEpisodes(completion: #escaping ([Episode]) -> ()) {
var episodes = [Episode]()
let url = URL(string:"https://www.pallive.net/random.json")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error)
completion(episodes)
}
else {
if let jsonData = data ,let jsonDictionary = NetworkService.parseJSONFromData(jsonData) {
let espDictionaries = jsonDictionary["episodes"] as! [[String : AnyObject]]
for espDictionary in espDictionaries {
let newEpisode = Episode(espDictionary: espDictionary)
episodes.append(newEpisode)
}
}
completion(episodes)
DispatchQueue.main.async(execute: {
completion(episodes)
})
}
}.resume()
}
func randomString(_ length: Int) -> String {
let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = UInt32(letters.length)
var randomString = ""
for _ in 0 ..< length {
let rand = arc4random_uniform(len)
var nextChar = letters.character(at: Int(rand))
randomString += NSString(characters: &nextChar, length: 1) as String
}
return randomString
}
}
I have a large class called "Goal" in parse. This class has multiple elements, one of which is a PFFile, that is always a UIImage.
When I perform my query for the "Goal" class, I cannot figure out how to take the PFFile, and change it to a UIImage for use.
var query = PFQuery(className:"Goal")
let currentUser = PFUser.currentUser()!.username
query.whereKey("creator", equalTo: currentUser!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects?.count) goals for the TableView.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
let goalType = object["type"] as! String
let goalPeriod = object["period"] as! String
let goalCategory = object["category"] as! String
let goalShortDescription = object["shortDescription"] as! String
let goalLongDescription = object["longDescription"] as! String
let goalPointvalue = object["pointValue"] as! Int
let goalSharedSetting = object["shared"] as! Bool
let goalAdoptionCount = object["adoptionCount"] as! Int
let goalIsComplete = object["isComplete"] as! Bool
let goalSuccessImageData = object["image"] as! PFFile
goalSuccessImageData.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.imageQuery = image
}
}
}
let goalSuccessImage : UIImage = self.imageQuery
let goalObjectID = object.objectId
let goalSpreadCount = object["spreadCount"] as! Int
let goalSpreadTotal = object["spreadTotal"] as! Int
let goalTotalCompletions = object["totalCompletions"] as! Int
let thisGoal = GoalModel(period: goalPeriod, type: goalType, category: goalCategory, shortDescription: goalShortDescription, longDescription: goalLongDescription, pointValue: goalPointvalue, shared: goalSharedSetting, adoptionCount: goalAdoptionCount, isComplete: goalIsComplete, successImage: goalSuccessImage, goalID: goalObjectID!, spreadCount: goalSpreadCount, spreadTotal: goalSpreadTotal, totalCompletions: goalTotalCompletions ) as GoalModel
any tips on how to modify the "success image" part? I added a space before and after to make it easier to find.
Thank you in advance!
I'm using this way in my projects, if it help's you :
func performSave(sender: UIBarButtonItem){
affichageActivityIndicator()
let qos = Int(QOS_CLASS_USER_INITIATED.value)
dispatch_async(dispatch_get_global_queue(qos,0)) { () -> Void in
dispatch_async(dispatch_get_main_queue()){
if let updateObject = self.currentObject as PFObject? {
let imageData = UIImageJPEGRepresentation(imageToSave, 0.1)
let imageFile = PFFile(name:"image.png", data:imageData)
updateObject["imageFile"] = imageFile
// Save the data back to the server in a background task
updateObject.saveInBackgroundWithBlock{(success: Bool, error: NSError!) -> Void in
UIApplication.sharedApplication().endIgnoringInteractionEvents()
if success == false {
println("Error")
}
}
}
}
}
}