I searched around for pagination but everything seems to point to pulling data from a server:
Pagination
Pagination
In my situation my data arrays are filled with static items wherein as I already know the count of what's inside of each array. My tabBar has three tabs and each tab has a tableVIew with different amounts of data
tableDataForTabOne = [DataModel]() // the array has 1000 items in it
tableDataForTabTwo = [DataModel]() // the array has 690 items in it
tableDataForTabThree = [DataModel]() // the array has 7 items in it
How do I paginate the arrays for the tableView into different pages? For example the first 10 items is 1 page, the next 10 items is another page, etc etc?
The question has nothing to do with the tabs. I don't know how to paginate on a tableView without pulling data from a server.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak fileprivate var tableView: UITableView!
let tableDataForTabTwo = [DataModel]() //has 690 items in it
var pageNumber = 0
override func viewDidLoad() {
super.viewDidLoad()
...
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableDataForTabTwo.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
cell.titleLabel.text = tableDataForTabTwo[indexPath.row]."some static title"
cell.imageView.image = tableDataForTabTwo[indexPath.row].UIImage(named: "some static image")
return cell
}
}
Related
I'm trying to sort array/names from my first TableViewController using core data to pass the data to my second UITableViewController, I have successfully been able to implement the number of sections and the correct number of rows for each section that the user needs by dividing the number of players with the number of sections.
However, I've been having problems separating the array of players continuously in each section. Cannot add the image of what the simulator looks like so I'll try to explain.
User types 10 names inside the first tableView, then selects the number of teams using a stepper(2 teams), lastly, they click on the team-up UIButton which divides 10(# players) by 2(number of teams) so on the second tableView two Sections will appear with 5 players but both Sections repeat the first 5 names.
I would like for the first Section to display the first 5 names of the array and the second Section to display the last 5 names instead of repeating the first 5 names on every Section regardless of how many players per Section the user chooses. I've been stuck for 4 days and have tried loops and Extensions and I cannot find a way for the rest of the sections to tap in the middle of the array of names, Please Help!! and thank you.
Here is the code of my secondTableView where I think the problems are either inside my tableView cellForRowAt or my loadedShuffledPlayers() function,
Note: Players comes from core data
import UIKit
import CoreData
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
loadedShuffledPlayers()
tableView.reloadData()
}
var players2 = [Players]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var numberOfTeams = Int()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return players2.count / numberOfTeams
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "shuffledNames", for: indexPath)
cell.textLabel?.text = players2[indexPath.row].names
return cell
}
func loadedShuffledPlayers(){
let request: NSFetchRequest<Players> = Players.fetchRequest()
do{
players2 = try context.fetch(request).shuffled()
}catch{
print("Error fetching data .\(error)")
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return numberOfTeams
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Team # \(section + 1)"
}
}
The problem is here
cell.textLabel?.text = players2[indexPath.row].names
You are only looking at the row number and are ignoring the section number. So with your example of 10 and 2 the row will always be between 0 and 4.
So you need to do something like (not tested):
let rowsPerSection = players2.count / numberOfTeams
let rowInSection = indexPath.row + rowsPerSection * indexPath.section
cell.textLabel?.text = players2[rowInSection].names
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 a main menu in my app with 15 item and each item has a sub of 20 items which i also added them as images array (15 image array , some thing like a restaurant menu ) so when ever the user clicks on 1 of the main menu items the app will take him to the sub menu , i have created the main menu table view the issue is with the sub menus do i have to create 15 table view for each sub menu !!??
is there is any way to create 1 table view for the sub menus and change its data according to user click
note : i don't want to use the sections in my table view
any ideas will be much appreciated
Need two viewControllers and a navigationController. One for main menu and other for sub menu. Let them be mainMenuViewController and subMenuViewController. Each controllers contains a tableView.
Create an menuArray containing 15 submenu data.Each submenu is an array.
In didSelectRowAtIndexPath of mainMenuViewController, if user selects a row in the tableView, then pass that data in the menuArray corresponding to the selected row.
For example, if user selects third row,
then pass menuArray[3] to subMenuViewController. Here indexPath.row = 3.
Sample Project Code:
MenuViewController.swift:
import UIKit
class MenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var menuTableView: UITableView!
var imagesArray: NSArray = []
var menuArray: NSArray = []
var subMenuDataArray: NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
menuTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
imagesArray = ["soups.jpg","salads.jpg","starters.jpg","pizzas.jpg","burgers.jpeg"]
menuArray = ["Soups","Salads","Starters","Pizzas","Burgers"]
subMenuDataArray = [["Cream of broccoli","Cream of celery","Cream of tomato","Etrog","Gazpacho"],
["Tuna salad","Urnebes","Waldorf salad"],
["Kakori Kebabs","Stir Fried Chilli Chicken"," Microwave Paneer Tikkas","Aloo and Dal ki Tikki","Cheese Balls","Bhuna Masala Chicken Wings"],
["Cheese Pizzas","Chicken Pizzas","Masala Pizzas","Double Cheese Pizzas","Herbal Pizzas"],
["Luger Burger","Le Pigeon Burger","The Company Burger","Dyer’s Deep-Fried Burger","The Lola Burger","Cheeseburger","Raw Steak Tartare Burger","Buckhorn Burger"]]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return imagesArray.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat{
return 70
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let menuTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
let cellImageView: UIImageView = UIImageView.init()
cellImageView.frame = CGRectMake(10, 10, 50, 50)
cellImageView.image = UIImage(named: imagesArray.objectAtIndex(indexPath.row) as! String)
menuTableViewCell.contentView.addSubview(cellImageView)
let menuLabel: UILabel = UILabel.init(frame: CGRectMake(70, 10, 200, 25))
menuLabel.text = menuArray.objectAtIndex(indexPath.row) as? String
menuTableViewCell.contentView.addSubview(menuLabel)
return menuTableViewCell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let subMenuViewController: SubMenuViewController = storyboard!.instantiateViewControllerWithIdentifier("SubMenuViewControllerID") as! SubMenuViewController
subMenuViewController.currentSubMenuArray = subMenuDataArray.objectAtIndex(indexPath.row) as! NSArray
navigationController?.pushViewController(subMenuViewController, animated: true)
}
}
SubMenuViewController.swift:
import UIKit
class SubMenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var subMenuTableView: UITableView!
var currentSubMenuArray: NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
subMenuTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "subCell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return currentSubMenuArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let subMenuTableViewCell = tableView.dequeueReusableCellWithIdentifier("subCell", forIndexPath: indexPath)
let subMenuLabel: UILabel = UILabel.init(frame: CGRectMake(10, 10, 250, 25))
subMenuLabel.text = currentSubMenuArray.objectAtIndex(indexPath.row) as? String
subMenuTableViewCell.contentView.addSubview(subMenuLabel)
return subMenuTableViewCell
}
}
Storyboard:
Output:
To test the sample project, use the following link of my GitHub account:
https://github.com/k-sathireddy/MenuTableViewSample
Declare one array for displaying and change the content of the array according to the selection and screen state like for menu, submenu etc. and reload the table to display the data in the main array. And if you want to display different kind of cell for different selection you can achieve it by taking an enum for whats the current screen state like i said and return required cell initialized in cellForRowAtIndexPath. Its all about how much you can think and implement the logic. Comment below if you need real technical solution with codes you already have used.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I would like to loop my numbers into the uitableviewcell and printing all the values from the highest number to the lowest and printing them in each cell. I posted the full code. See the cell.text output. This is my code:
import UIKit
class tableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var arr = [Int]()
var cell:tableCell!
var Payment: float_t! = 59600
var years1: float_t! = //15 * 12 = 180
var monthlyPayment: float_t! = 471
var interest: float_t! = 5%
var principal: float_t! = 222
var interstate: float_t! = 249
var initil: float_t!
var data = Array<float_t>()
var data2: NSMutableArray = []
override func viewDidLoad() {
super.viewDidLoad()
let c = Int(years1)
arr += 0...c
tableCalculation()
let nib = UINib(nibName: "table", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell")
}
func tableCalculation() {
let years = Int(years1)
initil = TPayment - 0
for i in 0..<years {
initil = initil - principil
interest = initil * interestrate
principil = monthlyPayment - interest
print("Month : \(monthlyPayment), principil: \(principil),interest: \(interest), initi: \(initil)")
self.data2 = [initil]
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arr.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! tableCell
cell.lbl1.text = "\(arr[indexPath.row])"
cell.lbl2.text = currencyFormatter(monthlyPayment)
cell.lbl3.text = currencyFormatter(interest)
cell.lbl4.text = currencyFormatter(principil)
cell.lbl5.text = "\(self.data2[indexPath.row % data2.count])"
return cell
}
// 4
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("Row \(indexPath.row) selected")
}
// 5
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 70
}
func currencyFormatter(Price: float_t) ->String {
let currencyFormatter = NSNumberFormatter()
currencyFormatter.usesGroupingSeparator = true
currencyFormatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
// localize to your grouping and decimal separator
let numberOfPlaces: float_t = 1.0
let multiplier: float_t = pow(10.0, numberOfPlaces)
let num = Price
let rounded = round(num * multiplier) / multiplier
let priceString = currencyFormatter.stringFromNumber(rounded)
return priceString!
}
}
This code always gives me the last number of the loop for all values, I would like to change it to write from the first value to the last one in every cell.
Welcome to SO. Neither your question nor your code make any sense.
It looks to me like you have no idea how to use table views.
You need to set up a data model (usually an array) that holds the data for your table view. Then when your cellForRowAtIndexPath method gets called, you look at the row (or row and section) in the request, fetch the data for that row (or row & section for a sectioned table view) and use that data to configure a cell which you return.
Try it this way:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! tableCell
cell.lbl5.text = "\(initial - indexPath.row*225)" //subtracts each consecutive row by a higher multiple of 225.
return cell
}
You need to understand that cellForRow:atIndexPath is a UITableView datasource method that's called by the UIKit framework. It gets called when the tableview is loaded, based on the number of sections (specified by another datasource method), and the number of rows in each section (also specified by yet another datasource method).
Here's how table views work, like Duncan said you need to know.
/*We need to subclass UITableViewDataSource and UITableViewDelegate
so we gain access to tableView methods. Don't forget to link your
tableView in the storyboard to the delegate and datasource!*/
class controller: UIViewController, UITableViewDataSource, UITableViewDelegate {
//1: Model. All the data you're gonna use for your table view
var data = Array<String>()
override func viewDidLoad() {
//1: Populate model with some data when the view loads.
self.data = ["Oranges", "Apples", "Bananas", "Grapes"]
}
//2: Number of rows in table view. This determines how many times #3 is called (below).
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//We want the table view to have all the items in variable `data`. So, we need to have as many cells as the number of items in `data`
return data.count
}
//3: This is called for each cell. It lets you customise each cell how you want.
//For us, we'll make the text inside the cell the current item in the `data` array
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Get the cell from the tableView. Use the identifier you've specified in the storyboard.
let cell = tableView.dequeueReusableCellWithIdentifier("myCellIdentifier", forIndexPath: indexPath)
//Set the text as the current item in `data`
cell.textLabel?.text = data[indexPath.row]
//Use this cell we've just created for the current row
return cell
}
}
I am facing an issue with UITableView.
I want to dynamically fill its cells with data fetched from a remote database, so it takes some times before the data arrived.
Here is the code:
class MailBoxViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var users: [NSDictionary] = []
override func viewDidLoad() {
super.viewDidLoad()
// call to rest API code to get all users in [NSDictionary]
(...)
// set table view delegate and data source
self.tableView.delegate = self
self.tableView.dataSource = self
}
// set number of sections within table view
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
// set number of rows for each section
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return self.users.count
}
return 0
}
// set header title for each section
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Users"
}
}
// set cell content for each row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// deque reusable cell
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as UITableViewCell
// set item title
if indexPath.section == 0 {
cell.textLabel?.text = self.users[indexPath.row]["firstname"] as? String
}
return cell
}
}
The problem is that when tableView functions are called to set number of rows for each section and to set cell content for each row, my [NSDictionary] users is still empty.
How could I do to set rows and cells only after my call to rest API code to get all users in [NSDictionary] is finished?
Thank you for any feedback.
Regards
When you get the response from the API, you should set self.users = arrayOfUsersYouReceivedFromServer and then call self.tableView.reloadData(). This
After users is populated, call tableView.reloadData(). This will call your data source methods again.
When you're done fetching the users call tableView.reloadData().