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
andObjectMapper
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
withfunc 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 andlogoUrl
is decoded asURL
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
- UIStackView contained in the Paging UIScrollView
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):
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:
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
How to Chain Filters in Metal for iOS
How to Add Frameworks into the Swift Project
Get Apple Watch Heartratevariabilitysdnn Realtime
How to Create a Scnnode from a .Usdz
Receipt Validation in iOS Returns Incorrect Info During Sandbox Testing
Is Localstorage on iPad Safari Guaranteed to Be Persistent
Spritekit Not Respecting Zposition
How to Show Viewcontroller from a Non-Viewcontroller Helper Class
Ad-Hoc Distributed Application Failed to Launch in Time
Xcode Debug View Hierarchy: Unable to Capture View Hierarchy
How to Use Indexesofobjectspassingtest: in Swift
Bluetooth Didconnect or Didfailtoconnect Not Called Xcode 9.2 with iOS 11.1.2
Why HTML5 Video Doesn't Play in iOS 8 Webapp(Webview)
Swift 3 Weird Crashes (Type Inference)
How to Display Collectionviewcell in Center in Swift 4