Map Object into 2D Array Swift for Tableview Sections

Filtering a 2D array with objects in Swift

Use this updated getMusic method,

func getMusic(instrument: MusicData.Instrument) -> [Section] {
if instrument == .Tenor {
var filtered = sections.filter {$0.items.filter {$0.tenor == true}.count > 0}
filtered = filtered.map({ (var section: Section) -> Section in
section.items = section.items.filter {$0.tenor == true}
return section
})
return filtered
} else {
let filtered = sections.filter {$0.items.filter {$0.snare == true}.count > 0}
filtered = filtered.map({ (var section: Section) -> Section in
section.items = section.items.filter {$0.snare == true}
return section
})
return filtered
}
}

how to change style of tableview cell according to data which is in two array detail is below

How are you mapping items from both arrays into the table view? In other words how to you map an item in either array at index i, to a UITableView IndexPath?

Specifying wether a cell should be selectable or not is done by overriding UITableViewDelegate's shouldHighlightRowAtIndexPath

Assuming section count is two and items at section index one are elements of the not recommended array:

Swift 3

override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {

return indexPath.section !=1
}

Search using TextFiled in Multi Sectioned TableView - Swift

You can do something like this in shouldChangeCharactersIn method

let areaArr = [["Vadvalli", "Mullai Nagar", "P.N.Pudhur"], ["Lawly Road", "Kowly Brown", "D.B Road"], ["Raja Street", "Gandhipark", "Five corner road", "Main Town Hall"]]

let areaTitles = ["Vadavalli", "R.S.Puram", "Town Hall"]
let searchText = "road"
var areaArrResult = areaArr.map { $0.filter({ $0.lowercased().contains(searchText) }) }
let areaTitlesResult = areaTitles.filter { !areaArrResult[areaTitles.index(of: $0)!].isEmpty }
areaArrResult = areaArrResult.filter { !$0.isEmpty }

print(areaArrResult)//[["Lawly Road", "D.B Road"], ["Five corner road"]]
print(areaTitlesResult)//["R.S.Puram", "Town Hall"]

OR

     var allDetails:[(String,[String])] = [] 
var searchResult:[(String,[String])] = []

allDetails = [("Vadavalli",["Vadvalli", "Mullai Nagar", "P.N.Pudhur"]),
("R.S.Puram",["Lawly Road", "Kowly Brown", "D.B Road"]),
("Town Hall",["Raja Street", "Gandhipark", "Five corner road", "Main Town Hall"])]
searchAreaArr = areaArr.map({ ($0.0,$0.1.filter({ $0.lowercased().contains(substring) })) }).filter { !$0.1.isEmpty}
print(searchResult)//[("R.S.Puram", ["Lawly Road", "D.B Road"]), ("Town Hall", ["Five corner road"])]

Then
In numberOfSections return searchResult.count

In numberOfRowsInScetion return searchResult[section].1.count

In titleForHeaderInSection method you can use

return searchResult[section].0

In cellForRowAt method

cell.textLabel?.text = "\(searchResult[indexPath.section].1[indexPath.row])"

Alamofire and ObjectMapper to TableView in Swift

First of all – as always – since Swift 4 libraries like ObjectMapper and SwiftyJSON have become obsolete in favor of built-in, efficient Codable protocol. Avoid tutorials which suggest those outdated libraries.

Regarding your mistake you are ignoring the root object, the object with keys success, user_id and devices.

This is a solution using Decodable

  • Drop SwiftyJSON and ObjectMapper

  • Replace the structs with

      struct Root : Decodable {
    let success : Bool
    let userId : String
    let devices : [Device]
    }

    struct Device : Decodable {
    let id : String
    let source : String
    let sourceText : String
    let logoUrl : URL
    let isConnected : String // this value is String, not Bool
    }
  • Replace loadData with

      func loadData(){
    let base = Config().base
    let endPoint = Config.EndPoint()
    let patientId = User().id

    let url = base + endPoint.showDevices
    let header: HTTPHeaders = [
    "Content-Type": "application/json",
    ]
    let params: Parameters = [
    "patient_id" : patientId
    ]

    let code = Code(id: id)
    AF.request(url, parameters: params, headers:header).validate()
    .responseData { (response) in
    switch response.result {
    case .success(let data):
    do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let result = try decoder.decode(Root.self, from: data)
    self.devices = result.devices
    self.tableView.reloadData()
    } catch { print(error) }

    case .failure(let error):
    print(error)
    }
    }
    }

    The Alamofire response returns raw data and the keyDecodingStrategy converts the snake_cased keys to camelCased struct members and logoUrl is decoded as URL

  • Finally rename the data source array to

      var devices = [Device]()

    and remove all question marks after devices

