Updating UILabel.text with a variable belonging to a Singleton - ios

I have the following Singleton
class SharingManager{
var smallBigText : String = "KLANG!"
static let sharedInstance = SharingManager()
}
I use it to set the text of the following UILabel
#IBOutlet weak var kleinGrossLabel: UILabel!
I initialize its text here, in my ViewController:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.kleinGrossLabel.text = SharingManager.sharedInstance.smallBigText
}
I reset the SharingManager.sharedInstance.smallBigText in an instance method of my SoundEvent class:
class SoundEvent {
var text:String
var duration:Double
init(text: String, duration: Double){
self.text = text
self.duration = duration
}
func startEvent(){
SharingManager.sharedInstance.smallBigText = self.text
}
func getDuration() -> Double{
return self.duration
}
}
When I run the app, the UILabel text remains as "KLANG" and is never changed.
It should be changed when I call startEvent in the following function:
func playEvent(eventIndex : Int){
if (eventIndex < 2){
let currEvent = self.eventArray[eventIndex]
currEvent?.startEvent()
let nextIndex = eventIndex + 1
//NSTimer.scheduledTimerWithTimeInterval(0.4, target: SomeClass.self, selector: Selector("someClassMethod"), userInfo: nil, repeats: true)
NSTimer.scheduledTimerWithTimeInterval((currEvent?.duration)!, target: self, selector: Selector("playEvent:"), userInfo: NSNumber(integer: nextIndex), repeats: false)
}
else if (eventIndex==2){
self.eventArray[eventIndex]?.startEvent()
NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("sentenceDidFinish"), userInfo: nil, repeats: false)
}
else{
//Do Nothing
}
}
Which I call here in my ViewController
var s1:Sentence = Sentence(type: "S3")
s1.start()
Which in the Sentence class does this:
func start(){
self.playEvent(0)
}
Somehow it breaks this flow of logic, or if the expected sequence of events IS executing, then it follows that I am not actually changing the UILabel's text when I change the shared Singleton resource var smallBigText
For clarity here are all the main .swift
https://gist.github.com/anonymous/07542f638fc5b9a3c4e9
https://gist.github.com/anonymous/10f5f0deb03f9adc354c
https://gist.github.com/anonymous/94fda980836dc057b05b

Related

Why timer is getting stopped after tapping on the textfield in swift?

I want to run timer in tableViewHeader part, the tableView header is containing textField ,label and button. To manage the timer I have created singleton class and it's working fine in the initial but when I tap on textField the timer getting stopped. I don't why. Should I manage it using separate thread?
//singleton class
class WorkoutTimerManager {
static let shared = WorkoutTimerManager()
private var sec = 0
private var min = 0
private var timer = Timer()
private var date: Date? = Date()
var timerLbl:UILabel?
private init() {
}
func startTimer() {
timer.invalidate()
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
}
func stopTimer() {
timer.invalidate()
}
#objc func updateTimer() {
if let date = self.date {
let elapsedSec = abs(Int(date.timeIntervalSinceNow))
self.sec = elapsedSec % 60
self.min = elapsedSec / 60
print("sdfs1 \(sec)")
if let timerLbl = timerLbl {
timerLbl.text = "\(sec)"
print("sdfs2 \(sec)")
}
}
}
}
// table view headerview delegate method
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let name = "EmptyWorkoutHeader"
guard
let nib = Bundle.main.loadNibNamed(name, owner: nil, options: nil)
else { fatalError("missing expected nib named: \(name)") }
guard
let headerView = nib.first as? EmptyWorkoutHeader
else { fatalError("view of type \(name) not found in \(nib)") }
WorkoutTimerManager.shared.timerLbl = headerView.timerLbl
WorkoutTimerManager.shared.startTimer()
return headerView
}

unrecognized selector sent to instance-Swift

