I am completely new to swift and I am pretty find it difficult to find that its quite different than obj.C
I have a difficulty while populating the table view.
my coding to populate goes as follows -
class DetailTableViewController: UITableViewController
{
var items = []
override func viewDidLoad() {
super.viewDidLoad()
items=["dodnf","dgfd"]
Item()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cellreuse", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.item]
return cell
}
When I am writing the following code cell.textLabel?.text = items[indexPath.item] I am getting an error message as- AnyObject is not convertible to String
So what is my error and why is it so?
Just add the type when declaring the data source array:
var items : [String] = []
That's all. In cellForRowAtIndexPath the compiler can infer the type.
However the proper syntax is supposed to be
cell.textLabel?.text = items[indexPath.row]
The problem is that you have declared items as an array without specifying the type of the elements (var items = []). Therefore, when you try to get an element from the array, the compiler errors because it cannot guarantee the type of the element is what you are expecting.
You need to specify the type of the items in the array. You can do this in either one of the two possible stages:
when you declare the array (preferred to leverage Swift's type safety):
var items = [String]() // or its equivalent: var items : [String] = []
// Alternatively, if you know it at the time of declaration you can just do the following and let
// Swift's type inference do its work
var items = ["dodnf","dgfd"]
OR when you get an element from the array:
cell.textLabel?.text = items[indexPath.item] as? String
You can learn more about Collection Types in Swift in The Swift Programming Language.
Related
I have a data source in this form:
struct Country {
let name: String
}
The other properties won't come into play in this stage so let's keep it simple.
I have separated ViewController and TableViewDataSource in two separate files. Here is the Data source code:
class CountryDataSource: NSObject, UITableViewDataSource {
var countries = [Country]()
var filteredCountries = [Country]()
var dataChanged: (() -> Void)?
var tableView: UITableView!
let searchController = UISearchController(searchResultsController: nil)
var filterText: String? {
didSet {
filteredCountries = countries.matching(filterText)
self.dataChanged?()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredCountries.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let country: Country
country = filteredCountries[indexPath.row]
cell.textLabel?.text = country.name
return cell
}
}
As you can see there is already a filtering mechanism in place.
Here is the most relevant part of the view controller:
class ViewController: UITableViewController, URLSessionDataDelegate {
let dataSource = CountryDataSource()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.tableView = self.tableView
dataSource.dataChanged = { [weak self] in
self?.tableView.reloadData()
}
tableView.dataSource = dataSource
// Setup the Search Controller
dataSource.searchController.searchResultsUpdater = self
dataSource.searchController.obscuresBackgroundDuringPresentation = false
dataSource.searchController.searchBar.placeholder = "Search countries..."
navigationItem.searchController = dataSource.searchController
definesPresentationContext = true
performSelector(inBackground: #selector(loadCountries), with: nil)
}
The loadCountries is what fetches the JSON and load the table view inside the dataSource.countries and dataSource.filteredCountries array.
Now, how can I get the indexed collation like the Contacts app has without breaking all this?
I tried several tutorials, no one worked because they were needing a class data model or everything inside the view controller.
All solutions tried either crash (worst case) or don't load the correct data or don't recognise it...
Please I need some help here.
Thank you
I recommend you to work with CellViewModels instead of model data.
Steps:
1) Create an array per word with your cell view models sorted alphabetically. If you have data for A, C, F, L, Y and Z you are going to have 6 arrays with cell view models. I'm going to call them as "sectionArray".
2) Create another array and add the sectionArrays sorted alphabetically, the "cellModelsData". So, The cellModelsData is an array of sectionArrays.
3) On numberOfSections return the count of cellModelsData.
4) On numberOfRowsInSection get the sectionArray inside the cellModelsData according to the section number (cellModelsData[section]) and return the count of that sectionArray.
5) On cellForRowAtindexPath get the sectionArray (cellModelsData[indexPath.section]) and then get the "cellModel" (sectionArray[indexPath.row]). Dequeue the cell and set the cell model to the cell.
I think that this approach should resolve your problem.
I made a sample project in BitBucket that could help you: https://bitbucket.org/gastonmontes/reutilizablecellssampleproject
Example:
You have the following words:
Does.
Any.
Visa.
Count.
Refused.
Add.
Country.
1)
SectionArrayA: [Add, Any]
SectionArrayC: [Count, Country]
SectionArrayR: [Refused]
SectionArrayV: [Visa]
2)
cellModelsData = [ [SectionArrayA], [SectionArrayC], [SectionArrayR], [SectionArrayV] ]
3)
func numberOfSections(in tableView: UITableView) -> Int {
return self.cellModelsData.count
}
4)
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionModels = self.cellModelsData[section]
return sectionModels.count
}
5)
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let sectionModels = self.cellModelsData[indexPath.section]
let cellModel = sectionModels[indexPath.row]
let cell = self.sampleCellsTableView.dequeueReusableCell(withIdentifier: "YourCellIdentifier",
for: indexPath) as! YourCell
cell.cellSetModel(cellModel)
return cell
}
I have this weird error message in my TableViewController class
class MenuTableViewController: UITableViewController {
fileprivate var menuItems = [MenuItem]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UINib(nibName: "MenuItemTableViewCell", bundle: nil), forCellReuseIdentifier: CELL_MENU_ITEM)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menuItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CELL_MENU_ITEM, for: indexPath) as! MenuItemTableViewCell
// this line throws the error message
if let menuItem = self.menuItems[indexPath.row].getTitle() as [MenuItem] {
cell.itemTitleLabel.text = menuItem
}
return cell
}
func setMenuItems(menuItems: [MenuItem]) {
self.menuItems = menuItems
}
}
I totally don't know what that error means. There are others facing this problem with type inout but they are doing errors with '=' instead of '==' and things like that. By the way the value of menuItems get set in another class in a completion function. But if I remove it from there I still got this error.
Since menuItems is declared as a concrete non-optional type there is no type casting nor optional binding needed.
let menuItem = self.menuItems[indexPath.row]
cell.itemTitleLabel.text = menuItem.getTitle()
The error message might be misleading. You are trying to cast (presumed) String to [MenuItem]
Maybe you want to check MenuItem? like that:
if let menuItem = self.menuItems[indexPath.row] as MenuItem {
cell.itemTitleLabel.text = menuItem.getTitle()
}
And you declare your collection with [MenuItem] type, so subscription will return non-optional value, you can remove iflet check and use:
cell.itemTitleLabel.text = self.menuItems[indexPath.row].getTitle()
I have missed to import uikit in my custom struct and get a similar error. hope it helps somebody reading here.
I know that I can query for, lets say, users that have emailVerified equal to true and present them into a tableView, but I was having trouble getting a single Parse object of type array into a tableView. I couldn't find anything online about this specific problem, but after putting a few answers together, I got it to work my answer is below for those also having trouble with this.
Here is what I found based on my question. I have an object in Parse called "my_classes" that is of type array. I want to get the items from the array into a tableView.
1) Create a variable: var myClassesResults : NSMutableArray = []
2) Create the function or place the code where necessary:
func getUserData() {
if PFUser.currentUser()!.objectForKey("my_classes") != nil {
let classes = PFUser.currentUser()!.objectForKey("my_classes")!
myClassesResults = classes as! NSMutableArray
self.noClasses = false
self.tableView.reloadData()
} else {
self.noClasses = true
self.tableView.reloadData()
}
}
3) tableView functions:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.myClassesResults.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel!.text = myClassesResults[indexPath.row] as? String
return cell
}
I have an iOS project I'm working on in Xcode 7 using Swift 2. I have an array called details with a dictionary which includes a String and an Int value. The Int is called cellOrder in the Class and the idea is to sort the details array in a TableView with a sort based on the cellOrder Int value.
The array shows the String values which are names. I looked here to try and implement this into my project with no success.
Here is my array:
// Array of data for the TableView
var details = [ProjectDetails]()
Here is my TableView Code:
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return details.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = details[indexPath.row]
return cell
}
How do I do the sort and where would I put the code, ViewDidLoad() or maybe cellForRowAtIndexPath?
UPDATE:
My ViewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
// TableView Sorting
details.sortInPlace({$0.cellOrder < $1.cellOrder})
tableView.delegate = self
...
}
Update 2:
My ProjectDetails class for the array data is:
import UIKit
class ProjectDetails: NSObject, NSCoding {
// MARK: Properties
var fileName: String
var cellOrder: Int
...
}
To sort the array you would just do
details.sortInPlace({$0.cellOrder < $1.cellOrder})
in viewDidLoad, or you could use
details.sort({$0.cellOrder < $1.cellOrder})[indexPath.row]
in cellForRowAtIndexPath, but that could prove dangerous if you do not keep track of your array.
One will sort the array in place, one will return a sorted immutable array.
If you insist on sorting immutably (has its pros and cons beyond this scope) assign it to another array and use that in your logic.
You must sort the itens in this array details[], after that, you can call tableView.reloadData() to reload the info.
In the cellForRowAtIndexPath you must just take the data to fill the cell, so NO SORT THERE!
Sort anywhere else, before call the function to reload.
I am trying to build a list within a table view controller and I have the proper setup, but for some reason my simulator crashes at the line where I set my array. It is not an error, but a Thread issue. I'm still learning the XCode warning system so I'm not sure what that means, but I noticed that in the Thread notifications that cityArray = ([String]) 0 values. Can anyone help?
import UIKit
class ListTableViewController: UITableViewController {
var cityArray: [String] = ["Portland","San Francisco","Cupertino"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cityArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellIdentifier", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = self.cityArray[indexPath.row]
return cell
}
}
UPDATE:
Images of the Thread message:
try this
let cityArray: [NSArray] = ["Portland","San Francisco","Cupertino"] as NSArray
Can you also provide the error you're receiving? Is it an issue with the array being mutated while being enumerated or something?
Also,
var cityArray: [String] = ["Portland","San Francisco","Cupertino"]
can change to
var cityArray = ["Portland", "San Francisco", "Cupertino"]
Since all your objects are of the same type, the Swift's type inference takes care of this for you.