I'm using XCode 6.3 to build a TableView of the different Fonts in iOS 8. First, per the book I'm reading, it said that nothing needed to be done regarding the height of the table rows, given that iOS8 takes care of that for you, so once I had everything per the book, the rows should update their heights based on their content, which wasn't the case. Then I tried to play with tableView.rowHeight and I set it equal to UITableViewAutomaticDimension in the TableViewController's viewDidLoad function, and that didn't work either. I also tried changing the height of the rows from Interface Builder, and that didn't seem to have any effect on the heights either. My code is as follows:
class RootViewController: UITableViewController {
private var familyNames: [String]!
private var cellPointSize: CGFloat!
private var favoritesList: FavoritesList!
private let familyCell = "FamilyName"
private let favoritesCell = "Favorites"
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
familyNames = sorted(UIFont.familyNames() as! [String])
let preferredTableViewFont = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cellPointSize = preferredTableViewFont.pointSize
favoritesList = FavoritesList.sharedFavoritesList
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
func fontForDisplay(atIndexPath indexPath: NSIndexPath) -> UIFont? {
if indexPath.section == 0 {
let familyName = familyNames[indexPath.row]
let fontName = UIFont.fontNamesForFamilyName(familyName).first as! String
return UIFont(name: fontName, size: cellPointSize)
} else {
return nil
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return favoritesList.favorites.isEmpty ? 1 : 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return section == 0 ? familyNames.count : 1
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return section == 0 ? "All Font Families" : "Favorite Fonts"
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier(familyCell, forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.font = fontForDisplay(atIndexPath: indexPath)
cell.textLabel!.text = familyNames[indexPath.row]
cell.detailTextLabel!.text = familyNames[indexPath.row]
return cell
} else {
return tableView.dequeueReusableCellWithIdentifier(favoritesCell, forIndexPath: indexPath) as! UITableViewCell
}
}
}
When I run this in the simulator, everything looks right until I scroll all the way to the bottom and I get this:
The attributes of the FontFamily cell are: style = subtitle, and accessory = disclosure indicator.
Any ideas on what I'm be doing wrong?
You must set self.tableView.estimatedRowHeight
Related
Problem Statement:
I want to display text separated by "," on new custom UITableViewCell.
Problem: It displays all data in single custom cell only with multi-line property, as shown in below.
I want to display tableView like this way.
Now I'm trying to display above data separated by "," on each new custom cell, as shown in above screenshot, but it displays only 1st data and skip remaining data, as per my code.
let Meaning :String = "Aback,Abacus,Abandon,Able,Aboard"
let Smeaning :String = "Fabric,Habit,keen,Pace"
func tableView(tableViewData: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableViewData.dequeueReusableCellWithIdentifier("cell")! as! StudentCell
let fmeaning = Mmeaning.characters.split{$0 == ","}.map(String.init)
let smeaning = Hmeaning.characters.split{$0 == ","}.map(String.init)
for var i = 0; i < fmeaning.count; i += 1{
print(fmeaning[i])
//Here it prints all values perfectly
}
for var i = 0; i < smeaning.count; i += 1{
print(smeaning[i])
//Here it prints all values perfectly
}
Problem occurs here below two statements: display only 1st value in UITableView
cell.lblMeaning1.text = fmeaning[indexPath.row]
cell.lblMeaning2.text = smeaning[indexPath.row]
return cell;
}
How should I assign an Array to these two custom cells, so that it will display data on separate new custom cell?
For that you need to use section table with your tableView, also you need to make this calculation in viewDidLoad and then reload the tableView, For that declare your two instance of array like this in your viewController.
var languageDic = [String: [String]]()
var allLanguage = [String]()
override func viewDidLoad() {
super.viewDidLoad()
let mMeaningArray = Mmeaning.characters.split{$0 == ","}.map(String.init)
let hMeaningArray = Hmeaning.characters.split{$0 == ","}.map(String.init)
self.languageDic = ["Marathi Meaning": mMeaningArray,"Hindi Meaning": hMeaningArray] // You can add other langaue in the dic sam way I have added this two
self.allLanguage = Array(self.languageDic.keys) as [String]
self.tableView.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.allLanguage.count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String {
return self.allLanguage[indexPath.section]
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.languageDic[self.allLanguage[section]].count
}
func tableView(tableViewData: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableViewData.dequeueReusableCellWithIdentifier("cell")! as! StudentCell
let str = self.languageDic[self.allLanguage[indexPath.section]][indexPath.row]
cell.lblMeaning1.text = str
return cell
}
Note: In cellForRowAtIndexPath you need only one label.
Edit: As of you want return the count of array that have more element you can try like this. F
var mMeaningArray = [String]()
var hMeaningArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.mMeaningArray = Mmeaning.characters.split{$0 == ","}.map(String.init)
self.hMeaningArray = Hmeaning.characters.split{$0 == ","}.map(String.init)
self.tableView.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (self.mMeaningArray.count > self.hMeaningArray.count)? self.mMeaningArray.count : self.hMeaningArray.count
}
func tableView(tableViewData: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableViewData.dequeueReusableCellWithIdentifier("cell")! as! StudentCell
if indexPath.row < self.mMeaningArray.count {
cell.lblMeaning1.text = self.mMeaningArray[indexPath.row]
}
else {
cell.lblMeaning1.text = ""
}
if indexPath.row < self.hMeaningArray.count {
cell.lblMeaning2.text = self.hMeaningArray[indexPath.row]
}
else {
cell.lblMeaning2.text = ""
}
return cell
}
the numberOfRowsInSection() function should return fmeaning.count and smeaning.count... put an if to determine which table you want to have the correct row number.
also you cand display the fmeaning as a section in your tableview, and the smeaning as cells.
I have a grouped table view which I want to have customized section headers. But I'm having trouble showing the first section header for the table view.
I have two sections and the section header for the first section is not showing but the second one is:
I have seen similar problems and those problems where solved by implementing the heightForHeaderInSection but I have implemented that.
I'm setting the sections like this:
let kSectionHeaderContact: String = "CONTACT INFORMATION"
let kSectionHeaderFeedback: String = "FEEDBACK"
enum Sections: Int {
case ContactSection = 0
case FeedbackSection = 1
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == Sections.ContactSection.rawValue {
return contactObjectsDictionary.count
} else if section == Sections.FeedbackSection.rawValue {
return feedbackObjectsDictionary.count
} else {
return 0
}
}
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == Sections.ContactSection.rawValue {
sectionHeaderView.sectionHeaderTitle.text = kSectionHeaderContact
} else if section == Sections.FeedbackSection.rawValue {
sectionHeaderView.sectionHeaderTitle.text = kSectionHeaderFeedback
}
return sectionHeaderView
}
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return kContactTableViewSectionHeaderViewHeight
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifierContactTableViewCell, forIndexPath: indexPath) as! ContactTableViewCell
// Configure the cell...
var titleText: String = ""
var detailText: String = ""
if indexPath.section == Sections.ContactSection.rawValue {
let contactObject = contactObjectsDictionary[indexPath.row]
titleText = contactObject[kDictionaryTitleKey]!
detailText = contactObject[kDictionaryDetailKey]!
} else if indexPath.section == Sections.FeedbackSection.rawValue {
let feedbackObject = feedbackObjectsDictionary[indexPath.row]
titleText = feedbackObject[kDictionaryTitleKey]!
detailText = feedbackObject[kDictionaryDetailKey]!
}
cell.configureCell(titleText, detail: detailText)
return cell
}
I'm implementing my custom section header in the storyboard and then referencing it by an outlet:
Edited:
I want to be able to design my custom section header in the storyboard and not programmatically.
Problem is inside viewForHeaderInSection method.
You need to create new instance of UIView & need to return it.
OR
You can create Class for UITableViewHeaderHeaderView and Reuse it.
Feel free to ask if you have any doubts regarding this.
let titleFirstLabel: UILabel = {
let label = UILabel()
label.text = "Text"
label.frame = CGRect(x: 15, y: 10, width: 100, height: 20)
return label
}()
and viewDidLoad() add this code
tableView.addSubview(titleFirstLabel)
That work for me. Good luck :)
I'm getting a fatal error: Array index out of range error when i run my application, but I don't see why. Here is my code:
var rippleLocations: [MKRippleLocation] = [.TapLocation, .TapLocation, .Center, .Left, .Right, .TapLocation, .TapLocation, .TapLocation]
var circleColors = [UIColor.clearColor(), UIColor.clearColor(),UIColor.clearColor(),UIColor.clearColor()]
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return aSport.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! MKTableViewCell
cell.textLabel?.text = aSport[indexPath.row].name
cell.textLabel?.text = aSport[indexPath.row].name
cell.backgroundColor = UIColor.clearColor()
cell.textLabel?.font = UIFont(name: "HelveticaNeue-Thin", size: 16)
cell.textLabel?.textColor = UIColor.whiteColor()
cell.rippleLocation = rippleLocations[indexPath.row]
let index = indexPath.row % circleColors.count
cell.rippleLayerColor = circleColors[index]
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let cell = sender as! MKTableViewCell
let row = tableView.indexPathForCell(cell)?.row
let detail = segue.destinationViewController as! SecondTableViewController
detail.selectedSchool = aSport[row!]
}
The error is highlighted on the cell.rippleLocation = rippleLocations[indexPath.row] string.. why is this error here?
The number of rows is equal to aSport.count:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return aSport.count
}
Shouldn't that be
return rippleLocations.count
? Or are you sure that aSport and rippleLocations always have the same number of elements? If you want to cycle through the ripple locations, replace
cell.rippleLocation = rippleLocations[indexPath.row]
with
cell.rippleLocation = rippleLocations[indexPath.row % rippleLocations.count]
It seems like the size of the rippleLocations arrays is less than aSport ...
So for example at the 10th row it will crash if aSport has 10 objects and rippleLocations has 8 objects only.
becuase in numberofRows method you're returning aSport's count
I know this question has been asked multiple times already, but for some reason none of the responses I've read has solved this issue. I'm retrieving an array of MealObjects, a class that I have defined, and setting their properties to labels in a Table View with a defined limit of 5 Table View Cells. I only have one section in the Table View.
I've pasted my View Controller class below. I've marked the error in a comment. What am I doing wrong?
import UIKit
class PlateViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var subtitleLabel: UILabel!
let locationHelper = LocationHelper.sharedInstance
let userChoice = UserChoiceCollectionDataSource()
var mealArray: [MealObject] = []
override func viewDidLoad() {
super.viewDidLoad()
locationHelper.setupLocation()
locationHelper.callback = {
self.mealArray = self.userChoice.getUserSuggestions()
}
tableView.dataSource = self
tableView.delegate = self
tableView.estimatedRowHeight = 125
tableView.rowHeight = UITableViewAutomaticDimension
titleLabel.text = "Your Plate"
subtitleLabel.text = "The top 5 suggestions based on the information you provided"
navigationController?.hidesBarsOnSwipe = true
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MealCell", forIndexPath: indexPath) as! PlateTableViewCell
let meals = mealArray[indexPath.row]
/* FATAL ERROR: ARRAY INDEX OUT OF RANGE
INDEXPATH.ROW = 0
*/
print(indexPath.row)
cell.mealTitleLabel.text = meals.mealTitle
cell.descriptionLabel.text = meals.mealDescription
cell.priceLabel.text = "\(meals.priceValue)"
return cell
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
}
If index 0 is out of range, it looks like your array is empty at the time the cellForRowAtIndexPath accesses it. You hard coded the number of rows to 5, but you should only do that if the array has at least 5 elements in it. For example, in the numberOfRowsInSection you can do
return min(mealArray.count, 5)
I don't see where you are setting the array. Your code
locationHelper.callback = {
self.mealArray = self.userChoice.getUserSuggestions()
}
does not do that.
to have one section tableView you must do this:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return mealArray.count
}
and after updating mealArray refresh tableView:
locationHelper.callback = {
self.mealArray = self.userChoice.getUserSuggestions()
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
};
}
i have a UITableView with multiple selection enabled with checkmarks. When i make selection that are all visible in the view, i don't run into any errors. However, if i scroll down further and place a selected item out of view, i get errors and even though the row stays selected, the checkmark goes away.
import Foundation
import Parse
import UIKit
class customerMenuVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var menuTV: UITableView!
var menuItems: [String] = ["Hello"]
var menuPrices: [Double] = [0.0]
var orderSelection: [String] = []
var priceSelection: [Double] = []
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return menuItems.count
}
func tableView(tableView: UITableView, numberOfColumnsInSection section: Int) -> Int
{
return 1;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell:UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "mycell")
cell.textLabel!.text = "\(menuItems[indexPath.row])\t $\(menuPrices[indexPath.row])"
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
//tableView.deselectRowAtIndexPath(indexPath, animated: true)
let cell = tableView.cellForRowAtIndexPath(indexPath)
cell!.accessoryType = .Checkmark
orderSelection.append(cell!.textLabel!.text!)
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath)
{
let cell = tableView.cellForRowAtIndexPath(indexPath)
cell!.accessoryType = .None
}
override func viewDidLoad() {
super.viewDidLoad()
menuTV.allowsMultipleSelection = true
let resMenu = resUser.sharedInstance
var resName = resMenu.nameStr
var resID = resMenu.idStr
var menuQ = PFQuery(className: "menu")
menuQ.getObjectInBackgroundWithId(resID){
(menus: PFObject?, error: NSError?) -> Void in
if error == nil && menus != nil {
let items: [String] = menus?.objectForKey("menuItems") as! Array
let prices: [Double] = menus?.objectForKey("menuPrices") as! Array
self.menuItems = items
self.menuPrices = prices
self.menuTV.reloadData()
}
}
}
#IBAction func continueButton(sender: AnyObject) {
let selections = menuTV.indexPathsForSelectedRows() as! [NSIndexPath]
var indexCount = selections.count
println(indexCount)
var x = 0
while x < indexCount
{
println(x)
let currentCell = menuTV.cellForRowAtIndexPath(selections[x]) as? UITableViewCell?;
println(x)
println(selections[x].row.description)
orderSelection.append(currentCell!!.textLabel!.text!)
println(orderSelection[x])
x++
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
This is how table views work.
When a cells scrolls off-screen, it gets tossed into the recycle queue and then used again to display data for a different indexPath in your data.
Any time the user makes any changes to the data for a cell you should save it to your data model (usually an array of information, or maybe an array of arrays if you're using a sectioned table view.) Then you should tell the table view to redisplay the changed cell. The cellForRowAtIndexPath method picks up the changed data and shows the changes to the cell. If the cell scrolls off-screen and then scrolls back on-screen, it gets displayed with the correct settings.
This applies to keeping track of which cells are selected as well.