Search Bar displaying results in both sections of Table View - arrays

I have a problem in my iOS app, I added 2 sections to my table view but, the search bar is displaying its results in both of my sections instead of just once over all.
Just guessing here, but may the problem be because the search bar doesn't have it's own section so it places the results in both? Do I have to add another section? Fix the code? Or add another tableview/ view controller to handle the filteredArray?
Code:
dataArray is where I have all my arrays from my server
followedArray is where certain arrays go from dataArray
filteredArray is the searched arrays from dataArray
numberOfRowsInSection
if (!isFiltered) {
if (section == 0) {
return [followedArray count];
}
else {
return [dataArray count];
}
}
return [filteredArray count];
titleForHeaderInSection
if (section == 0) {
return #"Followed Data";
}
else {
return #"All Data";
}
cellForRowAtIndexPath
Data * dataObject;
if (!isFiltered) {
if (indexPath.section == 0) {
dataObject = [followedArray objectAtIndex:indexPath.row];
}
else {
dataObject = [dataArray objectAtIndex:indexPath.row];
}
}
else {
dataObject = [filteredArray objectAtIndex:indexPath.row];
}
searchBar textDidChange
if (searchText.length == 0) {
isFiltered = NO;
} else {
isFiltered = YES;
filteredArray = [[NSArray alloc] init];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"self.dataName contains[c] %#", searchText];
filteredArray = [dataArray filteredArrayUsingPredicate:resultPredicate];
}
[myTableView reloadData];
--------------------------------
What am I doing wrong here? Or am I missing something?
I want to have my followedArray in Section 1, dataArray in Section 2, and the filteredArray when searching the dataArray with the Search Bar to display it independently and not in both sections like how it's doing it now. Thank you!

Very simple answer but I was new to iOS
// Title for Header
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if !(searchController.isActive && searchController.searchBar.text != "") {
if section == 0 {
return "Followed Data"
}
else {
return "All Data"
}
}
return "Filtered Data"
}
// Number of Rows in Section
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if !(searchController.isActive && searchController.searchBar.text != "") {
if section == 0 {
return followedArray.count
}
else if (section == 1) {
return dataArray.count
}
}
return filteredArray.count
}
// Number of Sections
func numberOfSections(in tableView: UITableView) -> Int {
if !(searchController.isActive && searchController.searchBar.text != "") {
return 2
}
return 1
}

Related

Hide Show Section According to server response swift

