I am using import AlamofireImage/Alamofire to load up pictures I am downloading from Firebase Storage on my tableview cells. However, when I run the app, I cannot see the pictures unless I navigate to a different page and then come back to the tableview page. Can anyone help?
At application start up:
After I navigate to a different view controller and coming back to the page
Here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = topNewsTableView.dequeueReusableCell(withIdentifier: "topNewsCell", for: indexPath) as! TopNewsCell
cell.cellDelegate = self
cell.favoriteDelegate = self
cell.share.tag = indexPath.row
cell.collect.tag = indexPath.row
cell.selectionStyle = .none
let article = articles[indexPath.row]
cell.topNewsText.text = article.title
let imageRef = storageRef.child("images/" + article.imageURL)
cell.imageView?.isHidden = true
if article.imageURL != ""{
imageRef.downloadURL { url, error in
if let error = error {
} else {
cell.imageView?.isHidden = false
AF.request(url!).responseData { (response) in
if response.error == nil {
if let data = response.data {
let image = UIImage(data: data)
cell.imageView?.isHidden = false
cell.imageView?.image = self.resizeImage(image: image!, targetSize: CGSize(width: 350.0, height: 300.0))
}
}
}
}
}
}
cell.indexPath = indexPath
return cell
}
The code below the calls a coredata and prints the string in a label.
#IBOutlet var displayL: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
user = coreDataHandler.getSortedData()
for i in user! {
displayL.text = String("\(displayL.text ?? "")\nCourse: \(i.username!) Score: \(i.password!)")
}}
This works and I want to transfer it to a tableview cell but what I have now is not working. Nothing is even showing up in the tableview. My tableview code is below. I just want to take the uitableview cell and have it display a entry for every cell.
import UIKit
import CoreData
class allScoresViewController: UIViewController, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tazbleView : UITableView!
var itemsName : [NSManagedObject] = []
var textFieldz : UITextField!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemsName.count
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let appD = UIApplication.shared.delegate as! AppDelegate
let context = appD.persistentContainer.viewContext
let FetchRequest = NSFetchRequest<NSManagedObject>(entityName: "User")
do {
itemsName = try context.fetch(FetchRequest)
}catch {
print("Ashley Tisdale")
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
let appD = UIApplication.shared.delegate as! AppDelegate
let context = appD.persistentContainer.viewContext
context.delete(itemsName[indexPath.row])
itemsName.remove(at: indexPath.row)
do {
try context.save()
}
catch {
print("Jessica Alba")
}
self.tazbleView.reloadData()
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for : indexPath)
let user = itemsName[indexPath.row]
cell.textLabel?.text = ("\nCourse: \(user.username!) Score: \(user.password!)")
return cell
}
}
I assume that since you fetch your User entities in viewDidAppear, that is what you want to use and not coreDataHandle.getSortedData().
Change the declaration of itemsName to
var itemsName: [User] = []
and in tableView:cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for : indexPath)
let user = itemsName[indexPath.row]
cell.textLabel?.text = ("\nCourse: \(user.username!) Score: \(user.password!)")
return cell
}
Update
You probably also want to update your declaration of the fetch request property in viewDidAppear
let fetchRequest = NSFetchRequest<User>()
i successfully saved the array of images as nsdata in coredata.
MY Question is - how can i check if there is an image exists at particular index(2) or not ??
Here is my code for retrieving data
var tableImageArray = NSData
func ShowCoreData(){
// core data - show
let appDel:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
let context:NSManagedObjectContext = appDel.managedObjectContext
var request = NSFetchRequest(entityName: "SavedIdea")
request.returnsObjectsAsFaults = false;
do {
var result:NSArray = try context.executeFetchRequest(request)
if (result.count > 0) {
self.tableTitleArray = result.valueForKey("heading") as! [String]
print(tableTitleArray)
self.tableDetailArray = result.valueForKey("detail") as! [String]
self.tableTypeArray = result.valueForKey("type") as! [String]
self.tableHoursArray = result.valueForKey("time") as! [String]
self.tableImageArray = result.valueForKey("image") as! [NSData]
// print(" image array nsdata -- \(tableImageArray)")
}else{
// print("0 result appear....error")
}
tableView.reloadData()
}catch {
let fetchError = ("returned error is \(error as! NSError)")
print(fetchError)
}
}
I got the whole array as NSdata in tableImageArray Then Suppose there is no image or value at index 2 but index 1,3 of tableImageArray have values. This is giving me BAD_Exc error at this line -
if tableImageArray[indexPath.row].length > 0 whenever no image found
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! as! OTPTableViewCell
cell.LabelTitle.text = tableTitleArray[indexPath.row]
cell.LabelDetail.text = tableDetailArray[indexPath.row]
cell.LabelType.text = tableTypeArray[indexPath.row]
cell.LabelHours.text = String(tableHoursArray[indexPath.row])
// print(tableImageArray[indexPath.row])
if tableImageArray[indexPath.row].length > 0 {
cell.ImgView.image = UIImage(data: tableImageArray[indexPath.row])//(named: tableImageArray[indexPath.row])
}else{
cell.imageWidthConstraints.constant = 0
}
return cell
}
I have created core data using 'NSFetchedResultController' and 'managedObjectContext' in a table view. But in the later view controller, after gathering accelerometer data and conduct calculation, I will get some results that I also want to store in the same row index with the core data I created before.
How can I achieve this? If I create managedObjectContext again, it will create another 'row' of core data in this table.
The code in tableViewController:
'
let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
var fetchedResultController: NSFetchedResultsController = NSFetchedResultsController()
override func viewDidLoad() {
super.viewDidLoad()
fetchedResultController = getFetchedResultController()
fetchedResultController.delegate = self
fetchedResultController.performFetch(nil)
}
func getFetchedResultController() -> NSFetchedResultsController {
fetchedResultController = NSFetchedResultsController(fetchRequest: trialFetchRequest(), managedObjectContext: managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultController
}
func trialFetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Trials")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
return fetchRequest
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
let numberOfSections = fetchedResultController.sections?.count
return numberOfSections!
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let numberOfRowsInSection = fetchedResultController.sections?[section].numberOfObjects
return numberOfRowsInSection!
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let trial = fetchedResultController.objectAtIndexPath(indexPath) as Trials
cell.textLabel?.text = trial.trialName
cell.detailTextLabel?.text = trial.date?.description
return cell
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
let managedObject:NSManagedObject = fetchedResultController.objectAtIndexPath(indexPath) as NSManagedObject
managedObjectContext?.deleteObject(managedObject)
managedObjectContext?.save(nil)
}
func controllerDidChangeContent(controller: NSFetchedResultsController!) {
tableView.reloadData()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "showTrial" {
let indexPath = tableView.indexPathForSelectedRow()
let trial:Trials = fetchedResultController.objectAtIndexPath(indexPath!) as Trials
let trialController:TrialDetailViewController = segue.destinationViewController as TrialDetailViewController
trialController.trial = trial
}
}
'
The code in the createTrial Controller:
' let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "createTrial" {
if theTrialName.text != "" {
createTrial()
} else {
let alertView = UIAlertController(title: "", message: "Trial name couldn't be empty", preferredStyle: .Alert)
alertView.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
presentViewController(alertView, animated: true, completion: nil)
}
}
}
func createTrial() {
let entityDescripition = NSEntityDescription.entityForName("Trials", inManagedObjectContext: managedObjectContext!)
let trial = Trials(entity: entityDescripition!, insertIntoManagedObjectContext: managedObjectContext)
trial.trialName = theTrialName.text
trial.location = theLocation.text
trial.notes = theNotes.text
if theArm.on {
trial.arm = 1
} else {
trial.arm = 0
}
managedObjectContext?.save(nil)
}
'
ps. the view controller I want to get data from is not this following segue, it is around 3 view afterwards. And I have created a string to store the data I need in that view.
I solved the problem by creating the object in the tableViewControllerand use segue twice to transmit it to secondViewControllerand thirdViewController. So it will be at the same index for the whole time. And I can change the content of the core data at both the following view controllers.
Im trying to create a collection view with cells displaying string with variable length.
Im using this function to set cell layout:
func collectionView(collectionView : UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAtIndexPath indexPath:NSIndexPath) -> CGSize
{
var cellSize:CGSize = CGSizeMake(self.whyCollectionView.frame.width, 86)
return cellSize
}
what I would like to do is manipulate cellSize.height based on my cell.labelString.utf16Count length.
the basic logic would be to sa that
if((cell.labelString.text) > 70){
cellSize.height = x
}
else{
cellSize.height = y
}
However, I can't manage to retrieve my cell label string length which always return nil. (I think it's not loaded yet...
for better understanding, here is the full code:
// WhyCell section
var whyData:NSMutableArray! = NSMutableArray()
var textLength:Int!
#IBOutlet weak var whyCollectionView: UICollectionView!
//Loading data
#IBAction func loadData() {
whyData.removeAllObjects()
var findWhyData:PFQuery = PFQuery(className: "PlacesWhy")
findWhyData.whereKey("placeName", equalTo: placeName)
findWhyData.findObjectsInBackgroundWithBlock({
(objects:[AnyObject]!,error:NSError!)->Void in
if (error == nil) {
for object in objects {
self.whyData.addObject(object)
}
let array:NSArray = self.whyData.reverseObjectEnumerator().allObjects
self.whyData = array.mutableCopy() as NSMutableArray
self.whyCollectionView.reloadData()
println("loadData completed. datacount is \(self.whyData.count)")
}
})
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.loadData()
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return whyData.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:whyCollectionViewCell = whyCollectionView.dequeueReusableCellWithReuseIdentifier("whyCell", forIndexPath: indexPath) as whyCollectionViewCell
// Loading content from NSMutableArray to cell
let therew:PFObject = self.whyData.objectAtIndex(indexPath.row) as PFObject
cell.userWhy.text = therew.objectForKey("why") as String!
textLength = (therew.objectForKey("why") as String!).utf16Count
self.whyCollectionView.layoutSubviews()
// Displaying user information
var whatUser:PFQuery = PFUser.query()
whatUser.whereKey("objectId", equalTo: therew.objectForKey("reasonGivenBy").objectId)
whatUser.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]!, error: NSError!)->Void in
if !(error != nil) {
if let user:PFUser = (objects as NSArray).lastObject as? PFUser {
cell.userName.text = user.username
// TODO Display avatar
}
}
})
return cell
}
func collectionView(collectionView : UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAtIndexPath indexPath:NSIndexPath) -> CGSize
{
var cellSize:CGSize = CGSizeMake(self.whyCollectionView.frame.width, 86)
return cellSize
}
While the answer above may solve your problem, it establishes a pretty crude way of assigning each cells height. You are being forced to hard code each cell height based on some estimation. A better way of handling this issue is by setting the height of each cell in the collectionview's sizeForItemAtIndexPath delegate method.
I will walk you through the steps on how to do this below.
Step 1: Make your class extend UICollectionViewDelegateFlowLayout
Step 2: Create a function to estimate the size of your text: This method will return a height value that will fit your string!
private func estimateFrameForText(text: String) -> CGRect {
//we make the height arbitrarily large so we don't undershoot height in calculation
let height: CGFloat = <arbitrarilyLargeValue>
let size = CGSize(width: yourDesiredWidth, height: height)
let options = NSStringDrawingOptions.UsesFontLeading.union(.UsesLineFragmentOrigin)
let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(18, weight: UIFontWeightLight)]
return NSString(string: text).boundingRectWithSize(size, options: options, attributes: attributes, context: nil)
}
Step 3: Use or override delegate method below:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
var height: CGFloat = <someArbitraryValue>
//we are just measuring height so we add a padding constant to give the label some room to breathe!
var padding: CGFloat = <someArbitraryPaddingValue>
//estimate each cell's height
if let text = array?[indexPath.item].text {
height = estimateFrameForText(text).height + padding
}
return CGSize(width: yourDesiredWidth, height: height)
}
You can dynamically set the frame of the cell in the cellForItemAtIndexPath function, so you can customize the height based on a label if you disregard the sizeForItemAtIndexPath function. With customizing the size, you'll probably have to look into collection view layout flow, but hopefully this points you in the right direction. It may look something like this:
class CollectionViewController: UICollectionViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var array = ["a","as","asd","asdf","asdfg","asdfgh","asdfghjk","asdfghjklas","asdfghjkl","asdghjklkjhgfdsa"]
var heights = [10.0,20.0,30.0,40.0,50.0,60.0,70.0,80.0,90.0,100.0,110.0] as [CGFloat]
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return array.count
}
override func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CellID", forIndexPath: indexPath) as Cell
cell.textLabel.text = array[indexPath.row]
cell.textLabel.sizeToFit()
// Customize cell height
cell.frame = CGRectMake(cell.frame.origin.x, cell.frame.origin.y, cell.frame.size.width, heights[indexPath.row])
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(64, 64)
}
}
which gives dynamic heights like so
In Swift 3, use the below method:
private func updateCollectionViewLayout(with size: CGSize) {
var margin : CGFloat = 0;
if isIPad {
margin = 10
}
else{
margin = 6
/* if UIDevice.current.type == .iPhone6plus || UIDevice.current.type == .iPhone6Splus || UIDevice.current.type == .simulator{
margin = 10
}
*/
}
if let layout = menuCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.itemSize = CGSize(width:(self.view.frame.width/2)-margin, height:((self.view.frame.height-64)/4)-3)
layout.invalidateLayout()
}
}