Random Ads between cells - swift

I am trying to put Ads totally randomly between cells inside a UITableView. I am gonna show my main file to you understand what I am doing and how I want:
Table View Controller:
class Page1: UITableViewController, UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
var employeesSearching = [Employee]()
var isSearching : Bool = false
#IBOutlet weak var GoogleBannerView: GADBannerView!
let collation = UILocalizedIndexedCollation.current()
var sections: [[Any]] = []
var objects: [Any] = [] {
didSet {
let selector: Selector = #selector(getter: UIApplicationShortcutItem.localizedTitle)
sections = Array(repeating: [], count: collation.sectionTitles.count)
let sortedObjects = collation.sortedArray(from: objects, collationStringSelector: selector)
for object in sortedObjects {
let sectionNumber = collation.section(for: object, collationStringSelector: selector)
sections[sectionNumber].append(object as AnyObject)
}
self.tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar.delegate = self
self.tableView.contentOffset = CGPoint(x: 0, y: searchBar.frame.height) //hide searchBar
Shared.instance.employees.sort {
(first, second) in
first.name.compare(second.name, options: .diacriticInsensitive) == .orderedAscending
}
}
func getMatches(letter: String, withArray array: [Employee]) -> [Employee] {
return array.filter({ ($0.name.compare(letter, options: .diacriticInsensitive, range: $0.name.startIndex..<$0.name.index($0.name.startIndex, offsetBy: 1), locale: nil) == .orderedSame)})
}
override func numberOfSections(in tableView: UITableView) -> Int {
if isSearching { return 1 }
return collation.sectionTitles.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let letter = collation.sectionTitles[section]
if isSearching {
return employeesSearching.count
} else {
let matches = getMatches(letter: letter, withArray: Shared.instance.employees)
if !matches.isEmpty { return matches.count }
}
return 0
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if isSearching { return nil }
let letter = collation.sectionTitles[section]
let matches = getMatches(letter: letter, withArray: Shared.instance.employees)
if matches.count == 0 { return nil }
return collation.sectionTitles[section] }
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
if isSearching { return nil }
return collation.sectionIndexTitles }
override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
return collation.section(forSectionIndexTitle: index) }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 3 || indexPath.row == 9 || indexPath.row == 14 {
let cellAd = tableView.dequeueReusableCell(withIdentifier: "cellAd", for: indexPath)
GoogleBannerView?.adUnitID = "ca-app-pub-6043248661561548/4628935113"
GoogleBannerView?.rootViewController = self
GoogleBannerView?.load(GADRequest())
return cellAd
}
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell1
if isSearching {
cell.nameLabel.text = employeesSearching[indexPath.row].name
cell.positionLabel.text = employeesSearching[indexPath.row].position
} else {
let letter = collation.sectionTitles[indexPath.section]
let matches = getMatches(letter: letter, withArray: Shared.instance.employees)
cell.nameLabel.text = matches[indexPath.row].name
cell.positionLabel.text = matches[indexPath.row].position
}
return cell
}
...
}
How do I smuggle a UITableViewCell as! AdCell randomly into the UITableView?
I mean, what should I do in cellForRowAt? I am a bit confused between all these indexed sections.

Firstly you need to generate a random number between 0 and your tableView Datasource array size
let lower : UInt32 = 0
let upper : UInt32 = array.count
let randomIndex = arc4random_uniform(upper - lower) + lower
then you need to add the Ad object in the array at the randomIndex
array.insert(AdObject, atIndex:randomIndex)
then just reload your tableView and handle the different types in cellForRow function

One approach would be to insert some sort of "ad" object at desired locations within your data model.
Then update cellForRowAt to look at the object in the data model for the given index path. If it's an "ad" object, create and setup an "ad" cell. Otherwise create and setup an appropriate data cell as you do now.

Related

TableView SearchBar doesn't work: Index out of range

