Matt
Matt

Reputation: 1177

Update UILabel from another class (with no relation to the view controller)

I have a app that has a UILabel that I would like to be updated by another swift class? The class is a service class and has no relation to the view controller with the UILabel but I would still like that service class to be able to update the label.

I think that this answer was what I needed but it does not work for Swift 3 and the text the label changes to is hardcoded which is not what I wanted either. I could not find anything else helpful about this.

BTService.swift:

import Foundation
import CoreBluetooth

let BLEServiceUUID = CBUUID(string: "025A7775-49AA-42BD-BBDB-E2AE77782966")
let PositionCharUUID = CBUUID(string: "A9CD2F86-8661-4EB1-B132-367A3434BC90") //RX let
let WriteCharUUID = CBUUID(string: "F38A2C23-BC54-40FC-BED0-60EDDA139F47") //TX let F38A2C23-BC54-40FC-BED0-60EDDA139F47
let BLEServiceChangedStatusNotification = "kBLEServiceChangedStatusNotification"
class BTService: NSObject, CBPeripheralDelegate {
    var peripheral: CBPeripheral?
    var positionCharacteristic: CBCharacteristic?
    var writeCharacteristic: CBCharacteristic?


    init(initWithPeripheral peripheral: CBPeripheral) {
        super.init()

        self.peripheral = peripheral
        self.peripheral?.delegate = self
    }

    deinit{
        self.reset()
    }

    func startDiscoveringServices() {
        self.peripheral?.discoverServices([BLEServiceUUID])
    }

    func reset() {
        if peripheral != nil {
            peripheral = nil
        }
        // Deallocating therefore send notification
        self.sendBTServiceNotificationWithIsBluetoothConnected(false)
    }


    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        let uuidsForBTService: [CBUUID] = [PositionCharUUID]
        let uuidsForBTService1: [CBUUID] = [WriteCharUUID]
        if (peripheral != self.peripheral) { // Wrong Peripheral
            return
        }
        if (error != nil) {
            return
        }

        if ((peripheral.services == nil) || (peripheral.services?.count == 0)) {
            // No Services
            return
        }

        for service in peripheral.services! {
            if service.uuid == BLEServiceUUID {
                peripheral.discoverCharacteristics(uuidsForBTService, for: service
                )
                peripheral.discoverCharacteristics(uuidsForBTService1, for: service )
            } }
    }


    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if (peripheral != self.peripheral) { // Wrong Peripheral
            return }
        if (error != nil) {
            return
        }

        for characteristic in service.characteristics! { if characteristic.uuid == PositionCharUUID {
            self.positionCharacteristic = (characteristic )
            peripheral.setNotifyValue(true, for: characteristic )

            self.sendBTServiceNotificationWithIsBluetoothConnected(true)

        }
        else if characteristic.uuid == WriteCharUUID {
            self.writeCharacteristic = (characteristic )
            peripheral.setNotifyValue(true, for: characteristic )

            self.sendBTServiceNotificationWithIsBluetoothConnected(true)

            }
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?){

       let it = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)

        if(it != nil){
            print("Val: \(it)") //Stackoverflow: This is what needs to be sent to the ViewController to be updated!!!
        }


    }



    func writePosition(position: UInt8) {
        if self.writeCharacteristic == nil {
            return
        }

        var positionValue = position
        let data = NSData(bytes: &positionValue, length: MemoryLayout<UInt8>.size)
        self.peripheral?.writeValue(data as Data, for: self.writeCharacteristic!, type: CBCharacteristicWriteType.withResponse)
    }


    func readPosition() -> NSString? {
        if self.positionCharacteristic == nil {
            return nil }
        self.peripheral?.readValue(for: self.positionCharacteristic!)
        if ((self.positionCharacteristic?.value) != nil) {
            print(self.positionCharacteristic!.value!)
            return NSString(data: self.positionCharacteristic!.value!, encoding:
                String.Encoding.utf8.rawValue) }

        return nil
    }

    func sendBTServiceNotificationWithIsBluetoothConnected(_ isBluetoothConnected: Bool) {
        let connectionDetails = ["isConnected": isBluetoothConnected]
        NotificationCenter.default.post(name: Notification.Name(rawValue: BLEServiceChangedStatusNotification), object: self, userInfo: connectionDetails)
    }




}