Hello All: i have a function that hit API to download data (LAT,LONG)and it works 100% fine. but the problem is i want to recall this function every 5 second and after i add a timer to do that every time i try to run it run very well and after 5 second i got crash
unrecognized selector sent to instance 0x7ffa4a51cb00
2018-07-20 11:05:31.191467+0200 Interactive Bus[684:6752] ***
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[Interactive_Bus.MapVC
downloadBusDataWithUserId:warning:delegate:completed:]: unrecognized
selector sent to instance 0x7ffa4a51cb00'
i try to clean then build and there is no Code Error i have no idea why this crash happens
this is my Model Class
// busLocationModel.swift
// Interactive Bus
import UIKit
import Alamofire
import SwiftyJSON
class busLocationModel {
var route_pins: String?
var busLatitude: Double?
var busLongitude: Double?
#objc func downloadBusData(userId: String,warning: #escaping (String,String,String) -> Void, delegate : MapVC ,completed : #escaping DownloadCompleted) {
let Parameters = ["parent_id": userId]
print(#function)
Alamofire.request(busLocationUrl, method: .get, parameters: Parameters).responseJSON { (response) in
switch response.result {
case .failure(let error):
print(error)
let alertControllerTitle = "Warning"
let actionButtonTitle = "Ok"
let alertMessage = "Some Thing Went Wrong Please Try Agin Later "
return warning(alertControllerTitle, actionButtonTitle, alertMessage)
case .success(let Value):
let json = JSON(Value)
//print(json)
let status = json["status"].boolValue
if status != false {
for locations in json["data"].arrayValue {
let busPins = locations["route_pins"].stringValue
let bus_lat = locations["bus_lat"].doubleValue
let bus_long = locations["bus_long"].doubleValue
delegate.busPins = busPins
delegate.currentBusLate = bus_lat
delegate.currentBusLong = bus_long
print(delegate.busPins ?? "HH")
print("the bus lat is \(bus_lat)")
print("the bus long is \(bus_long)")
}
}
}
completed()
}
}
}
and my Const is :
typealias DownloadCompleted = () -> ()
and My MapVC is :
//
import UIKit
import MapKit
import CoreLocation
class MapVC: UIViewController {
#IBOutlet weak private var busMapView: MKMapView!
var locationManager = CLLocationManager()
//var locationManager: CLLocationManager!
let authorizationStatus = CLLocationManager.authorizationStatus()
fileprivate let regionRadius: Double = 1000 //Meter's From UP,Right,Down and Left
fileprivate var busInfoObject: busLocationModel!
var busPins: String!
var currentBusLate: CLLocationDegrees?
var currentBusLong: CLLocationDegrees?
var callFuncTimer: Timer!
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
busMapView.delegate = self
busInfoObject = busLocationModel()
let myActivity = CreatActivityIndicator()
busInfoObject.downloadBusData(userId: "366", warning: DisplayAlertMessage, delegate: self) {
self.drawLine()
self.RemoveActivityIndicator(ActivityIndicator: myActivity)
guard let latitude = self.currentBusLate else { return }
guard let longitude = self.currentBusLong else { return }
let Location = CLLocation(latitude: latitude, longitude: longitude)
self.centerMapOnBusLocation(location: Location)
self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.busInfoObject.downloadBusData(userId:warning:delegate:completed:)), userInfo: nil, repeats: true)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
configureLocationServices()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
callFuncTimer.invalidate()
}
#IBAction func centerMapBtnPressed(_ sender: Any) {
if authorizationStatus == .authorizedAlways {
guard let latitude = self.currentBusLate else { return }
guard let longitude = self.currentBusLong else { return }
let Location = CLLocation(latitude: latitude, longitude: longitude)
centerMapOnBusLocation(location: Location)
}
}
}
extension MapVC: MKMapViewDelegate {
fileprivate func centerMapOnBusLocation(location: CLLocation) {
//guard let currtntLocationCoordinate = locationManager.location?.coordinate else { return }
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate , regionRadius * 2.0, regionRadius * 2.0)
busMapView.setRegion(coordinateRegion, animated: true)
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if (overlay is MKPolyline) {
let pr = MKPolylineRenderer(overlay: overlay)
pr.strokeColor = UIColor.blue
pr.lineWidth = 5
return pr
}
return MKPolylineRenderer()
}
}
extension MapVC {
func drawLine() {
let coordinates = busPins.components(separatedBy: "#").dropFirst().map { (pin) -> CLLocationCoordinate2D in
let latLng = pin.components(separatedBy: ",").map{ CLLocationDegrees($0)! }
return CLLocationCoordinate2D(latitude: latLng[0], longitude: latLng[1])
}
let polyLine = MKPolyline(coordinates: coordinates , count: coordinates.count)
self.busMapView.add(polyLine)
}
}
extension MapVC: CLLocationManagerDelegate {
fileprivate func configureLocationServices() {
if authorizationStatus == .notDetermined {
locationManager.requestAlwaysAuthorization()
} else {
return
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard let latitude = self.currentBusLate else { return }
guard let longitude = self.currentBusLong else { return }
let Location = CLLocation(latitude: latitude, longitude: longitude)
centerMapOnBusLocation(location: Location)
}
}
and this is DisplayAlertMessage:
func DisplayAlertMessage(alertControllerTitle: String , actionButtonTitle: String , alertMessage: String) -> Void{
let alertcontroller = UIAlertController(title: alertControllerTitle, message: alertMessage , preferredStyle: .alert)
let okaction = UIAlertAction(title: actionButtonTitle, style: .default, handler: nil)
alertcontroller.addAction(okaction)
self.present(alertcontroller, animated: true, completion: nil)
}
I can see no code Error i do the #OBJC the selector syntax is right but i still get the Error (unrecognized selector sent to instance) can you help me with that??
In target-action pattern, the target object needs to implement the action method.
But in your code:
self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.busInfoObject.downloadBusData(userId:warning:delegate:completed:)), userInfo: nil, repeats: true)
You use self as target, which is an instance of MapVC, that does not implement the method downloadBusData(userId:warning:delegate:completed:).
When you specify #selector(someInstance.methodName(...)) for the action method, you need to pass someInstance to the target object. In your case someInstance is self.busInfoObject.
Which means the line creating a Timer should become like this:
self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self.busInfoObject, selector: #selector(self.busInfoObject.downloadBusData(userId:warning:delegate:completed:)), userInfo: nil, repeats: true)
But this does not work.
I was stupid enough that I have almost forgotten to tell you another important thing in target-action pattern.
Which is,
The signature of the action method is definitely fixed according to the target.
When using Timer, the signature needs to be the same as this:
class func scheduledTimer(timeInterval: TimeInterval, target: Any, selector: Selector, userInfo: Any?, repeats: Bool) -> Timer
- (void)timerFireMethod:(NSTimer *)timer
The notation is in Objective-C format, but the action method for Timer needs to have one and only one argument of type Timer (it's NSTimer in Objective-C.)
So, you may need to define a method matches the signature in your MapVC:
func timerFired(_ timer: Timer) {
self.busInfoObject.downloadBusData(userId: ...,
warning: {_, _, _ in
...
},
delegate: self,
completed: {
...
})
}
And change the line setting the timer to:
self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.timerFired(_:)), userInfo: nil, repeats: true)
Sorry, for showing you an incomplete answer, please try with filling ... in my updated code.
Mistakes you have done here are
You are passing target as self that means your selector should be in MapVC
The selector you are passing is incorrect. According to Apple docs your selector should have signature
-(void)timerFireMethod:(NSTimer *)timer
Refer this https://developer.apple.com/documentation/foundation/timer/1412416-scheduledtimer
So to make it work do this:
func timerFireMethod(_ timer: Timer?) {
self.busInfoObject.downloadBusData(your parameters)
}
// Register timer
self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.timerFireMethod), userInfo: nil, repeats: true)
To make it easy you can use this method
self.callFuncTimer = Timer(timeInterval: 5, repeats: true, block: { (timer) in
print("timer")
})
Your settled target for timer is wrong. You should set function for timer and call.
func timerUpdates(_ timer : Timer)
{
self.busInfoObject.downloadBusData(userId:warning:delegate:completed:)
}
self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target:#selector(self.timerUpdates:) self, selector: #selector(), userInfo: nil, repeats: true)
Your invokation of the method missing some parameters
func downloadBusData(userId: String,warning: #escaping (String,String,String) -> Void, delegate : MapVC ,completed : #escaping DownloadCompleted)
as per your declaration it should have userId,warning,delegate and the completion handler
When you call function for every 5 seconds replace with this line and Please follow the convention of Swift. Write First letter capital for class name.
self.callFuncTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(BusLocationModel.downloadBusData(userId:warning:delegate:completed:)), userInfo: nil, repeats: true)