Dynamically populating multiple TableViews using UIScrollView and UIPageControl (Swift 5)

This was quite an interesting question to me since I do like to recreate UIs of popular apps. I think there are a few ways you can achieve this, I will show you one way.

I think the main challenges to solve:

  • The best UI components to use to set this up
  • Getting the right model mapped from the 2D array to the right table view

For the UI components, I have chosen to go this way:

  • Paging UIScrollView
    • UIStackView contained in the Paging UIScrollView
      • UITableView contained in the UIStackView

Here is how I went about doing this and added comments to explain

Initial set up

class PagingTableView: UIViewController
{
// Same model as yours
var model: [[String]] = [
["TableView 1: row 1"],

["TableView 2: row 1",
"TableView 2: row 2"],

["TableView 3: row 1",
"TableView 3: row 2",
"TableView 3: row 3"],

["TableView 4: row 1",
"TableView 4: row 2",
"TableView 4: row 3",
"TableView 4: row 4"],

["TableView 5: row 1",
"TableView 5: row 2",
"TableView 5: row 3",
"TableView 5: row 4",
"TableView 5: row 5"]
]

let scrollView: UIScrollView =
{
let scrollView = UIScrollView()
scrollView.isPagingEnabled = true
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()

let stackView: UIStackView =
{
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()

let pageControl = UIPageControl()

let padding: CGFloat = 20
let pageWidth = UIScreen.main.bounds.width

let tableViewCellIdentifier = "cell"

// Use this factor to unique identify your table
let tableViewUniqueIdFactor = 1000

// https://stackoverflow.com/a/21130486/1619193
// You can ignore this function, created for convenience
private func randomColor() -> UIColor
{
let red = CGFloat(arc4random_uniform(256)) / 255.0
let blue = CGFloat(arc4random_uniform(256)) / 255.0
let green = CGFloat(arc4random_uniform(256)) / 255.0

return UIColor(red: red, green: green, blue: blue, alpha: 1.0)
}

override func viewDidLoad()
{
super.viewDidLoad()

title = "Paging TableView"
view.backgroundColor = .white

// Configure everything, functions come later
configureScrollViewLayout()
configureStackViewLayout()
configurePageControl()
addTableViewsToStackView()
}

Configure your scroll view layout

private func configureScrollViewLayout()
{
scrollView.delegate = self

view.addSubview(scrollView)

// Auto layout
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
.isActive = true

scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,
constant: padding).isActive = true

scrollView.widthAnchor.constraint(equalToConstant: pageWidth).isActive = true

scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: -padding * 3).isActive = true
}

Configure your stack view layout

private func configureStackViewLayout()
{
scrollView.addSubview(stackView)

// Auto layout

stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor)
.isActive = true

stackView.topAnchor.constraint(equalTo: scrollView.topAnchor)
.isActive = true

stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor)
.isActive = true

stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
.isActive = true

stackView.heightAnchor.constraint(equalTo: scrollView.heightAnchor,
multiplier: 1).isActive = true
}

Configure your page control layout

private func configurePageControl()
{
pageControl.numberOfPages = model.count
pageControl.currentPage = 0
pageControl.tintColor = randomColor()
pageControl.pageIndicatorTintColor = randomColor()
pageControl.currentPageIndicatorTintColor = randomColor()

view.addSubview(pageControl)

// Auto layout
pageControl.translatesAutoresizingMaskIntoConstraints = false

pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor)
.isActive = true

pageControl.topAnchor.constraint(equalTo: scrollView.bottomAnchor)
.isActive = true

pageControl.widthAnchor.constraint(equalToConstant: 200)
.isActive = true

pageControl.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
.isActive = true
}

Configure and add table views to the stack view

    private func addTableViewsToStackView()
{
for modelIndex in 0 ..< model.count
{
let tableView = UITableView()

tableView.translatesAutoresizingMaskIntoConstraints = false

// Uniquely identify each table which will come in handy
// when figuring out which model should be loaded for a specific
// table view
tableView.tag = tableViewUniqueIdFactor + modelIndex

// Register a default UITableView Cell
tableView.register(UITableViewCell.self,
forCellReuseIdentifier: tableViewCellIdentifier)

tableView.dataSource = self

tableView.backgroundColor = randomColor()

// remove additional rows
tableView.tableFooterView = UIView()

stackView.addArrangedSubview(tableView)

tableView.widthAnchor.constraint(equalToConstant: pageWidth)
.isActive = true

// height is calculated automatically based on the height
// of the stack view
}
}