I know there are some similar questions, but it's doesn't work to me. I'm new to this, so I followed some tutorial trying make a search bar in my table view screen.
I got a problem: there are index out of range and I cannot realise why.
Here is my code:
import UIKit
final class AllGroupsViewController: UITableViewController {
var groups = [
"cats",
"birds",
"dogs",
"books",
"music",
"movies",
"art",
"science",
"tech",
"beauty",
]
#IBOutlet var searchBar: UISearchBar!
var isSearching = false
var filteredData = [String]()
var userGroups: [String] = []
var groupSectionTitles = [String]()
var groupsDictionary = [String: [String]]()
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(
nibName: "GroupCell",
bundle: nil),
forCellReuseIdentifier: "groupCell")
for group in groups {
let groupKey = String(group.prefix(1))
if var groupValues = groupsDictionary[groupKey] {
groupValues.append(group)
groupsDictionary[groupKey] = groupValues
} else {
groupsDictionary[groupKey] = [group]
}
}
groupSectionTitles = [String](groupsDictionary.keys)
groupSectionTitles = groupSectionTitles.sorted(by: { $0 < $1 })
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return groupSectionTitles.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearching {
return filteredData.count
} else {
return groups.count
}
let groupKey = groupSectionTitles[section]
if let groupValues = groupsDictionary[groupKey] {
return groupValues.count
}
return 0
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return groupSectionTitles[section]
}
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return groupSectionTitles
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard
let cell = tableView.dequeueReusableCell(withIdentifier: "groupCell", for: indexPath) as? GroupCell
else { return UITableViewCell() }
var currentGroup = groups[indexPath.row]
let groupKey = groupSectionTitles[indexPath.section]
if let groupValues = groupsDictionary[groupKey] {
currentGroup = groupValues[indexPath.row]
}
if isSearching {
currentGroup = filteredData[indexPath.row]
} else {
currentGroup = groups[indexPath.row]
}
return cell
cell.configure(
photo: UIImage(systemName: "person.3.fill") ?? UIImage(),
name: currentGroup)
return cell
}
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
defer {
tableView.deselectRow(at: indexPath, animated: true)
}
let groupKey = groupSectionTitles[indexPath.section]
var currentGroup = ""
if let groupValues = groupsDictionary[groupKey] {
currentGroup = groupValues[indexPath.row] // here is index out of range
}
if userGroups.firstIndex(of: currentGroup) == nil {
userGroups.append(currentGroup)
}
self.performSegue(withIdentifier: "addGroup", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addGroup",
let myGroupsViewController = segue.destination as? MyGroupsViewController {
myGroupsViewController.groups = userGroups
}
}
}
extension AllGroupsViewController {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredData = groups.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()})
isSearching = true
tableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
isSearching = false
searchBar.text = ""
tableView.reloadData()
}
}
I'll be so glad if somebody will help me. And, please, can you recommend me some good tutorial to achieve my aim?
Actually issue is more to do with logic of accessing groups than crash because of adding search bar.
For example:
override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
if isSearching {
return filteredData.count
} else {
return groups.count
}
let groupKey = groupSectionTitles[section]
if let groupValues = groupsDictionary[groupKey] {
return groupValues.count
}
return 0
}
Here because you use if-else you will either return filteredData.count when searching or groups.count - you will not go beyond this code
So when you are not searching, you will return groups.count which is 10 and that is wrong because you want to return the count for which section we are in, for example a should return 1, b should return 3.
The logic after if-else block should replace logic in else section
Now looking at next two functions:
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
guard let cell
= tableView.dequeueReusableCell(withIdentifier: "groupCell",
for: indexPath) as? GroupCell
else { return UITableViewCell() }
var currentGroup = groups[indexPath.row]
let groupKey = groupSectionTitles[indexPath.section]
if let groupValues = groupsDictionary[groupKey] {
currentGroup = groupValues[indexPath.row]
}
if isSearching {
currentGroup = filteredData[indexPath.row]
} else {
currentGroup = groups[indexPath.row]
}
return cell
cell.configure(
photo: UIImage(systemName: "person.3.fill") ?? UIImage(),
name: currentGroup)
return cell
}
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
defer {
tableView.deselectRow(at: indexPath, animated: true)
}
let groupKey = groupSectionTitles[indexPath.section]
var currentGroup = ""
if let groupValues = groupsDictionary[groupKey] {
currentGroup = groupValues[indexPath.row] // here is index out of range
}
if userGroups.firstIndex(of: currentGroup) == nil {
userGroups.append(currentGroup)
}
self.performSegue(withIdentifier: "addGroup", sender: nil)
}
First because numberOfRowsInSection returns wrong values, we will have issues in these functions.
Then I think the logic of accessing the right data source of groups, group sections is not done right.
For example: currentGroup = groups[indexPath.row] in cellForRowAt indexPath is not right because this gets group from group array of 10 when we only want to group for the specific section.
And also I see return cell twice so code after the first will not be run.
So what I did is just refactored these functions to make it more clear and added some comments.
First, we need to keep in mind the different data sources:
// All the groups
var groups = [
"cats",
"birds",
"dogs",
"books",
"music",
"movies",
"art",
"science",
"tech",
"beauty",
]
// Checks if search is active or not
var isSearching = false
// This will hold the filtered array when searching
var filteredData = [String]()
// This will hold groups of the user
var userGroups: [String] = []
// This will hold section prefixes [a, b, c, etc]
var groupSectionTitles = [String]()
// This will hold mapping of prefixes to groups
// [a: [art], b: [beauty, books], etc]
var groupsDictionary = [String: [String]]()
There is nothing different above from your code, only comments, however we have to keep a visual image of this because this is important to how we need to access the data
Next, I created this function to get the correct groups in a section since we need to do this many times
private func getGroups(in section: Int) -> [String]
{
// The current section should be got from groupSectionTitles
let groupKey = groupSectionTitles[section]
var groupsInSection: [String] = []
// Get groups for current section
if let groupValues = groupsDictionary[groupKey] {
groupsInSection = groupValues
}
// Change groups in section if searching
if isSearching {
groupsInSection = filteredData
}
return groupsInSection
}
Then I refactored these functions slightly:
override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int
{
if isSearching {
return filteredData.count
} else {
let groupsInSection = getGroups(in: section)
return groupsInSection.count
}
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
guard let cell
= tableView.dequeueReusableCell(withIdentifier: "groupCell",
for: indexPath) as? GroupCell
else { return UITableViewCell() }
let groupsInSection = getGroups(in: indexPath.section)
cell.configure(
photo: UIImage(systemName: "person.3.fill") ?? UIImage(),
name: groupsInSection[indexPath.row])
return cell
}
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath)
{
let groupsInSection = getGroups(in: indexPath.section)
let currentGroup = groupsInSection[indexPath.row]
if userGroups.firstIndex(of: currentGroup) == nil {
userGroups.append(currentGroup)
}
defer {
tableView.deselectRow(at: indexPath, animated: true)
}
self.performSegue(withIdentifier: "addGroup", sender: nil)
}
I think now your crash will be resolved and things work as expected.
However, since you did not connect and implement search delegate yet, maybe there can be some issues when isSearching becomes true but I think that can be for another question on filtering with search delegate.
For tutorials, you can have a look at:
UISearchResultsController tutorial - watch from minute 8 onwards
UISearchBar tutorial - watch from minute 10 onwards
StackOverflow discussion