The ViewController:

import UIKit
import AVFoundation


class ViewController: UIViewController {

  @IBOutlet weak var imgBluetoothStatus: UIImageView!
  @IBOutlet weak var positionSlider: UISlider!
  @IBOutlet weak var BottomLabel: UILabel!




  var timerTXDelay: Timer?
  var allowTX = true
  var lastPosition: UInt8 = 255

  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.




    // Rotate slider to vertical position


    // Watch Bluetooth connection
    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.connectionChanged(_:)), name: NSNotification.Name(rawValue: BLEServiceChangedStatusNotification), object: nil)

    // Start the Bluetooth discovery process
    _ = btDiscoverySharedInstance
  }

  deinit {
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: BLEServiceChangedStatusNotification), object: nil)
  }

  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    self.stopTimerTXDelay()
  }

  @IBAction func positionSliderChanged(_ sender: UISlider) {
    self.sendPosition(UInt8(sender.value))




  }

  func connectionChanged(_ notification: Notification) {
    // Connection status changed. Indicate on GUI.
    let userInfo = (notification as NSNotification).userInfo as! [String: Bool]

    DispatchQueue.main.async(execute: {
      // Set image based on connection status
      if let isConnected: Bool = userInfo["isConnected"] {
        if isConnected {
          self.imgBluetoothStatus.image = UIImage(named: "Bluetooth_Connected")

          // Send current slider position
          self.sendPosition(UInt8( self.positionSlider.value))
        } else {
          self.imgBluetoothStatus.image = UIImage(named: "Bluetooth_Disconnected")
        }
      }
    });
  }

  func sendPosition(_ position: UInt8) {
    // Valid position range: 0 to 180

    if !allowTX {
      return
    }

    // Validate value
    if position == lastPosition {
      return
    }
    else if ((position < 0) || (position > 180)) {
      return
    }

    // Send position to BLE Shield (if service exists and is connected)
    if let bleService = btDiscoverySharedInstance.bleService {
      bleService.writePosition(position: position)
      lastPosition = position;

      // Start delay timer
      allowTX = false
      if timerTXDelay == nil {
        timerTXDelay = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(ViewController.timerTXDelayElapsed), userInfo: nil, repeats: false)
      }
    }
  }

  func timerTXDelayElapsed() {
    self.allowTX = true
    self.stopTimerTXDelay()

    // Send current slider position
    self.sendPosition(UInt8(self.positionSlider.value))
  }

  func stopTimerTXDelay() {
    if self.timerTXDelay == nil {
      return
    }

    timerTXDelay?.invalidate()
    self.timerTXDelay = nil
  }





}

In the BTService, there is a didUpdateValueFor function and I would like the it variable to be sent to the ViewController and have it set as the BottomLabel.

Upvotes: 0

Views: 481

Answers (1)

muescha
muescha

Reputation: 1539

it should be similar to your existing BLEServiceChangedStatusNotification:

define the name:

let BLEServicePeripheralChangedNotification = "kBLEServicePeripheralChangedNotification"

add the observer in viewDidLoad:

NotificationCenter.default.addObserver(self, selector: #selector(ViewController.peripheralChanged(_:)), name: NSNotification.Name(rawValue: BLEServicePeripheralChangedNotification), object: nil)

remove the observer in deinit:

NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: BLEServicePeripheralChangedNotification), object: nil)

add the function:

func peripheralChanged(_ notification: Notification) {
  // peripheral status changed. Indicate on GUI.
  let userInfo = (notification as NSNotification).userInfo as! [String: NSString]
  if let it: NSString = userInfo["it"] {
    //BottomLabel should be bottomLabel in declaration!
    BottomLabel.text = it
  }
}

add the post to notification center:

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?){

    let it = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)

    if(it != nil){
        print("Val: \(it)") 
        let itDetails = ["it": it]
        NotificationCenter.default.post(name: Notification.Name(rawValue: BLEServicePeripheralChangedNotification), object: self, userInfo: itDetails)
    }

}

PS: BottomLabel should be bottomLabel in declaration.

Upvotes: 1

Related Questions