How does a piece of closure work within a UITableViewCell definition? - uitableview

The following code snippet was retrieved from an on-line tutorial:
typealias TableCellConfigurationBlock = (cell: ScheduleTableViewCell, indexPath: NSIndexPath, session: Session) -> ()
The 'typealias' appears to be a closure; or a tuple yielding a void()?
But I don't know how it works within the following function:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ScheduleTableViewCell") as! ScheduleTableViewCell
let session = sessionForIndexPath(indexPath)
if let configureBlock = tableCellConfigurationBlock {
configureBlock(cell: cell, indexPath: indexPath, session: session)
}
return cell
}
Explanation?
References?

typealias ClosureType = (i: Int, s: String, d: Double)->String
let c1: ClosureType = {
// has three input parameters
// i:Int, s: String, d: Double
// and returns String
i, s, d in
return s + " integer: \(i) and double: \(d)"
}
let c2: ClosureType = {
$1 + " integer: \($0) and double: \($2)"
}
print(c1(i: 1,s: "You pass in",d: 3.14))
print(c2(i: 1,s: "You pass in",d: 3.14))
/*
You pass in integer: 1 and double: 3.14
You pass in integer: 1 and double: 3.14
*/

Related

'subscript(_:)' is unavailable: cannot subscript String with an Int, use a String.Index instead [duplicate]

This question already has answers here:
Get nth character of a string in Swift
(47 answers)
Closed 1 year ago.
I'm trying to pass the data and got this error: 'subscript(_:)' is unavailable: cannot subscript String with an Int, use a String.Index instead. Here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: CustomLoopsCell = beatTableView.dequeueReusableCell(withIdentifier: "firstLoopCell", for: indexPath) as! CustomLoopsCell
let beatLoop = overtunePacks[indexPath.row]
// On the lines which is down I'm getting this error
let beatDesc = loopsDesc[indexPath.row]
let beatProd = loopsProd[indexPath.row]
let beatAudio = loopsAudio[indexPath.row]
let beatInst = loopsInst[indexPath.row]
cell.loopNameLabel.text = beatDesc
cell.producerLabel.text = beatProd
cell.instrumentLabel.text = beatInst
Here is the code from another view controller, from where I'm passing the data:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(identifier: "BeatViewID") as? BeatPackViewController
vc?.beatInfoCollectName = beatInfoCollect[indexPath.row].name
vc?.beatProducerName = beatInfoCollect[indexPath.row].producer
vc?.imageBeat = beatInfoCollect[indexPath.row].imagename
vc?.loopsAudio = beatAudioCollect[indexPath.row].loopPath
vc?.loopsDesc = beatAudioCollect[indexPath.row].name
vc?.loopsInst = beatAudioCollect[indexPath.row].instrument
vc?.loopsProd = beatAudioCollect[indexPath.row].producer
self.navigationController?.pushViewController(vc!, animated: true)
}
Here is my variables in second view controller, where I'm passing data:
var loopsAudio = ""
var loopsDesc = ""
var loopsInst = ""
var loopsProd = ""
Here is what I want to display:
struct BeatLoops: Decodable {
let name: String
let instrument: String
let loopPath: String
let producer: String
var songURL : URL {
let components = loopPath.components(separatedBy: ".")
guard components.count == 2,
let url = Bundle.main.url(forResource: components[0], withExtension: components[1]) else { fatalError("Audio file is missing") }
return url
}
}
I'm parsing this data and have instance: var beatAudioCollect = [BeatLoops]()
Seems that you are trying to get Substring from a loopsDesc String here
let beatDesc = loopsDesc[indexPath.row]
...
let beatInst = loopsInst[indexPath.row]
The point is that you can't just get String's characters using subscription index.
If you want to get a character from a String, you should get it's Substring with a specified range.
Like that:
let index = loopsDesc.index(loopsDesc.startIndex, offsetBy: 1)
let mySubstring = loopsDesc[..<index]
Or another way:
1 - add this extension to a String:
extension StringProtocol {
subscript(offset: Int) -> Character {
self[index(startIndex, offsetBy: offset)]
}
}
2 - use it like that
let input = "My String Here"
let char = input[3]
//or
let char = String(input[3])
Please see another stackoverflow question related to that.
But it's unsafe method and I would suggest to use another way to get params (for example store them in Array, not in String)
Your design is wrong.
Extracting single characters from a string in a table view to be displayed in different rows makes no sense.
Actually you are passing two objects to the second view controller, an infoCollect object and an audioCollect object.
First of all rather than extracting all properties just pass the entire objects.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(identifier: "BeatViewID") as! BeatPackViewController
vc.infoCollect = beatInfoCollect[indexPath.row]
vc.audioCollect = beatAudioCollect[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
By the way using multiple arrays as data source is strongly discouraged!.
In the second controller replace the String properties with
var infoCollect : Loop!
var audioCollect : BeatLoops!
If the structs are declared inside in first view controller you have to write
var infoCollect : BPLibraryViewController.Loop!
var audioCollect : BPLibraryViewController.BeatLoops!
Then remove the table view and add UILabels instead. In viewDidLoad assign the values for example
imageBeatLabel.text = infoCollect.name
loopsAudioLabel.text = audioCollect.loopPath

Tableview numberOfRowsInSection returns duplicate .count number?

This might be a really stupid question, but i can't seem to find the answer anywhere.
Below is a small part code of a tableview and an empty dictionary.
// MARK: - Object2
struct Object2: Codable {
let id: Int
let url: String
let displayname: String
let media: [Media2]
}
// MARK: - Media2
struct Media2: Codable {
let id, prodno: Int
let webpath: String
let slot: Int
let type, extdata: String
let ctime, xsize, ysize, mediasetid: Int
let alt, title: String
let generation: Int
let created, changed: String
}
var sokresultat2 = [Object2]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(sokresultat2.count)
return sokresultat2.count
}
The "numberOfRowsInSection" func is checking the .count of the variable "sokresultat2", and it always prints out double zero, just like this:
0
0
Isn't it supposed to print out only 1 zero?
I've played around with the code, but it always prints out a duplicate of the amount, why is this? I'm simply curious to how this works.
Thanks!