// end of the class PagingTableView
}

At this stage, if you run this, you were to run the app, you will see the set up is like the weather app without the data in it and the paging control is not wired yet but we have the same number of pages as models (5):

Paging UIScrollView Paging UIStackView iOS Weather App Swift

Now what's left is to wire up the page control and the tableview to the model

Update the page view with the scrollview delegate

extension PagingTableView: UIScrollViewDelegate
{
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
{
// Make sure you don't do anything with table view scrolls
// This is only a worry if the view controller is the table view's
// delegate also
if !(scrollView is UITableView)
{
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
}
}

Implement the table view data source to map the table to the model

extension PagingTableView: UITableViewDataSource
{
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int
{
// Retrieve the correct model using the unique identifier created earlier
let modelIndex = tableView.tag - tableViewUniqueIdFactor

// Get the correct array needed from model
let modelForeCurrentTable = model[modelIndex]

return modelForeCurrentTable.count
}

func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellIdentifier)!

// Retrieve the correct model using the unique identifier created earlier
let modelIndex = tableView.tag - tableViewUniqueIdFactor

// Get the correct array needed from model
let modelForeCurrentTable = model[modelIndex]

cell.textLabel?.text = modelForeCurrentTable[indexPath.row]

cell.backgroundColor = .clear

return cell
}
}

Now everything is set up and you get something similar to the weather app with the right data showing in each table view and the page control wired up as well:

Paging UITableView UIScrollView Paging UIStackView iOS Weather App Swift

Final comments

You can skip the layout parts if you create your UI using frames or in storyboard. I tried to give you something from start to finish that you would run in isolation and you could play around with it by tweaking things.

UITableView with Multiple Sections in Alphabetical Order using Realm and Swift, with multiple labels connected with same data

There are a number of issues with the code in the question and instead of trying to dissect them, let me provide an example that may help.

Suppose we have a bunch of fruits stored in Realm. We want to display those in alphabetical order in a tableview using sections

A
Apple
B
Banana

etc

This first piece of code is a structure to hold a section title and then an array of the fruits that go in that section. Also, a class fruitNameArray which will act as our tableView DataSource that holds all of the FruitStructs.

struct FruitStruct {
var sectionTitle = ""
var fruitNameArray = [String]()
}

var fruitDataSource = [FruitStruct]()

I am using an array to hold the initial data but in your case it could be a Realm results object populated from a Realm. This is called from viewDidLoad to organize our data into the dataSource array

func setupDataSourceData() {
let fruitArray = ["Apple", "Pear", "Banana", "Bing Cherry", "Grape", "Orange", "Plum", "Watermelon", "Cantelope"]

let allFirstChars = fruitArray.map { String($0.prefix(1)) } //get all first chars for section titles
let sectionTitles = Array(Set(allFirstChars)).sorted() //eliminate dups and sort

//iterate over the unique section titles and get the fruits that are in that section
// sort and then craft structs to hold the title and the associated fruits
sectionTitles.forEach { firstChar in
let results = fruitArray.filter { $0.prefix(1) == firstChar }
let sortedFruits = results.sorted()
let fruit = FruitStruct(sectionTitle: firstChar, fruitNameArray: sortedFruits)
fruitDataSource.append(fruit)
}

for fruitData in fruitDataSource {
print(fruitData.sectionTitle)
let fruits = fruitData.fruitNameArray
for fruitName in fruits {
print(" \(fruitName)")
}
}
}

Then, there are 4 functions a tableView needs to show the sections and then the rows in each section. You appear to have these 4 but an additional one sectionIndexTitles which may not be needed.

//
//handle sections
//
func numberOfSections(in tableView: UITableView) -> Int {
return self.fruitDataSource.count
}

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let title = self.fruitDataSource[section].sectionTitle
return title
}

//
//handleTableView rows
//
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let rowsInSection = self.fruitDataSource[section].fruitNameArray.count
return rowsInSection
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellReuseIdentifier", for: indexPath)
let text = self.fruitDataSource[indexPath.section].fruitNameArray[indexPath.row]
cell.textLabel?.text = text
return cell
}

Note that I am not performing any filtering and any other stuff as the tableView is being populated. That section of code needs to be fast and lean to the UI is as responsive as possible.

I am just getting a string

let text = self.fruitDataSource[indexPath.section].fruitNameArray[indexPath.row]

but in your case, you may want to return an exersise object where you could then get the exerciseName and bodypartName to then assign those in the custom cellView

Best Practice Note: class properties should be lower cased - exerciseName, not uppercase, ExerciseName. Class and Struct names are typically capitalized.



Related Topics



Leave a reply



Submit