I am developing an app which used table view with multiple section and multiple header view. Is it possible to hide randomly section according to server response, i.e.: I have 6 Multiple cells and header view. I got response from the server which show only 2 sections, 4 section and 6 section. I am trying to achieve this, but didn't get success. Here is my code below:
Code:
var allPermissionSection = ["A","B","C","D","E","F"] // default permission
override func numberOfSections(in tableView: UITableView) -> Int {
return totalSectionTable // total count of showing cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let modelPermission = mainArrayCheckValidations[0] as! MyAcountModel
if section == 0 {
return 1
}
// arraySectionName got from server check index if default section or server reponse index is equal to same then true this continue
if allPermissionSection[0] == arraySectionName[0] {
if arrayTblItems.count > 0 {
return arrayTblItems.count
}else if modelPermission.phoneNumbersEnabled == true {
return 1
}
}
if allPermissionSection[1] == arraySectionName[1] {
if arrayTblItemsForElectronic.count > 0 {
return arrayTblItemsForElectronic.count
}else if modelPermission.econsentEnabled == true {
return 1
}
}
if allPermissionSection[2] == arraySectionName[2] {
if arrayTblItemsForEmergnceContact.count > 0 {
return arrayTblItemsForEmergnceContact.count
}else if modelPermission.emergencyContactsEnabled == true {
return 1
}
}
return 0
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let modelPermission = mainArrayCheckValidations[0] as! MyAcountModel
if indexPath.section == 0 {
return UITableView.automaticDimension
}
if allPermissionSection[0] == arraySectionName[0] {
// if indexPath.section == 1 {
if arrayTblItems.count <= 0 {
if modelPermission.phoneNumbersEnabled == true{
return 40
}else{
return 0
}
}
// }
}
if allPermissionSection[1] == arraySectionName[1] {
// if indexPath.section == 2 {
if arrayTblItemsForElectronic.count <= 0 {
if modelPermission.econsentEnabled == true{
return 40
}else{
return 0
}
}
// }
}
if allPermissionSection[2] == arraySectionName[2] {
// if indexPath.section == 3 {
if arrayTblItemsForEmergnceContact.count <= 0 {
if modelPermission.emergencyContactsEnabled == true{
return 40
}else{
return 0
}
}
// }
}
return UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: USER_DETAIL_CELL, for: indexPath) as! UserDetail_Cell
cell.updateWith(mainArrayCheckValidations[indexPath.row], indexPath.row, mainArrayCheckValidations.count, tblCellType)
return cell
}
if allPermissionSection[0] == arraySectionName[0] {
// if indexPath.section == 1 {
if arrayTblItems.count > 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: PHONE_CELL, for: indexPath) as! PhoneNumberDetailCell
cell.updateWith(arrayTblItems[indexPath.row], indexPath.row, arrayTblItems.count, tblCellType)
return cell
}else{
let cell = tableView.dequeueReusableCell(withIdentifier: DEFAULT_CELL, for: indexPath) as! Default_Custom_Cell
cell.lbl_Name.text = ""
return cell
}
//}
}
if allPermissionSection[1] == arraySectionName[1] {
// if indexPath.section == 2 {
if arrayTblItemsForElectronic.count > 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: CONSTENT_CELL, for: indexPath) as! Constent_Cell
cell.updateWith(arrayTblItemsForElectronic[indexPath.row], indexPath.row, arrayTblItemsForElectronic.count, tblCellType)
return cell
}else{
let cell = tableView.dequeueReusableCell(withIdentifier: DEFAULT_CELL, for: indexPath) as! Default_Custom_Cell
cell.lbl_Name.text = ""
return cell
}
// }
}
if allPermissionSection[2] == arraySectionName[2] {
// if indexPath.section == 3 {
if arrayTblItemsForEmergnceContact.count > 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: EMERGENCY_CONTACT_CELL, for: indexPath) as! EmergencyCnctCell
cell.updateWith(arrayTblItemsForEmergnceContact[indexPath.row], indexPath.row, arrayTblItemsForEmergnceContact.count, tblCellType)
return cell
}else{
let cell = tableView.dequeueReusableCell(withIdentifier: DEFAULT_CELL, for: indexPath) as! Default_Custom_Cell
cell.lbl_Name.text = ""
return cell
}
// }
}
return UITableViewCell()
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let headerView = setupHeaderView(textHeading: " Details")
return headerView
}
if allPermissionSection[0] == arraySectionName[0] {
//if section == 1 {
let headerView = setupHeaderView(textHeading: "bers")
return headerView
// }
}
if allPermissionSection[1] == arraySectionName[1] {
// if section == 2 {
let headerView = setupHeaderView(textHeading: "sent")
return headerView
// }
}
if allPermissionSection[2] == arraySectionName[2] {
// if section == 3 {
let headerView = setupHeaderView(textHeading: "ontacts")
return headerView
// }
}
return UIView()
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return 50
}
if allPermissionSection[0] == arraySectionName[0] {
// if section == 1 {
return 50
// }
}
if allPermissionSection[1] == arraySectionName[1] {
// if section == 2 {
return 50
// }
}
if allPermissionSection[1] == arraySectionName[1] {
// if section == 3 {
return 50
// }
}
return 0
}
setupHeaderView(textHeading:String) -> UIView? {
let headerView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: tblVw.frame.width, height: 50))
let headingLbl = UILabel()
let topLbl = UILabel()
let botomLbl = UILabel()
topLbl.translatesAutoresizingMaskIntoConstraints = false
botomLbl.translatesAutoresizingMaskIntoConstraints = false
headerView.addSubview(botomLbl)
headerView.addSubview(topLbl)
botomLbl.leadingAnchor.constraint(equalTo: headerView.leadingAnchor).isActive = true
botomLbl.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
botomLbl.heightAnchor.constraint(equalToConstant:0.5).isActive = true
botomLbl.bottomAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true
topLbl.leadingAnchor.constraint(equalTo: headerView.leadingAnchor).isActive = true
topLbl.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
topLbl.heightAnchor.constraint(equalToConstant:0.5).isActive = true
topLbl.topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true
botomLbl.text = NULL_STRING
topLbl.text = NULL_STRING
topLbl.backgroundColor = .lightGray
botomLbl.backgroundColor = .lightGray
headingLbl.frame = CGRect.init(x: 0, y: 0, width: headerView.frame.width, height: headerView.frame.height)
headingLbl.text = textHeading
headingLbl.textColor = UIColor(hexString: "587358")
headerView.backgroundColor = UIColor(displayP3Red: 245/256, green: 245/256, blue: 245/256, alpha: 0.6)
headingLbl.textAlignment = .center
headerView.addSubview(headingLbl)
return headerView
}
So, Here is the my code please let me know if i m doing wrong please correct me
Can someone please explain to me hide or show randomly section,
Any help would be greatly appreciated.
Thanks in advance.
class AllDetailAccount {
var HeaderName: String?
var HeaderDeatil: [Any]?
init(HeaderName: String, HeaderDeatil: [Any]) {
self.HeaderName = HeaderName
self.HeaderDeatil = HeaderDeatil
}
}
class UnitUserDetail: AbstractController {
var allAcountDetailInfo = [AllDetailAccount]()
override func numberOfSections(in tableView: UITableView) -> Int {
return allAcountDetailInfo.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if allAcountDetailInfo[section].HeaderName == "USERDETAIL_STRING" {
return 1
}else if allAcountDetailInfo[section].HeaderName == "PHONE_NUMBR_STRING" {
let modelDate = allAcountDetailInfo[section].HeaderDeatil?[0] as! [PhoneNumber]
if modelDate.count == 0 {
return 1
}
return modelDate.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if allAcountDetailInfo[indexPath.section].HeaderName == "USERDETAIL_STRING" {
let modelDate = allAcountDetailInfo[indexPath.section].HeaderDeatil![0] as! UnitUserDetailModel
let cell = tableView.dequeueReusableCell(withIdentifier: "USER_DETAIL_CELL", for: indexPath) as! UserDetail_Cell
cell.updateWith(modelDate, indexPath.row, 0, tblCellType)
return cell
}else if allAcountDetailInfo[indexPath.section].HeaderName == "PHONE_NUMBR_STRING" {
let modelDate = allAcountDetailInfo[indexPath.section].HeaderDeatil?[0] as! [PhoneNumber]
let cell = tableView.dequeueReusableCell(withIdentifier: "PHONE_CELL", for: indexPath) as! PhoneNumberDetailCell
cell.updateWith(modelDate[indexPath.row], indexPath.row, modelDate.count, tblCellType)
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "DEFAULT_CELL", for: indexPath) as! Default_Custom_Cell
return cell
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 40))
view.backgroundColor = UIColor(displayP3Red: 245/256, green: 245/256, blue: 245/256, alpha: 0.6)
let lbl = UILabel(frame: CGRect(x: 15, y: 0, width: view.frame.width - 15, height: 40))
lbl.font = UIFont.systemFont(ofSize: 20)
lbl.text = allAcountDetailInfo[section].HeaderName
lbl.textAlignment = .center
lbl.textColor = UIColor(hexString: "HEADER_LABLE_TEXT_COLOUR")
let topLbl = UILabel()
view.addSubview(topLbl)
topLbl.translatesAutoresizingMaskIntoConstraints = false
topLbl.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
topLbl.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
topLbl.heightAnchor.constraint(equalToConstant:0.5).isActive = true
topLbl.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
topLbl.backgroundColor = .lightGray
topLbl.text = NULL_STRING
view.addSubview(lbl)
return view
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
}