How to identify first and last rows of each section | Swift

How can I identify the first and last rows of each section in a dynamic tableview and make a view inside the cell class hidden.
For each first cell in each section I need to hide the topView, for each last row of each section, I need to hide the bottomView.
For example I have the following Class:
class cell: UITableViewCell {
#IBOutlet weak var topView: UIView!
#IBOutlet weak var bottomView: UIView!
}
I have tried to identify the last row of each section by doing the following, but it simple does not hide the correct bottomView, except for in the last section. Is there a way to identify the rows correctly?
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! cell
let item = sections[indexPath.section].items[indexPath.row]
structure = sections[indexPath.section].items
let totalRow = tableView.numberOfRows(inSection: indexPath.section)
if(indexPath.row == totalRow - 1)
{
cell.bottomView.isHidden = true
}
return cell
}
var sections = [mySections]()
var structure = [myStructure]()
Fetching Data:
private func fetchJSON() {
guard let url = URL(string: "test.com")
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "id=\1".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
self.structure.sort { $0. datestamp > $1.datestamp }
let res = try decoder.decode([myStructure].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0. datestamp })
let keys = grouped.keys.sorted()
self.sections = keys.map({mySections(date: $0, items: grouped[$0]!
)})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
print(error)
}
}.resume()
}
Struct:
struct mySections {
let date : String
var items : [myStructure]
}
struct myStructure: Decodable {
let recordid: Int
let testname: Int
let datestamp: String
}
Example of Data:
[
{
"recordid": 1,
"testname": "Jen",
"datestamp": "2021-11-3"
},
{
"recordid": 1,
"testname": "Jake",
"datestamp": "2021-11-2"
}
]
Setting Up Sections:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let section = sections[section]
return section.items.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].date
}
When you are in your delegate creating cells, you are in the process of telling the table view what rows and sections it has. That means that the table view hasn't finished setting up sections so it's not the right time to call tableView.numberOfRows(inSection:).
You're already pulling your data out of the model... in this case it looks like your model has an array of sections and each section has an array of rows, so ask the model whether or not the cell your building is at the beginning or end of its section:
import UIKit
import SwiftUI
import PlaygroundSupport
class CustomCell : UITableViewCell {
static let identifier = "CustomCell"
}
class DataSource : NSObject, UITableViewDataSource {
let sections = [
[
"Cow",
"Duck",
"Chicken"
],
[
"Lion",
"Zebra",
"Oryx"
],
]
func numberOfSections(in tableView: UITableView) -> Int {
sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let newCell = tableView.dequeueReusableCell(withIdentifier: CustomCell.identifier, for: indexPath)
if let cell = newCell as? CustomCell {
cell.textLabel?.text = sections[indexPath.section][indexPath.row]
if indexPath.row == 0 {
cell.textLabel?.backgroundColor = UIColor.yellow
}
if indexPath.row == sections[indexPath.section].count - 1 {
cell.textLabel?.backgroundColor = UIColor.gray
}
}
return newCell
}
}
let tableView = UITableView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
tableView.register(CustomCell.self, forCellReuseIdentifier: CustomCell.identifier)
let dataSource = DataSource()
tableView.dataSource = dataSource
PlaygroundSupport.PlaygroundPage.current.liveView = tableView

