I want to keep calling following function for 'n' number of times, I'll write that code later. Now am facing a problem.
func keepHighlighting(myLbl : UILabel)
{
myLbl.text = "hi"
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for : indexPath) as! TableViewCell
let mySelector = #selector(self.keepHighlighting(duaLbl : cell.tempLbl))
let timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: mySelector, userInfo: nil, repeats: true);
timer.fire()
return cell
}
Error:
Argument of '#selector' does not refer to an '#objc' method, property, or initializer
You've misunderstood selectors. self.keepHighlighting(duaLbl : cell.tempLbl) is a function call. A selector is the name of a method (it's technically a message, but those generally resolve into a method call).
You cannot pass parameters via a selector. The method you're calling from a timer should have the signature void someMethod(_ timer: Timer). For that, the selector would be #selector(someMethod). (That compiles down to someMethod:, but you generally won't have to deal with that fact.)
Depending on what you're trying to do here, there are several approaches, but it's not quite clear what you're trying to do. My recommendation, though, is to put this logic into the cell itself rather than trying to manage it from the controller. It is generally much easier to have each cell manage its own behaviors than try to have the controller keep track of every cell and tweak it.
You have to annotate your method with #objc and use the Timers userInfoto parse arguments:
#objc func keepHighlighting(timer:Timer)
{
let myLbl = timer.userInfo as! UILabel
myLbl.text = "hi"
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for : indexPath) as! UITableViewCell
let mySelector = #selector(self.keepHighlighting)
let timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: mySelector, userInfo: cell.tempLbl, repeats: true);
timer.fire()
return cell
}
Related
I have a tableView with a timer in each cell that runs whentableView:didSelectRowAtIndexPath is called. I'm trying to use a label (cellName) in a custom cell (CustomCell) to show the timer incrementing. I'm using #selector in .scheduledTimer to call a seperate function named getTimer() to increment the value. Code as it is below.
I've abbreviated this code to show only the relevant info
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var time = 0
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell: CustomCell = tableView.cellForRow(at: indexPath)! as!
CustomCell
var timer = Timer()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.getTimer), userInfo:nil, repeats: true)
cell.cellName.text = String(self.getTimer())
}
func getTimer() -> String {
time += 1
return String(time)
}
}
Two things:
My time variable is defined on the class level so that getTimer() can increment this on every .scheduledTimer refresh. I'd like to define it in :didSelectRowAtIndexPath so that I can run multiple timers at once. Is there a way I define this in :didSelectRowAtIndexPath, and maybe get the result through returning the increment from getTimer()?
Currently I am updating my cellName with a string returned from getTimer. This is static and currently thinking of ways that I can refresh it when .scheduledTimer refreshes. Should I be maybe passing indexPath.row to getTimer and updating it in the getTimer() function itself?
Your logic to the problem is not right. I'll give you a quick solution to your problem. Create two variables timeArray and timerArray in the class scope
var timeArray = [Int]()
var timerArray = [Timer]()
Put this code in your viewDidLoad. When creating the array, replace the count 1 to the number of cells in your tableview.
timeArray = Array(repeating: 0, count: 1)
let timer = Timer(timeInterval: 1, target: self, selector: #selector(self.timerFired(_:)), userInfo: nil, repeats: true)
timerArray = Array(repeating: timer, count: 1)
In your didSelectRow tableview delegate method handle the timer validation and invalidation
if timerArray[indexPath.row].isValid{
timerArray[indexPath.row].invalidate()
}
else{
timerArray[indexPath.row] = Timer(timeInterval: 1, target: self, selector: #selector(self.timerFired(_:)), userInfo: indexPath.row, repeats: true)
timerArray[indexPath.row].fire()
}
Add this function that will get triggered when the timer is fired.
func timerFired(_ timer:Timer){
if let index = timer.userInfo as? Int{
timeArray[index] += 1
}
}
Finally, in you cellForRow datasource method of the tableview set the label text
cell.cellName.text = time[indexPath.row]
Don't forget to reload the particular cell when the timerFired function.
In swift, I have a uitableviewCell that has double tap and single tap implemented. The double tap works. However, I am having a bit of trouble with single tap. Due to the present of double tap, I implemented single tap with a timer. The following code successfully prints out "Single Tapped"
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var now = NSDate().timeIntervalSince1970
if (now - lastClick < 0.3) && indexPath.isEqual(lastIndexPath) {
// Double tapped on cell
if let cell = discoverTableView.cellForRowAtIndexPath(indexPath) as? CommentCell {
cell.doubleTapCellActive({ (addBumpStatus, success) in
})
}
singleTapTimer?.invalidate()
} else {
singleTapTimer = NSTimer.scheduledTimerWithTimeInterval(0.31, target: self, selector: #selector(DiscoverVC.singleTapped), userInfo: nil, repeats: false)
}
lastClick = now
lastIndexPath = indexPath
}
func singleTapped() {
print("Single tapped")
}
However, the problem is, I want the single tap to know which index path was selected. I tried doing something like
#selector(DiscoverVC.singleTapped(_:indexPath))
func singleTapped(indexPath: NSIndexPath) {}
But this gives me an error as selector does not like this syntax. Is there a way to make the selector work or a better way of doing it?
Thanks,
The usual way is to implement the action with the NSTimer parameter
func singleTapped(timer : NSTimer) {
print("Single tapped", timer.userInfo!["indexPath"] as! NSIndexPath)
}
and pass custom data via the userInfo property of the timer for example
singleTapTimer = NSTimer.scheduledTimerWithTimeInterval(0.31,
target: self,
selector: #selector(singleTapped(_:)),
userInfo: ["indexPath":indexPath],
repeats: false)
I have a tableview
I set timer in ViewDidLoad() as follows
self.timer = NSTimer(timeInterval: 10.0, target: self, selector: Selector("fireCellsUpdate"), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(self.timer, forMode: NSRunLoopCommonModes)
func fireCellsUpdate() {
print("In fireCellsUpdate")
let notification = NSNotification(name: "CustomCellUpdate", object:UILabel())
NSNotificationCenter.defaultCenter().postNotification(notification)
}
and in tableviewcell as follows
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "mycell")
var lbl_uploadTime = UILabel()
lbl_uploadTime.tag = indexPath.row
lbl_uploadTime.text = "3 hours 2 mins"
cell.contentView.addsubView(lbl_uploadTime)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateCountdownLabel:", name: "CustomCellUpdate", object: nil)
}
How can I update text for lbl_uploadTime without reloading whole tableview?
Here I reload whole tableview
func updateCountdownLabel(notification: NSNotification) {
println("broadcast received in cell %#",notification)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableview_DiscoverVideos.reloadData()
})
}
But I want to change only label text without reloading whole tableview. Please help me.
I recommend you to subclass UITableViewCell and add observer in that cell and then update to that UILabel to whatever you need. You should not add observer in cellForRowAtIndexPath: method.
Update: improvement:
Add observers in only custom cells where you need to change Label.
I have a tableView and I want update it every 10 seconds. For it I do:
var timer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: "reloadTable", userInfo: nil, repeats: true)
func reloadTable() {
print("reload")
self.tableView.reloadData()
}
but it doesn't reload correctly. What it means:
Yes, it reloads, but my data doesn't update, but when I drag my tableView to top and leave it, my tableView cells' data update. How can I achieve this effect programmatically?
UPDATE
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> onlineUserCell {
let cell:onlineUserCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! onlineUserCell
let user = OneRoster.userFromRosterAtIndexPath(indexPath: indexPath)
cell.username.text = user.displayName
if user.unreadMessages.intValue > 0 {
cell.backgroundColor = .orangeColor()
} else {
cell.backgroundColor = .whiteColor()
}
configurePhotoForCell(cell, user: user)
cell.avatarImage.layer.cornerRadius = cell.avatarImage.frame.size.height / 2
cell.avatarImage.clipsToBounds = true
return cell;
}
I'm not 100% sure, but if the table isn't visually updating, you probably aren't calling the UI code on the main thread. All UI code needs to be executed on the main thread in iOS.
Look up dispatch_async and/or performSelectorOnMainThread.
You need to update your datasource programmatically too.The reloadData() method just refresh the cell.If you don't refresh the datasource(the actually array) there is noting different.
I'd like to know how to get cell number(indexPath.row) in the following tapPickView function. topView is on the UITableViewCell, and pickView is on the topView. If pickView is tapped, tapPickView is activated.
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(
"QuestionAndAnswerReuseIdentifier",
forIndexPath: indexPath) as! QuestionAndAnswerTableViewCell
cell.topView.pickView.userInteractionEnabled = true
var tap = UITapGestureRecognizer(target: self, action: "tapPickView")
cell.topView.pickView.addGestureRecognizer(tap)
return cell
}
func tapPickView() {
answerQuestionView = AnswerQuestionView()
answerQuestionView.questionID = Array[/*I wanna put cell number here*/]
self.view.addSubview(answerQuestionView)
}
First of all, you need to append : sign to your selector upon adding gesture recognizer in order for it to get the pickView as its parameter.
var tap = UITapGestureRecognizer(target: self, action: "tapPickView:")
Besides that, cells are reusable objects, so you should prevent adding same gesture again and again to the same view instance by removing previously added ones.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("QuestionAndAnswerReuseIdentifier", forIndexPath: indexPath) as! QuestionAndAnswerTableViewCell
cell.topView.pickView.userInteractionEnabled = true
cell.topView.pickView.tag = indexPath.row
for recognizer in cell.topView.pickView.gestureRecognizers ?? [] {
cell.topView.pickView.removeGestureRecognizer(recognizer)
}
cell.topView.pickView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "tapPickView:"))
return cell
}
While populating the cell, you can set tag value of the pickView as indexPath.row so you can easily query that by cellForRowAtIndexPath(_:).
cell.topView.pickView.tag = indexPath.row
Assuming you already know the section of the cell you tap on. Let's say it is 0.
func tapPickView(recognizer: UIGestureRecognizer) {
let indexPath = NSIndexPath(forRow: recognizer.view.tag, inSection: 0)
if let cell = self.tableView.cellForRowAtIndexPath(indexPath) {
print("You tapped on \(cell)")
}
}
Hope this helps.
Assuming that this was not as simple as didSelectRowAtIndexPath, which I strongly recommend to first look into, passing the information to your method could look like this:
#IBAction func tapPickView:(sender: Anyobject) {
if let cell = sender as? UITableViewCell {
let indexPath = self.tableView.indexPathForCell(cell: cell)
println(indexPath)
}
}
Use didSelectRowAtIndexPath delegate method.