Change label.text from function Xcode/Swift - xcode

I have a timer, which calls a selector.
Directly from that function, I want to change the text of a label.
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
var timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update () {
self.myLabel.text = "MyNewText"
}
Xcode tells me to use "self", but the label is still not updating!
Thanks for your help!

UI updates must always be done in the main thread, so try this:
func update () {
DispatchQueue.main.async {
self.myLabel.text = "MyNewText"
}
}

Related

Xcode - control drag function not working

Beneath is my code,
Currently, it isn't working and allowing me to connect a button to my swift file (main storyboard). Also, the timer function isn't working, would this be to do with the function not working as well?
unsure on how to fix this, let me know if you need any other screen shots.
Any help will be appreciated.
Thanks
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var CounterLabel: UILabel!
var time = 30
var timer = Timer()
#IBAction func start(_ sender: UIButton)
{
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.action), userInfo: nil, repeats: true)
}
func action()
{
time += 1
}
//GIFS
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Creating a Scroll View Protocol in swift 2.2

I am currently developing an iOS application with login and sign up forms. To make sure that the keyboard does not cover any UITextFields I've implemented the following solution provided by Apple and discussed in this issue.
To briefly sum it up, this solution uses a UIScrollView in which the different UI elements are placed and UIKeyboardDidShowNotification and UIKeyboardDidHideNotification to move the elements up and down when the keyboard appears/disappears so that the UITextFields aren't hidden.
This works like a charm except for one thing: for all my UIViewControllers I have to repeat the same code. To tackle my problem I have tried:
to create a base UIViewController, providing an implementation for the different functions, that can be subclasses by the other UIViewControllers;
to use a protocol and a protocol extension to provide a default implementation for the different functions and make my UIViewControllers conform to it.
Both solutions didn't solve my problem. For the first solution, I wasn't able to connect the UIScrollView of my base class through the Interface Builder although it was declared.
#IBOutlet weak var scrollView: UIScrollView!
When trying to implement the second solution, the UIViewController implementing my protocol somehow did not recognise the declared methods and their implementations.
The protocol declaration:
protocol ScrollViewProtocol {
var scrollView: UIScrollView! { get set }
var activeTextField: UITextField? { get set }
func addTapGestureRecognizer()
func singleTapGestureCaptured()
func registerForKeyboardNotifications()
func deregisterForKeyboardNotifications()
func keyboardWasShown(notification: NSNotification)
func keyboardWillBeHidden(notification: NSNotification)
func setActiveTextField(textField: UITextField)
func unsetActiveTextField()
}
The protocol extension implements all functions expect for the addTapGestureRecognizer() as I would like to avoid using #objc:
extension ScrollViewProtocol where Self: UIViewController {
// The implementation for the different functions
// as described in the provided links expect for the following method
func registerFromKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardDidShowNotification, object: nil, queue: nil, usingBlock: { notification in
self.keyboardWasShown(notification)
})
NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardDidHideNotification, object: nil, queue: nil, usingBlock: { notification in
self.keyboardWillBeHidden(notification)
})
}
}
Does anyone have a good solution to my problem, knowingly how could I avoid repeating the code related to moving the UITextFields up and down when the keyboard appears/disappears? Or does anyone know why my solutions did not work?
I found a solution. I'll post it in case someone once to do the same thing.
So, I ended up deleting the UIScrollView outlet in my base class and replacing it with a simple property that I set in my inheriting classes. The code for my base class look as follow:
import UIKit
class ScrollViewController: UIViewController, UITextFieldDelegate {
// MARK: Properties
var scrollView: UIScrollView!
var activeTextField: UITextField?
// MARK: View cycle
override func viewDidLoad() {
super.viewDidLoad()
let singleTap = UITapGestureRecognizer(target: self, action: #selector(singleTapGestureCaptured))
scrollView.addGestureRecognizer(singleTap)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
registerForKeyboardNotifications()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
deregisterFromKeyboardNotifications()
}
// MARK: Gesture recognizer
func singleTapGestureCaptured(sender: AnyObject) {
view.endEditing(true)
}
// MARK: Keyboard management
func registerForKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWasShown), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillBeHidden), name: UIKeyboardWillHideNotification, object: nil)
}
func deregisterFromKeyboardNotifications() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWasShown(notification: NSNotification) {
scrollView.scrollEnabled = true
let info : NSDictionary = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height, 0.0)
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeFieldPresent = activeTextField {
if (!CGRectContainsPoint(aRect, activeFieldPresent.frame.origin)) {
scrollView.scrollRectToVisible(activeFieldPresent.frame, animated: true)
}
}
}
func keyboardWillBeHidden(notification: NSNotification) {
let info : NSDictionary = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height, 0.0)
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
view.endEditing(true)
scrollView.scrollEnabled = false
}
// MARK: Text field management
func textFieldDidBeginEditing(textField: UITextField) {
activeTextField = textField
}
func textFieldDidEndEditing(textField: UITextField) {
activeTextField = nil
}
}
And here is the inheriting class code:
class ViewController: ScrollViewController {
#IBOutlet weak var scrollViewOutlet: UIScrollView! {
didSet {
self.scrollView = self.scrollViewOutlet
}
}
// Your view controller functions
}
I hope this will help!