UILocalizedIndexedCollation in this use case

How to add section headers and index list to UITableView in this use case?
#IBOutlet var tableView: UITableView!
var detail: Detail? = nil
var list = [tabledata]()
let search = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
list = [
tabledata(name:"something".localized, sort:"sort.something".localized, id:"something.html"),
tabledata(name:"somethingelse".localized, sort:"sort.somethingelse".localized, id:"somethingelse.html"),
...
]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "library", for: indexPath)
var data: tabledata
data = list[indexPath.row]
cell.textLabel!.text = data.name
return cell
}
Now the point is that table data are going to be translated.
Note that
name: is the actual cell name going to be .localized
sort: has to help characters like á é etc. in cell name to sort properly (avoiding them to show in the end of alphabet)
id: calls the html file location to display in detailViewController ('cause name has to be translated and we want a static text here)
A usual implementation of section headers and index list will result in something like
T // section header
translation // cell names
transmission
...
T // table in
Übersetzung // another language
Getriebe
...
What's the correct model for UILocalizedIndexedCollation?
.xcodeproj on my github. More info on demand.
Thanks for help!
Edit: it turns out that "getting the first letter of each row to use as the index" is much more complicated than I thought when accounting for multiple languages, especially non-Latin ones. I'm making use of UILocalizedIndexedCollation to simplify this task.
I think that UILocalizedIndexedCollation is more confusing than rolling your own data models. You need to 2 models: one to represent the row and one to represent the section:
// UILocalizedIndexedCollation uses a selector on the `name` property
// so we have to turn this data type in to a class.
class TableRow: NSObject {
#objc var name: String
var id: String
var sectionTitle = ""
init(name: String, id: String) {
self.name = name
self.id = id
}
}
// TableSection does not have to be a class but we are making it so
// that it's consistent with `TableRow`
class TableSection: NSObject {
var title: String
var rows: [TableRow]
init(title: String, rows: [TableRow]) {
self.title = title
self.rows = rows
}
}
After that, populating and filtering the table is very easy:
class Library: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var detail: Detail? = nil
var list = [TableSection]()
var filter = [TableSection]()
let search = UISearchController(searchResultsController: nil)
let collation = UILocalizedIndexedCollation.current()
override func viewDidLoad() {
super.viewDidLoad()
// search
search.searchResultsUpdater = self as UISearchResultsUpdating
search.obscuresBackgroundDuringPresentation = false
search.searchBar.placeholder = "search".localized
navigationItem.hidesSearchBarWhenScrolling = false
navigationItem.searchController = search
definesPresentationContext = true
// Set the color of the index on the right of the table.
// It's settable from Interface Builder as well
tableView.sectionIndexColor = UIColor(red: 0, green: 122.0 / 255.0, blue: 1, alpha: 1)
// I took the liberty to add a few more items to the array
let rows = ["something", "somethingelse", "apple", "orange", "apricot", "strawberry"].map {
TableRow(name: $0.localized, id: $0)
}
list = organizeIntoSections(rows: rows)
tableView.reloadData()
}
// Organize rows into sections with titles
func organizeIntoSections(rows: [TableRow]) -> [TableSection] {
// Organize the rows into sections based on their `name` property
let selector: Selector = #selector(getter: TableRow.name)
// Sort the rows by `name`
let sortedRows = collation.sortedArray(from: rows, collationStringSelector: selector) as! [TableRow]
// Allocate rows into sections
var sections = collation.sectionTitles.map { TableSection(title: $0, rows: []) }
for row in sortedRows {
let sectionNumber = collation.section(for: row, collationStringSelector: selector)
sections[sectionNumber].rows.append(row)
}
// Remove empty sections
sections.removeAll(where: { $0.rows.isEmpty })
return sections
}
override func viewWillAppear(_ animated: Bool) {
if let selection = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: selection, animated: animated)
}
super.viewWillAppear(animated)
}
// MARK: - Table View
func numberOfSections(in tableView: UITableView) -> Int {
return filtering() ? filter.count : list.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
// If a section has no row, don't show its header
let data = filtering() ? filter[section] : list[section]
return data.rows.isEmpty ? nil : data.title
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filtering() ? filter[section].rows.count : list[section].rows.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "library", for: indexPath)
let data = filtering() ? filter[indexPath.section].rows[indexPath.row]
: list[indexPath.section].rows[indexPath.row]
cell.textLabel!.text = data.name
return cell
}
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return filtering() ? filter.map { $0.title } : list.map { $0.title }
}
// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "transporter" {
if let indexPath = tableView.indexPathForSelectedRow {
let selected = filtering() ? filter[indexPath.section].rows[indexPath.row]
: list[indexPath.section].rows[indexPath.row]
let controller = (segue.destination as! Detail)
// This assumes you change `controller.result` to have type TableRow
controller.result = selected
}
}
}
// search filter
func filterContent(_ searchText: String) {
let query = searchText.lowercased()
filter = list.compactMap { section in
let matchingRows = section.rows.filter { $0.name.lowercased().contains(query) }
return matchingRows.isEmpty ? nil : TableSection(title: section.title, rows: matchingRows)
}
tableView.reloadData()
}
func searchEmpty() -> Bool {
return search.searchBar.text?.isEmpty ?? true
}
func filtering() -> Bool {
return search.isActive && (!searchEmpty())
}
}
Results:
English:
Italian:
Slovak:
(I got the translation from Google Translate so my apology in advance if any word is out of whack -- I cannot speak Italian or Slovak)

