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
}
Related
I try to Search for locations using MKLocalSearchRequest in Swift4 (for ios 12). When I append thelocationSearchTable.mapView = mapViewat the end of viewDidLoad I get the error.
In ViewController
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
var resultSearchController: UISearchController? = nil
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
let locationSearchTable = storyboard!.instantiateViewController(withIdentifier: "LocationSearchTable")
resultSearchController = UISearchController(searchResultsController: locationSearchTable)
resultSearchController?.searchResultsUpdater = locationSearchTable as! UISearchResultsUpdating
let searchBar = resultSearchController!.searchBar
searchBar.sizeToFit()
searchBar.placeholder = "Search for places"
navigationItem.titleView = resultSearchController?.searchBar
resultSearchController?.hidesNavigationBarDuringPresentation = false
resultSearchController?.dimsBackgroundDuringPresentation = true
definesPresentationContext = true
locationSearchTable.mapView = mapView
}...
In the LocationSearchTable
import UIKit
import MapKit
class LocationSearchTable: UITableViewController {
var matchingItems: [MKMapItem] = []
var mapView: MKMapView? = nil
}
extension LocationSearchTable: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
guard let mapView = mapView,
let searchBarText = searchController.searchBar.text else { return }
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = searchBarText
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.start { response, _ in
guard let response = response else {
return
}
self.matchingItems = response.mapItems
self.tableView.reloadData()
}
}
}...
Maybe locationSearchTable is not LocationSearchTable class.
Try the following code
if let locationSearchTable = locationSearchTable as? LocationSearchTable {
locationSearchTable.mapView = mapView
}
I am trying to re-use a code from swift 7.3. I feel I am very close.
When I try the code on the simulator, the picker view it is empty, but when I try to use it I have only one data show up.
I also try to adapt and other code to load data from core data to it, didn't work.
Any Idea?
Thank you
import CoreData
class ViewController: UIViewController, UIPickerViewDelegate, UIImagePickerControllerDelegate {
#IBOutlet weak var TextField: UITextField!
#IBOutlet weak var stylePicker: UIPickerView!
var styleData = [PilotBase]()
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PilotBase")
do {
styleData = try context.fetch(fetchRequest) as! [PilotBase]
} catch let err as NSError {
print(err.debugDescription)
}
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "PilotBase")
request.returnsObjectsAsFaults = false
do{
let result = try context.fetch(request)
for data in result as! [NSManagedObject]{
print(data.value(forKey: "name") as! String)
}
}catch{
print("Failed")
}
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return styleData.count
} else {
return 0
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
return styleData[row].name
} else {
return nil
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
print(styleData[row])
stylePicker.selectRow(row, inComponent: 0, animated: true)
TextField.text = styleData[row].name
}
}
Below the code to add data to Core Data.
#IBAction func Save(_ sender: Any) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "PilotBase", in: context)
let newUser = NSManagedObject(entity: entity!, insertInto: context)
newUser.setValue(TextField.text!, forKey: "name")
do{
try context.save()
}catch{
print("failed saving")
}
}
I want to play audio on click on cell. And change button image.They work fine. But when i am scroll my 4 cell button image automatically change.Please help. Any Help would be appreciated.
#IBAction func playSong (_ sender : UIButton , event: UIEvent){
let buttonPosition:CGPoint = sender.convert(.zero, to: table)
let indexPath = self.table.indexPathForRow(at: buttonPosition)
let cell = table.cellForRow(at: indexPath!) as? CustumCell
let a = URL(string : "http://www.abstractpath.com/files/audiosamples/sample.mp3")
if((audioPlayers) != nil){
audioPlayers = nil
}
audioPlayers = AVPlayer(url: a!)
if sender.isSelected == false {
sender.isSelected = true
audioPlayers?.play()
cell?.play.setImage(UIImage(named : "homestop"), for: .normal)
}else{
sender.isSelected = false
audioPlayers?.pause()
cell?.play.setImage(UIImage(named : "homeplay"), for: .normal)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "CustumCell"
var cell: CustumCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? CustumCell
if cell == nil {
var nib : Array = Bundle.main.loadNibNamed("CustumCell",owner: self,options: nil)!
cell = nib[4] as? CustumCell
}
cell.reportView.isHidden = true
cell.play.tag = indexPath.row
cell.play.addTarget(self, action:#selector(playSong(_:event:)), for: .touchUpInside)
cell.homereport.tag = indexPath.row
cell.homereport.addTarget(self, action:#selector(showReportView(_:)), for: .touchUpInside)
return cell
}
Basically whenever you scroll down/top/left/right and your marked cell going to out of bounds then whenever you back with scroll the cellForRowAt going to be called once more.
I sugest you to create dictionary with [UITableViewCell : Bool] and inside :
if sender.isSelected == false {
sender.isSelected = true
audioPlayers?.play()
dic[cell] = true
cell?.play.setImage(UIImage(named : "homestop"), for: .normal)
}else{
sender.isSelected = false
audioPlayers?.pause()
dic[cell] = false
cell?.play.setImage(UIImage(named : "homeplay"), for: .normal)
}
Later on inside :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "CustumCell"
var cell: CustumCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? CustumCell
if cell == nil {
var nib : Array = Bundle.main.loadNibNamed("CustumCell",owner: self,options: nil)!
cell = nib[4] as? CustumCell
}
cell.reportView.isHidden = true
cell.play.tag = indexPath.row
cell.play.addTarget(self, action:#selector(playSong(_:event:)), for: .touchUpInside)
cell.homereport.tag = indexPath.row
cell.homereport.addTarget(self, action:#selector(showReportView(_:)), for: .touchUpInside)
if dic[cell] {
// Set the image of the button or what ever you like to :)
}
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()
}
}