How to update label in Second ViewController with information from First ViewController every second

I have an app that has two ViewControllers. On the first there is a count of current speed in realtime through CLLocationManager. Also there is a label that shows current speed with update by timer (NSTimer). In second ViewController there is another Label, where this current speed has to be shown too. It shows it, but don't update. I tried to set second timer (different ways: in first VC, in second VC - there is always was an error or just nothing).
Will be grateful for help, thanks!
First VC
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var currentSpeedLabel: UILabel!
var manager = CLLocationManager()
var currentSpeed: CLLocationSpeed = CLLocationSpeed()
var timer = NSTimer()
override func viewDidLoad() {
super.viewDidLoad()
mapView.mapType = MKMapType.Hybrid
trackingMe()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func HUDMapView(sender: AnyObject) {
speedCount()
}
#IBAction func findMe(sender: AnyObject) {
trackingMe()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0] as CLLocation
manager.stopUpdatingLocation()
let location = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegion(center: location, span: span)
mapView.setRegion(region, animated: true)
}
func trackingMe() {
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
mapView.showsUserLocation = true
currentSpeedUpdate()
}
func currentSpeedUpdate() {
timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("speedCount"), userInfo: nil, repeats: true)
}
func speedCount() {
currentSpeed = manager.location!.speed
currentSpeedLabel.text = String(format: "%.0f km/h", currentSpeed * 3.6)
}
override func prepareForSegue(segue: (UIStoryboardSegue!), sender: AnyObject!) {
let speedController = segue.destinationViewController as! speedViewController
currentSpeed = manager.location!.speed
speedController.showSpeed = currentSpeedLabel.text
}
}
Second VC
import UIKit
class speedViewController: UIViewController {
#IBOutlet weak var secondSpeedLabel: UILabel!
var showSpeed: String!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
secondSpeedLabel.text = showSpeed
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func back(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Project Link
You could use a Singleton to hold the LocationManager. Then you can access it from all over your app. When you move to a second VC you can either change the delegate to the second VC or just get the needed data manually.
Remember that a delegate can only point to one "receiver". Changing the delegate will stop updates in the first VC. but since it is now a Singleton you can also store information in there about past locations / speeds. When dismissing the second VC get the stored data and update.
This will keep running until you call stop()
The code was simplified a bit to illustrate the idea.
VC Code:
import UIKit
import CoreLocation
class ViewController: UIViewController, TrackerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
Tracker.shared.delegate = self
Tracker.shared.start()
}
func speedUpdate(speed: CLLocationSpeed) {
print(speed)
}
}
Singleton Code:
import UIKit
import MapKit
import CoreLocation
class Tracker: NSObject, CLLocationManagerDelegate {
static var shared = Tracker()
private var manager = CLLocationManager()
private var timer = NSTimer()
var region : MKCoordinateRegion?
var currentSpeed: CLLocationSpeed = CLLocationSpeed()
weak var delegate : TrackerDelegate?
private override init() {
super.init()
manager.delegate = self
manager.requestWhenInUseAuthorization()
}
internal func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
manager.stopUpdatingLocation()
let userLocation: CLLocation = locations[0] as CLLocation
let coordinates2D = CLLocationCoordinate2D(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
let span = MKCoordinateSpanMake(0.05, 0.05)
region = MKCoordinateRegion(center: coordinates2D, span: span)
currentSpeed = userLocation.speed
guard let del = delegate else {
return
}
del.speedUpdate(currentSpeed)
}
func start() {
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.startUpdatingLocation()
timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("loopUpdate"), userInfo: nil, repeats: true)
}
func stop() {
timer.invalidate()
}
internal func loopUpdate() {
// restart updating
manager.startUpdatingLocation()
}
}
Delegate for the Singleton:
Add more functions, or more values to the current function to get more feedback.
protocol TrackerDelegate : class {
func speedUpdate(speed:CLLocationSpeed)
}

