IOS-CoreBluetooth-Mock/Example/nRFBlinky/ViewControllers/ScannerView/ScannerTableViewController....

246 lines
8.5 KiB
Swift

/*
* Copyright (c) 2020, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
class ScannerTableViewController: UITableViewController {
// MARK: - Outlets and Actions
@IBOutlet weak var emptyPeripheralsView: UIView!
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
// MARK: - Properties
private var manager: BlinkyManager!
// MARK: - UIViewController
override func viewDidLoad() {
super.viewDidLoad()
tableView.isAccessibilityElement = true
tableView.accessibilityLabel = "Scan Results"
tableView.accessibilityIdentifier = "scanResults"
let mock = (UIApplication.shared.delegate as! AppDelegate).mockingEnabled
manager = BlinkyManager(mock)
_ = manager.onStateChange { [unowned self] state in
if state == .poweredOn {
self.startScan()
} else {
self.activityIndicator.stopAnimating()
}
}
_ = manager.onBlinkyDiscovery { [unowned self] blinky in
self.addOrUpdateBlinky(blinky)
}
_ = onPeripheralSelected { [unowned self] blinky in
self.stopScan()
self.connectBlinky(blinky)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
manager.reset()
tableView.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startScan()
}
override func viewWillTransition(to size: CGSize,
with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
// This will rotate the view displayed when there are no peripherals scanned.
if view.subviews.contains(emptyPeripheralsView) {
coordinator.animate(alongsideTransition: { context in
let width = self.emptyPeripheralsView.frame.width
let height = self.emptyPeripheralsView.frame.height
if context.containerView.frame.height > context.containerView.frame.width {
self.emptyPeripheralsView.frame = CGRect(
x: 0, y: (context.containerView.frame.height / 2) - 180,
width: width, height: height
)
} else {
self.emptyPeripheralsView.frame = CGRect(
x: 0, y: 16,
width: width, height: height
)
}
})
}
}
// MARK: - UITableViewDelegate
override func numberOfSections(in tableView: UITableView) -> Int {
if manager.isEmpty {
showEmptyPeripheralsView()
} else {
hideEmptyPeripheralsView()
}
return manager.discoveredPeripherals.count > 0 ? 1 : 0
}
override func tableView(_ tableView: UITableView,
titleForHeaderInSection section: Int) -> String? {
return "Nearby devices"
}
override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return manager.discoveredPeripherals.count
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
withIdentifier: BlinkyTableViewCell.reuseIdentifier,
for: indexPath
) as! BlinkyTableViewCell
let blinky = manager.discoveredPeripherals[indexPath.row]
cell.setupView(withPeripheral: blinky)
return cell
}
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
post(.selectPeripheral(at: indexPath.row))
}
// MARK: - Segue and navigation
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return identifier == "PushBlinkyView"
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "PushBlinkyView" {
if let blinky = sender as? BlinkyPeripheral,
let destination = segue.destination as? BlinkyViewController {
destination.blinky = blinky
}
}
}
}
// MARK: - Implementation
private extension ScannerTableViewController {
func showEmptyPeripheralsView() {
if !view.subviews.contains(emptyPeripheralsView) {
view.addSubview(emptyPeripheralsView)
emptyPeripheralsView.alpha = 0
emptyPeripheralsView.frame = CGRect(
x: 0, y: (view.frame.height / 2) - 180,
width: view.frame.width, height: emptyPeripheralsView.frame.height)
view.bringSubviewToFront(emptyPeripheralsView)
UIView.animate(withDuration: 0.5) {
self.emptyPeripheralsView.alpha = 1
}
}
}
func hideEmptyPeripheralsView() {
if view.subviews.contains(emptyPeripheralsView) {
UIView.animate(withDuration: 0.5, animations: {
self.emptyPeripheralsView.alpha = 0
}, completion: { completed in
self.emptyPeripheralsView.removeFromSuperview()
})
}
}
func addOrUpdateBlinky(_ blinky: BlinkyPeripheral) {
tableView.reloadData()
}
func startScan() {
if manager.startScan() {
activityIndicator.startAnimating()
}
}
func stopScan() {
manager.stopScan()
activityIndicator.stopAnimating()
}
func connectBlinky(_ blinky: BlinkyPeripheral) {
performSegue(withIdentifier: "PushBlinkyView", sender: blinky)
}
}
extension Notification.Name {
static let selection = Notification.Name("Selection")
}
extension Notification {
static func selectPeripheral(at index: Int) -> Notification {
return Notification(name: .selection, userInfo: ["row": index])
}
}
private extension ScannerTableViewController {
func post(_ notification: Notification) {
NotificationCenter.default.post(notification)
}
func dispose(_ observer: NSObjectProtocol) {
NotificationCenter.default.removeObserver(observer)
}
private func on(_ name: Notification.Name, do action: @escaping (Notification) -> ()) -> NSObjectProtocol {
return NotificationCenter.default.addObserver(forName: name, object: nil, queue: nil, using: action)
}
func onPeripheralSelected(do action: @escaping (BlinkyPeripheral) -> ()) -> NSObjectProtocol {
return on(.selection) { notification in
if let userInfo = notification.userInfo,
let index = userInfo["row"] as? Int {
let blinky = self.manager.discoveredPeripherals[index]
action(blinky)
}
}
}
}