How do I assign a tuple of String to a cell's text?

I am making a grouped table and it is showing this error:
cannot assign value of type '(String, String)' to type String.
I am using Xcode 8.3.3.
import UIKit
class ViewController: UIViewController, UITableViewDataSource {
let people = [
("Purnima Yadav", "New Delhi"), ("Gitesh Yadav", "Toronto"), ("Vidhi Yadav", "Chicago")]
let videos = [
("Android app Development", "74 videos"), ("C++ for Beginners", "87 videos"), ("Java", "142 videos"), ("Python Programming", "63 videos"), ("Web design", "68 videos")
]
//return int , how many rows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if section == 0 {
return people.count
} else {
return videos.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if indexPath.section == 0 {
var (personName, personLocation) = people[indexPath.row]
cell.textLabel?.text = personName
} else {
var (videoTitle, videoDesc) = videos[indexPath.row]
cell.textLabel?.text = videoTitle
}
return cell
}
}
I am getting error at:
cell.textLabel?.text = personName
You can resolve your issue by giving a specific type to your videos object as [(String, String)] which is an Array of Tuples and your declaration will look like:
let videos: [(String, String)] = [("Android app Development", ""), ("C++ for Beginners", "87 videos"), ("Java", "142 videos"), ("Python Programming", "63 videos"), ("Web design", "68 videos")]
Here you also need to update your ("Android app Development") with ("Android app Development", "") to satisfy your type casting.
The tuple ("Android app Development") does not have a second set. It should be ("Android app Development", "").
Also you casted your array of tuples to an array of [Any]. Just remove it.

Swift generic closure

I'm building a library for static table views and it works fine, but I encountered a problem with generic closures.
It looks like this so far:
orderForm = Form(tableView: orderTable) { f in
f.section { s in
s.footer("Při platbě nejsou účtovány žádné další poplatky.")
s.cell("Selection")
.configure { (cell, path) in
let c = cell as! ProfileSelectionCell
c.titleLabel?.text = "Způsob platby"
c.detailLabel?.text = self.paymentType.first
}
s.cell("Selection")
.configure { (cell, path) in
let c = cell as! ProfileSelectionCell
c.titleLabel?.text = "Balíček"
c.detailLabel?.text = "20 kr. za 1000 Kc"
}.selected { path in
}
}
}
I wanna have the "cell" variable already cast to appropriate type, in this case ProfileSelectionCell.
Here is the source for the cell class:
class Cell {
internal let id: String
internal var configure: ((cell: UITableViewCell, path: NSIndexPath) -> Void)?
internal var selected: ((path: NSIndexPath) -> Void)?
init(id: String) {
self.id = id
}
func configure(config: ((cell: UITableViewCell, path: NSIndexPath) -> Void)?) -> Self {
self.configure = config
return self
}
func selected(selected: (path: NSIndexPath) -> Void) -> Self {
self.selected = selected
return self
}}
My problem is that if I make the configure method generic, it is not possible to store the config closure to the cell variable and if I make the whole cell generic, I can't save the cell to an array in Section class and so on..
Is this solvable in any way?
You can make the Cell class generic, e.g.
class Cell<T : UITableViewCell> {
}
and then use T instead of every UITableViewCell.
Unfortunately you would have to have the same in both Section and Form classes, too. That would work for tables with one type of cells but it won't probably work for tables with multiple cell types. In that case you will always need casting somewhere.

Can't retain array data outside of method in SWIFT

I am storing the category name from a JSON in an Array using alamofire .
The array has values only when it is called from this Method CategoryNameFunc.
If i call the the array from the tableview or any other method it always returns 0
CODE
var CategoryNameArray : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
Network()
tester()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return CategoryNameArray.count // This returns 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : UITableViewCell = self.TableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
println(self.CategoryNameArray[indexPath.row])
cell.textLabel?.text = "Hello"
return cell
}
func Network(){
Alamofire.request(.GET, "http://www.wive.com/index.php/capp/category_list")
.responseJSON { (_, _, data, _) in
let json = JSON(data!)
let count = json.count
self.CategoryNameFunc(json, Count: count) }
}
func CategoryNameFunc(Json: JSON, Count: Int)
{
for index in 0...Count-1 {
let name = Json[index]["CATEGORY_NAME"].string
CategoryNameArray.append(name!)
}
// This returns 23 (The correct value)
println(self.CategoryNameArray.count)
}
When you called Network() function it creates a new thread (Alamofire start an asynchronous request) and your tester() function is not waiting for your Network() function to finish before you count your CategoryNameArray().But your CategoryNameFunc() function waits for network operation to finish.
I am not sure (didn't use Almofire) but it think, this happens because the method Network, more precisly the Almofire request is fired asynchronously.
So, the methods Network() and tester() are running simultaneously, but because Network() needs to fetch data first, tester() is faster and is executed first.
The proper way to execute tester() and Network() one after another would be:
func CategoryNameFunc(Json: JSON, Count: Int)
{
for index in 0...Count-1 {
let name = Json[index]["CATEGORY_NAME"].string
CategoryNameArray.append(name!)
}
// run tester, AFTER you have the data.
tester()
}

Resources