unrecognized selector sent to instance - Swift 3

I created a class based on this code: https://gist.github.com/etartakovsky/06b8c9894458a3ff1b14
When I try to instantiate the class and call the tick methods, I get "unrecognized selector sent to instance" error. I reviewed the code over and over but don't understand why this is happening, any advice is appreciated:
StopWatch Class source:
import Foundation
import QuartzCore
class StopWatch: NSObject{
private var displayLink: CADisplayLink!
private let formatter = DateFormatter()
var callback: (() -> Void)?
var elapsedTime: CFTimeInterval!
override init() {
super.init()
self.displayLink = CADisplayLink(target: self, selector: "tick:")
displayLink.isPaused = true
displayLink.add(to: RunLoop.main, forMode: RunLoopMode.commonModes)
self.elapsedTime = 0.0
formatter.dateFormat = "mm:ss,SS"
}
convenience init(withCallback callback: #escaping () -> Void) {
self.init()
self.callback = callback
}
deinit {
displayLink.invalidate()
}
func tick(sender: CADisplayLink) {
elapsedTime = elapsedTime + displayLink.duration
callback?()
}
func start() {
displayLink.isPaused = false
}
func stop() {
displayLink.isPaused = true
}
func reset() {
displayLink.isPaused = true
elapsedTime = 0.0
callback?()
}
func elapsedTimeAsString() -> String {
return formatter.string(from: Date(timeIntervalSinceReferenceDate:elapsedTime))
}
}
And here is the ViewController Code:
import UIKit
class ActivityViewController: UIViewController {
let stopwatch = StopWatch()
#IBOutlet weak var elapsedTimeLabel: UILabel!
func tick() {
elapsedTimeLabel.text = stopwatch.elapsedTimeAsString()
}
override func viewDidLoad() {
super.viewDidLoad()
tick()
stopwatch.callback = self.tick
stopwatch.start()
// Do any additional setup after loading the view.
}
}
In Swift 3 use the #selector syntax
self.displayLink = CADisplayLink(target: self, selector: #selector(tick))
In Swift 4 additionally you have to insert #objc at the beginning of the action
#objc func tick(...
Try two things. Use the new (and safer) selector syntax introduced by Swift 3:
CADisplayLink(target: self, selector: #selector(tick(sender:)))
and be sure to expose your tick method to Objective-C (the rules have changed in Swift 4):
#objc func tick(sender: CADisplayLink) {
...
}
To make it clear: unrecognized selector sent to instance is an error in MessagePassing scenario which means the desired selector which is:
func tick(sender: CADisplayLink) {...}
and has to receive the message is unrecognized.
It cannot be found because of the wrong way of addressing to it.
as other members said, you have to change your target selector by adding #selector(tick):
self.displayLink = CADisplayLink(target: self, selector: #selector(tick))
you can find more details about the error in this thread

How to get Notification from standalone class (Swift 3)

I want to call method in a class as soon as observer get Notification in another one. The problem is that I cannot call one class from another, because I will get recursion call then.
1) Controller class with Player instance:
// PlayerController.swift
// player
import UIKit
import MediaPlayer
class NowPlayingController: NSObject {
var musicPlayer: MPMusicPlayerController {
if musicPlayer_Lazy == nil {
musicPlayer_Lazy = MPMusicPlayerController.systemMusicPlayer()
let center = NotificationCenter.default
center.addObserver(
self,
selector: #selector(self.playingItemDidChange),
name: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange,
object: musicPlayer_Lazy)
musicPlayer_Lazy!.beginGeneratingPlaybackNotifications()
}
return musicPlayer_Lazy!
}
//If song changes
func playingItemDidChange(notification: NSNotification) {
//somehow call updateSongInfo() method from 2nd class
}
//Get song metadata
func getSongData() -> (UIImage, String?, String?) {
let nowPlaying = musicPlayer.nowPlayingItem
//...some code
return (albumImage, songName, artistAlbum)
}
func updateProgressBar() -> (Int?, Float?){
let nowPlaying = musicPlayer.nowPlayingItem
var songDuration: Int?
var elapsedTime: Float?
songDuration = nowPlaying?.value(forProperty: MPMediaItemPropertyPlaybackDuration) as? Int
elapsedTime = Float(musicPlayer.currentPlaybackTime)
return(songDuration, elapsedTime)
}
}
2) View controller which should be updated when Player Controller get notification
// MainViewController.swift
// player
import UIKit
class MainViewController: UIViewController {
let playerController = PlayerController()
#IBOutlet weak var albumView: UIImageView!
#IBOutlet weak var songLabel: UILabel!
#IBOutlet weak var artistAlbum: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//Start updating progress bar
Timer.scheduledTimer(timeInterval: 0.5,
target: self,
selector: #selector(MainViewController.updateProgressBar),
userInfo: nil,
repeats: true)
}
private func updateSongInfo(){
(albumView.image!, songLabel.text, artistAlbum.text) = playerController.getSongData()
}
private func updateProgressBar(){
(progressBar.maximumValue, progressBar.value) = playerController.playingItemProgress()
}
}
Solution for Swift 3:
In NowPlayingController:
let newSongNotifications = NSNotification(name:NSNotification.Name(rawValue: "updateSongNotification"), object: nil, userInfo: nil)
func playingItemDidChange(notification: NSNotification) {
NotificationCenter.default.post(newSongNotifications as Notification)
}
And in other controller:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.updateSongInfo), name: NSNotification.Name(rawValue: "updateSongNotification"), object: nil)
}
You can post a notification from within your custom object where you need it:
let notification = NSNotification(name:"doSomethingNotification", object: nil, userInfo: nil)
NotificationCenter.defaultCenter.postNotification(notification)
And then in your other view controller in which you want to execute something in response to this notification, you tell it to observe the notification in viewDidLoad(). The selector you pass in is the method you want to be executed when the notification is received.
override func viewDidLoad(){
super.viewDidLoad()
NotificationCenter.addObserver(self, selector: #selector(self.doSomething), name: "doSomethingNotification", object: nil)
}
You can use delegate method to update MainViewController

Swift 2 - Timed Actions one second apart?

I'm trying to get output like so:
1 (then a one second delay)
Hello
2 (then a one second delay)
Hello
3 (then a one second delay)
Hello
But instead I get
1
2
3 (then a one second delay)
Hello
Hello
Hello
Here's my for loop invoking the NSTimer
var timer = NSTimer()
for i in 1...3 {
print("\(i)");
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(MainVPScreenViewController.printTest), userInfo: nil, repeats: false)
}
And here's the selector method:
func printTest() {
print("Hello")
}
Thanks in advance for any help you can provide
Try this solution without NSTimer:
var i = 1
func printHello() {
print(i)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
print("Hello")
i +=1
if i <= 3 {
printHello()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
printHello()
}
I need 2 NSTimers to do this, this is my approach
class ViewController: UIViewController {
var i = 1
override func viewDidLoad() {
super.viewDidLoad()
beginPrinting()
}
func beginPrinting() {
var timer2 = NSTimer()
if(i <= 100)
{
timer2 = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(self.printWithDelay), userInfo: nil, repeats: false)
}
}
func printWithDelay()
{
var timer = NSTimer()
print("\(i)");
i += 1
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(self.printTest), userInfo: nil, repeats: false)
}
func printTest() {
print("Hello")
beginPrinting()
}
}
Hope this helps you
Use timer with repeat to true. So in your view controller would be like this
var timer = NSTimer()
var counter = 0
var max = 10
let delay = 1 // in second
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer.scheduledTimerWithTimeInterval(delay, target: self,
selector: #selector(self.printTest), userInfo: nil, repeats: true)
}
func printTest() {
counter += 1
print(counter)
print(hello)
if counter == maxNumber {
timer.invalidate()
}
}
This does it with repeat false, and is set up to be in a playground:
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
#objc class Foo: NSObject {
static var timer = NSTimer()
var i:Int
override init() {
Foo.timer = NSTimer()
i = 1
}
func schedule() {
print("\n\(i)");
i += 1
Foo.timer = NSTimer.scheduledTimerWithTimeInterval(1.0,
target: self,
selector: #selector(printTest),
userInfo: nil,
repeats: false)
}
#objc func printTest() {
print("Hello")
if i < 5 {
schedule()
}
}
}
let bar = Foo()
bar.schedule()

Resources