Why is JSON data from local path shows incorrectly in the UITableView?

I want to parse JSON data from a local file that is available in the project and then populate these data to UITableView.
My Requirements
parse the json data from local path not from URL
Populate the json data to UITableView
Facing problems
Unable to display the parsed data, ( bracket is displaying in the table view.
I am able to print the data in console using dump() but unable to print data in tableView
Updated view controller for passing data to another controller.
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lookArrayModel.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = myTableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let displayData = lookArrayModel[indexPath.row]
cells.textLabel?.text = String(describing: displayData.Lookname!)
cells.detailTextLabel?.text = String(describing: displayData.Lookdetails!)
// print(displayData.shadeModel)
return cells
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = myTableView.indexPathForSelectedRow;
let currentCell = myTableView.cellForRow(at: indexPath!) as UITableViewCell!;
lookNameValue = currentCell?.textLabel?.text
lookDetailValue = currentCell?.detailTextLabel?.text
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//let lookShade = LookModelData()
if (segue.identifier == "segueToLook") {
let destController:DetailsViewController = segue.destination as! DetailsViewController
//Set the selecte row index value
destController.LabelText = String(describing: lookNameValue)
destController.DetailText = String(describing: lookDetailValue)
// destController.arrayData = lookShade.shadeModel as! NSMutableArray
}
}
}
Destination view controller. Swift
class DetailsViewController:UIViewController,UITableViewDataSource,UITableViewDelegate {
var lookArrayModel = [LookModelData]()
var arrayData: NSMutableArray = []
#IBOutlet weak var secondView: UITableView!
var LabelText = String()
var DetailText = String()
var shadeText = String()
#IBOutlet weak var LookLabel: UILabel!
#IBOutlet weak var LookName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print(arrayData)
LookName?.text = LabelText
LookLabel?.text = DetailText
secondView.dataSource = self
secondView.delegate = self
secondView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayData.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = secondView.dequeueReusableCell(withIdentifier: "secondCell", for: indexPath)
let displayData = arrayData
// cells.textLabel?.text = String(describing: (displayData as AnyObject))
// print(arrayData)
return cells
}
}
Please check my code :
Changed lookArrayModel type NSMutableArray to [LookModelData]. Like those I did some changes. Please check.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var lookArrayModel = [LookModelData]()
#IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
guard let Path = Bundle.main.path(forResource: "ColorShade", ofType: "json") else { return }
let url = URL(fileURLWithPath: Path)
do {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
myTableView.dataSource = self
myTableView.delegate = self
//Calling the function for adding look
createLooks(dictionary: json as! NSArray)
myTableView.reloadData()
} catch {
print(error)
}
}
func createLooks(dictionary:NSArray) {
for item in dictionary {
let item1 = item as! NSDictionary
let lookModal = LookModelData()
lookModal.Lookname = item1.value(forKey: "Lookname") as? String
lookModal.LookId = item1.value(forKey: "LookId") as? String
lookModal.Lookdetails = item1.value(forKey: "Lookdetails") as? String
lookModal.shadeModel = createshade(shades: item1.value(forKey: "shades") as! NSArray)
lookArrayModel.append(lookModal)
}
}
func createshade(shades: NSArray) -> [ShadeDescription] {
var arrayShade = [ShadeDescription]()
for item in shades
{
let item1 = item as! NSDictionary
let shadeModal = ShadeDescription()
shadeModal.comboID = item1.value(forKey: "comboID") as? String
shadeModal.shadeName = item1.value(forKey: "shadeName") as? String
shadeModal.ShadeType = item1.value(forKey: "ShadeType") as? String
shadeModal.ShadeCode = item1.value(forKey: "shadeCode") as? String
arrayShade.append(shadeModal)
}
return arrayShade
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lookArrayModel.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = myTableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let displayData = lookArrayModel[indexPath.row]
// You will get like this
// print(displayData.LookId!)
// print(displayData.Lookname!)
// print(displayData.Lookdetails!)
// print(displayData.shadeModel!)
// This is the way to get shade model data
if let shadeModels = displayData.shadeModel {
for var shadeModel in shadeModels {
print(shadeModel.comboID)
print(shadeModel.ShadeType)
print(shadeModel.shadeName)
print(shadeModel.ShadeCode)
}
}
cells.textLabel?.text = String(describing: displayData.Lookname!)
return cells
}
}
class LookModelData
{
var Lookname:String?
var LookId:String?
var Lookdetails:String?
//Shades Array
var shadeModel : [ShadeDescription]?
}
class ShadeDescription {
var ShadeType:String?
var shadeName:String?
var comboID:String?
var ShadeCode:String?
}