Swift 3 JSON to array of objects is very slow

I am upgrading my app from swift 2 to swift 3 and have a significant performance loss when building an array of custom objects. In swift 2 this took a few seconds while in swift 3 it takes around 30 seconds. I am using alamofire to return swiftyJSON, returning about 3000 rows of data. the alamofire return is quick, its looping through this json to build array of custom objects thats slow. These objects greatly simplify the code written when building table cells and passing data to new views.
Code:
class Customer {
var ID: String!
var sysName: String!
var address: String!
var contactID: String!
required init(_name:String?, _id: String?, _address:String?, _contactID:String?) {
//print(json)
if _id != nil {
self.ID = _id
}else{
self.ID = ""
}
if _name != nil {
self.sysName = _name
}else{
self.sysName = ""
}
if _address != nil {
self.address = _address
}else{
self.address = "No Address on File"
}
if _contactID != nil {
self.contactID = _contactID
}else{
self.contactID = ""
}
}
}
Alamofire.request(API.Router.customerList()).responseJSON() {
response in
print(response.request ?? "") // original URL request
print(response.response ?? "") // URL response
print(response.data ?? "") // server data
print(response.result) // result of response serialization
if let json = response.result.value {
print("JSON: \(json)")
self.customers = JSON(json)
self.parseJSON()
}
}
func parseJSON(){
let jsonCount = self.customers["customers"].count
self.totalCustomers = jsonCount
for i in 0 ..< jsonCount {
self.loadedCustomers = i
print("customer = \(self.customers["customers"][i] ["sysName"].string!)")
//VERY SLOW
//create a customer object
let customer = Customer( _name: self.customers["customers"][i]["sysName"].string!, _id: self.customers["customers"][i]["ID"].string!, _address: self.customers["customers"][i]["mainAddr"].string!, _contactID: self.customers["customers"][i]["contactID"].string!)
//add customer to customer array
self.customersArray.append(customer)
}
self.layoutViews() //build view, call all table methods
}
Thanks
Improved code:
import Foundation
import UIKit
import Alamofire
import SwiftyJSON
enum SearchMode{
case name
case address
}
class CustomerListViewController: ViewControllerWithMenu, UITableViewDelegate, UITableViewDataSource, UISearchControllerDelegate, UISearchBarDelegate, UISearchDisplayDelegate, UISearchResultsUpdating{
var indicator: SDevIndicator!
var totalCustomers:Int!
//data arrays
var ids = [String]()
var names = [String]()
var addresses = [String]()
var searchController:UISearchController!
var currentSearchMode = SearchMode.name
var customerTableView:TableView = TableView()
var layoutVars:LayoutVars = LayoutVars()
var sections : [(index: Int, length :Int, title: String)] = Array()
var customersSearchResults:[String] = []
var shouldShowSearchResults:Bool = false
let viewsConstraint_V:NSArray = []
let viewsConstraint_V2:NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
title = "Customer List"
view.backgroundColor = layoutVars.backgroundColor
getCustomerList()
}
func getCustomerList() {
//remove any added views (needed for table refresh
for view in self.view.subviews{
view.removeFromSuperview()
}
// Show Indicator
indicator = SDevIndicator.generate(self.view)!
Alamofire.request(API.Router.customerList()).responseJSON() {
response in
//print(response.request ?? "") // original URL request
//print(response.response ?? "") // URL response
//print(response.data ?? "") // server data
//print(response.result) // result of response serialization
do {
if let data = response.data,
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let customers = json["customers"] as? [[String: Any]] {
for customer in customers {
if let id = customer["ID"] as? String {
self.ids.append(id)
}
if let name = customer["sysName"] as? String {
self.names.append(name)
}
if let address = customer["mainAddr"] as? String {
self.addresses.append(address)
}
}
}
} catch {
print("Error deserializing JSON: \(error)")
}
// build sections based on first letter(json is already sorted alphabetically)
var index = 0;
var firstCharacterArray:[String] = [" "]
for i in 0 ..< self.names.count {
let stringToTest = self.names[i].uppercased()
let firstCharacter = String(stringToTest[stringToTest.startIndex])
if(i == 0){
firstCharacterArray.append(firstCharacter)
}
if !firstCharacterArray.contains(firstCharacter) {
let title = firstCharacterArray[firstCharacterArray.count - 1]
firstCharacterArray.append(firstCharacter)
let newSection = (index: index, length: i - index, title: title)
self.sections.append(newSection)
index = i;
}
if(i == self.names.count - 1){
let title = firstCharacterArray[firstCharacterArray.count - 1]
let newSection = (index: index, length: i - index, title: title)
self.sections.append(newSection)
}
}
self.layoutViews()
}
}
func layoutViews(){
indicator.dismissIndicator()
searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.placeholder = "Search Customers"
searchController.searchResultsUpdater = self
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.dimsBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = false
navigationItem.titleView = searchController.searchBar
let items = ["Name","Address"]
let customSC = SegmentedControl(items: items)
customSC.selectedSegmentIndex = 0
customSC.addTarget(self, action: #selector(self.changeSearchOptions(sender:)), for: .valueChanged)
self.view.addSubview(customSC)
self.customerTableView.delegate = self
self.customerTableView.dataSource = self
self.customerTableView.register(CustomerTableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(self.customerTableView)
//auto layout group
let viewsDictionary = [
"view2":customSC,
"view3":self.customerTableView
]as [String:AnyObject]
let sizeVals = ["fullWidth": layoutVars.fullWidth,"width": layoutVars.fullWidth - 30,"navBottom":layoutVars.navAndStatusBarHeight,"height": self.view.frame.size.height - 100] as [String:Any]
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view2(fullWidth)]", options: [], metrics: sizeVals, views: viewsDictionary))
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view3(fullWidth)]", options: [], metrics: sizeVals, views: viewsDictionary))
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-navBottom-[view2(40)][view3(height)]", options: [], metrics: sizeVals, views: viewsDictionary))
}
/////////////// Search Methods ///////////////////////
func changeSearchOptions(sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
currentSearchMode = .name
break
case 1:
currentSearchMode = .address
break
default:
currentSearchMode = .name
break
}
filterSearchResults()
}
func updateSearchResults(for searchController: UISearchController) {
filterSearchResults()
}
func filterSearchResults(){
customersSearchResults = []
switch currentSearchMode {
case .name:
self.customersSearchResults = self.names.filter({( aCustomer: String ) -> Bool in
return (aCustomer.lowercased().range(of: self.searchController.searchBar.text!.lowercased()) != nil) })
break
case .address:
self.customersSearchResults = self.addresses.filter({( aCustomer: String) -> Bool in
return (aCustomer.lowercased().range(of: self.searchController.searchBar.text!.lowercased()) != nil)
})
break
}
self.customerTableView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
//print("searchBarTextDidBeginEditing")
shouldShowSearchResults = true
self.customerTableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
shouldShowSearchResults = false
self.customerTableView.reloadData()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if !shouldShowSearchResults {
shouldShowSearchResults = true
self.customerTableView.reloadData()
}
searchController.searchBar.resignFirstResponder()
}
/////////////// TableView Delegate Methods ///////////////////////
func numberOfSections(in tableView: UITableView) -> Int {
if shouldShowSearchResults{
return 1
}else{
return sections.count
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
//print("titleForHeaderInSection")
if shouldShowSearchResults{
return nil
}else{
if(sections[section].title == "#"){
return " # \(self.totalCustomers) Customers Found"
}else{
return " " + sections[section].title //hack way of indenting section text
}
}
}
func sectionIndexTitles(for tableView: UITableView) -> [String]?{
print("sectionIndexTitlesForTableView 1")
if shouldShowSearchResults{
return nil
}else{
//print("sectionIndexTitlesForTableView \(sections.map { $0.title })")
return sections.map { $0.title }
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
//print("heightForHeaderInSection")
if shouldShowSearchResults{
return 0
}else{
return 50
}
}
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
return index
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//print("numberOfRowsInSection")
if shouldShowSearchResults{
return self.customersSearchResults.count
} else {
return sections[section].length
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = customerTableView.dequeueReusableCell(withIdentifier: "cell") as! CustomerTableViewCell
customerTableView.rowHeight = 50.0
if shouldShowSearchResults{
let searchString = self.searchController.searchBar.text!.lowercased()
if(currentSearchMode == .name){
cell.nameLbl.text = self.customersSearchResults[indexPath.row]
cell.name = self.customersSearchResults[indexPath.row]
if let i = self.names.index(of: cell.nameLbl.text!) {
//print("\(cell.nameLbl.text!) is at index \(i)")
cell.addressLbl.text = self.addresses[i]
cell.address = self.addresses[i]
cell.id = self.ids[i]
} else {
cell.addressLbl.text = ""
cell.address = ""
cell.id = ""
}
//text highlighting
let baseString:NSString = cell.name as NSString
let highlightedText = NSMutableAttributedString(string: cell.name)
var error: NSError?
let regex: NSRegularExpression?
do {
regex = try NSRegularExpression(pattern: searchString, options: .caseInsensitive)
} catch let error1 as NSError {
error = error1
regex = nil
}
if let regexError = error {
print("Oh no! \(regexError)")
} else {
for match in (regex?.matches(in: baseString as String, options: NSRegularExpression.MatchingOptions(), range: NSRange(location: 0, length: baseString.length)))! as [NSTextCheckingResult] {
highlightedText.addAttribute(NSBackgroundColorAttributeName, value: UIColor.yellow, range: match.range)
}
}
cell.nameLbl.attributedText = highlightedText
}else{//address search mode
cell.addressLbl.text = self.customersSearchResults[indexPath.row]
cell.address = self.customersSearchResults[indexPath.row]
if let i = self.addresses.index(of: cell.addressLbl.text!) {
cell.nameLbl.text = self.names[i]
cell.name = self.names[i]
cell.id = self.ids[i]
} else {
cell.nameLbl.text = ""
cell.name = ""
cell.id = ""
}
//text highlighting
let baseString:NSString = cell.address as NSString
let highlightedText = NSMutableAttributedString(string: cell.address)
var error: NSError?
let regex: NSRegularExpression?
do {
regex = try NSRegularExpression(pattern: searchString, options: .caseInsensitive)
} catch let error1 as NSError {
error = error1
regex = nil
}
if let regexError = error {
print("Oh no! \(regexError)")
} else {
for match in (regex?.matches(in: baseString as String, options: NSRegularExpression.MatchingOptions(), range: NSRange(location: 0, length: baseString.length)))! as [NSTextCheckingResult] {
highlightedText.addAttribute(NSBackgroundColorAttributeName, value: UIColor.yellow, range: match.range)
}
}
cell.addressLbl.attributedText = highlightedText
}
} else {
//print("make cell")
cell.id = self.ids[sections[indexPath.section].index + indexPath.row]
cell.name = self.names[sections[indexPath.section].index + indexPath.row]
cell.address = self.addresses[sections[indexPath.section].index + indexPath.row]
cell.nameLbl.text = self.names[sections[indexPath.section].index + indexPath.row]
cell.addressLbl.text = self.addresses[sections[indexPath.section].index + indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRow(at: indexPath!) as! CustomerTableViewCell
let customerViewController = CustomerViewController(_customerID: currentCell.id)
navigationController?.pushViewController(customerViewController, animated: false )
tableView.deselectRow(at: indexPath!, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

My loop to grab number of results within an array in swift is not working

Greetings dear fans and programmers of the Swift language. I have tried to formulate an algorithm number of times an element shows up within an array with a for loop, but it doesn't seem to be working.
My code is as follows:
else if indexPath.section == 1 {
cell = tableView.dequeueReusableCellWithIdentifier("GreenCell", forIndexPath: indexPath)
for item in theModel.userAnswer {
numOfCoincidences[item] = (numOfCoincidences[item] ?? 0) + 1
}
for (numLangs, value) in numOfCoincidences {
txtSummary = "#languages spoken: \(numLangs)"
txtSummary2 = "# of People: \(value)"
}
cell.textLabel?.text = txtSummary
cell.detailTextLabel?.text = txtSummary2
I am trying to display this information in a table Cell but it is not working. I thought that my algorithm was spot on. Any suggestions?
I'm getting the following output: I have a navigation controller in effect where I input data on one screen and it outputs the data on a table. It's a bit of a survey where I prompt the user to enter their name and the number of languages spoken. I'm using an MVC programming methodology.
so in the model, here is the code:
import Foundation
class Model {
var userName = [String]()
var userAnswer = [String]()
var userInfo = [String]()
var name:String
var answer:String
init(){
self.name = ""
self.answer = ""
}
func addUserInfo(name:String, answer:String) -> Void {
userName.append(name)
userAnswer.append(answer)
}
}
In the input screen, I have 2 text boxes that prompt for username and number of languages spoken. So on the output screen, if 2 people speak 4 languages, the output should reflect that, but it's not. If 1 person speaks 3 languages, it should display that and so on. The output is coming out completely incorrectly. Here is the for the data entry code:
import UIKit
class ViewController: UIViewController {
var model = Model()
#IBOutlet var txtName: UITextField!
#IBOutlet var lblStatus: UILabel!
#IBOutlet var txtAnswer: UITextField!
#IBAction func btnAnswer(sender: UIButton) {
model.answer = txtAnswer.text!
model.name = txtName.text!
if ((txtName.text)! == "" || (txtAnswer.text)! == "") {
lblStatus.text = "Name and answer are both required"
}else if model.userName.contains(model.name) {
lblStatus.text = "Answer already recorded for \(model.name)"
} else {
model.addUserInfo(model.name, answer: model.answer)
lblStatus.text = "Ok, \(model.name) answered \(model.answer)"
}
txtAnswer.text = ""
txtName.text = ""
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toReesultsController" {
let vc = segue.destinationViewController as! TableViewController
vc.theModel = self.model
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
The code for the table to display the data inputted from the data entry screen is presented in a table as shown below:
import UIKit
class TableViewController: UITableViewController {
var theModel = Model()
var numOfCoincidences:[String:Int] = [:]
var txtSummary:String = ""
var txtSummary2:String = ""
var greatest:Int = 0
/* override func viewDidLoad() {
for index in 0..<theModel.userName.count {
}
}*/
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 3
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return theModel.userAnswer.count
} else if section == 1 {
return theModel.userAnswer.count
} else if section == 2{
return theModel.userAnswer.count
} else {
return 0
}
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Answer Log"
} else if section == 1 {
return "Summary"
} else if section == 2 {
return "Top Answers"
} else {
return nil
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell! = nil
if indexPath.section == 0 {
cell = tableView.dequeueReusableCellWithIdentifier("BlueCell", forIndexPath: indexPath)
cell.textLabel?.text = "\(theModel.userName[indexPath.row]):\(theModel.userAnswer[indexPath.row])"
} else if indexPath.section == 1 {
cell = tableView.dequeueReusableCellWithIdentifier("GreenCell", forIndexPath: indexPath)
for item in theModel.userAnswer {
numOfCoincidences[item] = (numOfCoincidences[item] ?? 0) + 1
}
for (numLangs, value) in numOfCoincidences {
txtSummary = "#languages spoken: \(numLangs)"
txtSummary2 = "# of People: \(value)"
}
cell.textLabel?.text = txtSummary
cell.detailTextLabel?.text = txtSummary2
} else if indexPath.section == 2 {
cell = tableView.dequeueReusableCellWithIdentifier("OrangeCell", forIndexPath: indexPath)
greatest = 0
for index in 0..<theModel.userAnswer.count {
if Int(theModel.userAnswer[index])! > greatest {
greatest = Int(theModel.userAnswer[index])!
}
}
cell.textLabel?.text = "Answer with most votes is \(greatest) languages spoken."
}
return cell
}
}
Based on what you have shared, and making some assumptions on your input this seems to do what you want.
If each item is a dictionary with the keys shown below. Then you would walk through the array and get the number of languages spoken by the person. I chose to define that as a number(Int) since that is what it really is.
This is almost the same as your original code so I have to conclude that your idea was correct, but your implementation was missing something.
Assuming this is your input:
let a = [["name":"carla", "langs" : 3], ["name":"scott", "langs" : 3], ["name":"brad", "langs" : 1], ["name":"cynthia", "langs":2]]
var numOfCoincidences = [Int: Int]()
for item in a {
let numLangs = item["langs"] as! Int
numOfCoincidences[numLangs] = (numOfCoincidences[numLangs] ?? 0) + 1
}
var txtSummary = ""
var txtSummary2 = ""
for (numLangs, value) in numOfCoincidences {
// You should also note that you are overwriting values here.
txtSummary = "#languages spoken: \(numLangs)"
txtSummary2 = "# of People: \(value)"
print(txtSummary)
print(txtSummary2)
}
numOfCoincidences
Output:
// key := number of languages spoken
// value := number of people who speak that many languages
[2: 1, 3: 2, 1: 1]
You do have a problem in your cellForRowAtIndexPath.
After you loop through all the data, you do this:
cell.textLabel?.text = txtSummary
cell.detailTextLabel?.text = txtSummary2
Therefore, every cell is going to have the last values from the loop.

first cell in tableview array index out of range error

I have a tableview in which the first cell is different from all the others (I have two custom cells in the table dequeued With different Identifiers). The codes are as below:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("profileCell") as! SbProfileViewCell
//cell.textArea.text = bio[indexPath.row]
//pictureFile[indexPath.row].getDataInBackgroundWithBlock { (fileData, error) -> Void in
if let pic = UIImage(data: fileData!) {
cell.imageView.image = pic
}
}
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier("postCell") as! SbPostsTableViewCell
cell.date.text = dateFormatter.stringFromDate(dates[indexPath.row - 1])
pictureFileOne[indexPath.row - 1].getDataInBackgroundWithBlock { (fileOneData, error) -> Void in
if let downloadedImageOne = UIImage(data: fileOneData!) {
cell.imageArea.image = downloadedImageOne
}
}
cell.textArea.text = text[indexPath.row - 1]
return cell
}
}
i'm getting the array out of index error for the commented lines and i'm also uncertain about whether using [indexPath.row - 1] would yield the effect I want (I want the second cell of the tableview to display the first object in the array), hope i explained everything ok, please help! thanks
EDIT
var dates = [NSDate]()
var autoBio = [String]()
func getPosts() {
let getPostsQuery = PFQuery(className: "allPosts")
getPostsQuery.whereKey("userId", equalTo: userNumber)
getPostsQuery.limit = 15
getPostsQuery.orderByDescending("createdAt")
getPostsQuery.findObjectsInBackgroundWithBlock { (posts, error) -> Void in
if let posts = posts {
self.dates.removeAll(keepCapacity: true)
for post in posts {
self.dates.append(post.createdAt as NSDate!)
}
}
print(self.dates.count)
}
}
func getBio() {
let bioQuery = PFQuery(className: "bios")
bioQuery.whereKey("userId", equalTo: userNumber)
bioQuery.limit = 1
bioQuery.findObjectsInBackgroundWithBlock { (bios, error) -> Void in
if let bios = bios {
self.bio.removeAll(keepCapacity: false)
for bio in bios {
self.bio.append(bio["about"] as! String)
}
}
}
print(self.bio.count)
}
i put getPosts() and getBio() in viewDidLoad. And I tried printing the bio and dates count as the end of these func and they do return a number > 0 so the arrays should be filled. Any idea what's wrong?
You should try using the first cell as Section Header cell...
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCellWithIdentifier("profileCell") as! SbPostsTableViewCell
return cell
}

Array index out of range. Swift. Table View

I got a fatal error: Array index out of range. The error comes from let profilePicture = tempProductPictureArray[count] when tempProductPictureArray[count] is nil. I am wondering do I set something wrong on imageArray?
var imageArray = [[PFFile]]()
........
.......
.......
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell:MarketTableViewCell = tableView.dequeueReusableCellWithIdentifier("MarketCell") as! MarketTableViewCell
let tempProductPictureArray = imageArray[indexPath.row]
// tempProductPictureArray.count is something like [nil / PFFile(image), nil / PFFile(image) .....]
if tempProductPictureArray.count != 0 {
var hasImage = false
var count = 0
while hasImage == false {
let profilePicture = tempProductPictureArray[count]
if profilePicture != nil {
profilePicture!.getDataInBackgroundWithBlock { data, error in
if error != nil {
self.appDelegate.displayMyAlertMessage((error?.localizedDescription)!, userVI: self)
} else {
cell.productImage.image = UIImage(data: data!)
}
}
hasImage = true
} else if count == tempProductPictureArray.count {
cell.productImage.image = UIImage(named: "Profile Picture")
} else {
count += 1
}
}
} else {
cell.productImage.image = UIImage(named: "Profile Picture")
}
data source
func refreshResults(){
...
...
...
sameObjectInQuery.findObjectsInBackgroundWithBlock{ (sameObjects: [PFObject]?, error: NSError?) -> Void in
for var i = 0; i <= tempUniqueTitle.count - 1; i++ {
tempProductImage = [PFFile]()
for sameobject in sameObjects! {
if sameobject.objectForKey("detailsImage") != nil {
tempProductImage.append(sameobject.objectForKey("detailsImage") as? PFFile)
} else {
tempProductImage.append(nil)
}
imageArray.append(tempProductImage)
}
....
Trace through your code, assuming that tempProductPictureArray contains one item. tempProductPictureArray.count will be 1 and that item is at offset 0. Presumably, it's a nil optional and you want to skip over it.
Since count is not equal to tempProductPictureArray.count, you add one to count and try to fetch something from the array at that location (offset 1). However, we've said the only element in the array is at offset 0, so...crash.
You need to make sure that count is always less than tempProductPictureArray.count.

Resources