Trying to multiply a variable from a slider to a NSTimer variable.

I made a timer and then I added a slider with a label that presented its value. What I want to do is have my last label (Moneyadded) show a multiplication of the slider value by the current amount of seconds with NStimer.
import UIKit
class ViewController: UIViewController {
var timercount = 0
var timerRunning = false
var timer = NSTimer()
var myVaribale: Int = 0
override func viewDidLoad() {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
// fired once a second
myVaribale += 258
}
#IBOutlet weak var timerlabel: UILabel!
func counting(){
timercount += 1
timerlabel.text = "\(timercount)"
var timerValue = timercount.value
}
#IBAction func Clockin(sender: UIButton) {
if timerRunning == false{
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("counting"), userInfo: nil, repeats: true)
timerRunning = true
}
}
#IBAction func Clockout(sender: UIButton) {
if timerRunning == true{
timer.invalidate()
timerRunning = false
}
}
#IBAction func Restart(sender: UIButton) {
timercount = 0
timerlabel.text = "0"
}
#IBOutlet weak var Slider: UISlider!
#IBOutlet weak var Label: UILabel!
#IBAction func valuechanged(sender: UISlider) {
var currentValue = Int(Slider.value)
Label.text = "\(currentValue)"
}
#IBOutlet weak var Moneyadded: UILabel!
\\this is for the label (text) that I want the NStimer to be multiplied by the slider value.
Change your counting function to the following:
/** Gets triggered once every second */
func counting(){
timercount += 1
timerlabel.text = "\(timercount)"
Moneyadded.text = "\(timercount * Int(Slider.value))"
}

Extra argument 'selector' in call

I'm trying to create a simple stopwatch app where the timer label increments when the startButton button is pressed. This is what I have:
#IBOutlet weak var timer: UILabel!
#IBAction func startButton(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(0.0025, target: self, selector: Selector("result"), userInfo: nil, repeats: true)
}
var count = 0
func result() {
count++
timer.text=String(count)
}
I get the error "Extra argument 'selector' in call" but can't workout the syntax to do it properly.
Swift error messages are a little lacking at times. It should have said something like "NSTimer is not convertible to UILabel". You are assigning the timer you create to your IBOutlet timer which is a UILabel. The timer is an NSTimer. Just assign it to a different variable when you create it and all will be fine.
#IBAction func startButton(sender: AnyObject) {
let myTimer = NSTimer.scheduledTimerWithTimeInterval(0.0025, target: self, selector: "result", userInfo: nil, repeats: true)
}
As a shortcut, you can just use a string as a selector, so Selector("result") can be replaced by just "result".

Resources