I get the error "index 6 beyond bounds [0 .. 5]' " when implementing search in my app

Here's my code. While following multiple tutorials on how to implement search in Swift I've had no luck.
import UIKit
class DataTableExercisesTableViewController: UITableViewController, UISearchBarDelegate, UISearchResultsUpdating {
var exercises = ["Abs", "Arms", "Back", "Chest", "Legs", "Shoulders", "Triceps"]
var searchActive : Bool = false
#IBOutlet weak var searchBar: UISearchBar!
var filteredTableData = [String]()
var resultSearchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
// Reload the table
self.tableView.reloadData()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return exercises.count;
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! as UITableViewCell;
if (self.resultSearchController.active) {
cell.textLabel?.text = filteredTableData[indexPath.row]
return cell
}
else {
cell.textLabel?.text = exercises[indexPath.row]
return cell
}
}
func updateSearchResultsForSearchController(searchController: UISearchController)
{
filteredTableData.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (exercises as NSArray).filteredArrayUsingPredicate(searchPredicate)
filteredTableData = array as! [String]
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
I've been having trouble implementing search from different tutorials and just doesn't seem to be working out too well. Any insight is much appreciated.
Your numberOfRowsInSection is always returning exercises.count. But when you are filtering, you are not using exercises, but a smaller array, filteredTableData. So, just as in cellForRowAtIndexPath, you need to change your answer if you are filtering.
The best solution is before access the array value just the check the total count should less the the index you want fetch from the array or use below way to iterate the array
EX :
let arrayOfInts: [Int] = [1, 2, 3];
for i in arrayOfInts {
print(i);
}
In your case you could change the code :
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var rowCount = 0
if(self.resultSearchController.active){
rowCount = filteredTableData.count
}
else{
rowCount = exercises.count
}
return rowCount;
}