Creare un’interfaccia utente in SwiftUI per controllare la torcia del tuo dispositivo iOS

In questa esercitazione, svilupperemo un’app iOS che userà SwiftUI per creare un’interfaccia utente (UI) interattiva per controllare la torcia del tuo dispositivo.

Il codice sarà strutturato in modo da permettere all’utente di accendere e spegnere la torcia premendo un pulsante. Quando la torcia è accesa, mostreremo un’immagine con la dicitura “ON AIR” e quando è spenta, l’immagine diventerà invisibile.

Vediamo il codice passo per passo:

import SwiftUI
import AVFoundation

All’inizio del file, importiamo i framework necessari. SwiftUI ci offre gli strumenti per costruire l’interfaccia utente, mentre AVFoundation ci permette di controllare l’hardware della fotocamera, che include la torcia.

struct ContentView: View {
    @State private var torch = false

Qui definiamo una nuova struct chiamata ContentView che aderisce al protocollo View di SwiftUI. Questa sarà la vista principale della nostra app. L’attributo @State è una proprietà di SwiftUI che permette di creare una variabile reattiva. In questo caso, stiamo usando torch per tracciare se la torcia è accesa (true) o spenta (false).

var body: some View {
        ZStack {
            onAirView

            VStack {
                Spacer()
                actionButton
            }
        }
        .padding()
        .background(Color.black)
        .edgesIgnoringSafeArea(.all)
    }

Il blocco body definisce la struttura principale dell’interfaccia utente. ZStack impila le viste l’una sopra l’altra. La nostra onAirView viene mostrata prima, seguita da un VStack che contiene un pulsante di azione (actionButton).

Notiamo che abbiamo impostato lo sfondo su nero utilizzando .background(Color.black) e abbiamo fatto sì che lo sfondo si estenda fino ai bordi dello schermo con .edgesIgnoringSafeArea(.all).

var onAirView: some View {
        VStack {
            Image("on_air")
                .resizable()
                .scaledToFit()
                .opacity(torch ? 1.0 : 0.0)
            Spacer()
        }
    }

onAirView è un’altra vista personalizzata. Qui, un’immagine con la dicitura “ON AIR” viene mostrata se la torcia è accesa. La vista Image viene resa ridimensionabile e adatta alla sua contenitore con .resizable() e .scaledToFit(). L’immagine diventa completamente trasparente quando la torcia è spenta, rendendola invisibile, grazie a .opacity(torch ? 1.0 : 0.0). Il Spacer() posiziona l’immagine in alto nello schermo.

var actionButton: some View {
        Button(action: {
            torch.toggle()
            toggleTorch(on: torch)
        }) {
            ZStack {
                if torch {
                    Color.red
                    Image(systemName: "power").foregroundColor(.white)
                }
                else {
                    Color.blue.opacity(0.1)
                    Image(systemName: "power").foregroundColor(.secondary)
                }
            }.frame(maxWidth:50, maxHeight: 50)
            .cornerRadius(15)
        }
    }

actionButton è il pulsante che l’utente preme per accendere o spegnere la torcia. Il colore e l’icona del pulsante cambiano in base allo stato della torcia. Usiamo la funzione toggleTorch(on:) per cambiare effettivamente lo stato della torcia sul dispositivo.

The Game of Life…

Esporremo brevemente come creare un’applicazione iOS per il Gioco della Vita, un automa cellulare ideato dal matematico britannico John Horton Conway, utilizzando Swift e SwiftUI.

Struttura del codice

Il codice è suddiviso in tre parti principali:

  1. La classe GameOfLife, che gestisce la logica del gioco.
  2. La vista GridView, che visualizza la griglia del gioco.
  3. La vista ContentView, che contiene i controlli del gioco e include GridView.

1. La Classe GameOfLife

La classe GameOfLife si occupa della logica e dello stato del gioco. Utilizza l’annotazione @Published per segnalare a SwiftUI quando determinate proprietà cambiano, provocando il re-rendering dell’interfaccia utente.

  • Inizializzazione: Genera una griglia di dimensioni specificate, con ogni cellula settata in modo casuale a vivente o morta.
  • toggleCell(x: Int, y: Int): Cambia lo stato di una cellula specifica (viva diventa morta, morta diventa viva).
  • startOrStop(): Inizia o ferma l’evoluzione del gioco.
  • reset(): Ferma il gioco e genera una nuova griglia casuale.
  • nextGeneration(): Calcola la prossima generazione della griglia in base alle regole del Gioco della Vita.
  • aliveNeighbours(x: Int, y: Int): Conta i vicini vivi di una determinata cellula.

2. La Vista GridView

Questa vista visualizza la griglia del gioco. Utilizza un VStack di HStacks per creare la griglia, con un rettangolo per ogni cellula. Il colore del rettangolo dipende dallo stato della cellula (nero per vivente, bianco per morto). Un tap su un rettangolo inverte lo stato della cellula corrispondente.

3. La Vista ContentView

ContentView è la vista principale dell’app. Contiene una GridView e due pulsanti per controllare il gioco: uno per avviare/fermare l’evoluzione del gioco e l’altro per resettare il gioco.

La Logica del Gioco della Vita

La logica del Gioco della Vita è implementata nel metodo nextGeneration() della classe GameOfLife. Le regole del gioco sono le seguenti:

  • Una cellula viva con meno di due vicini vivi muore.
  • Una cellula viva con due o tre vicini vivi sopravvive.
  • Una cellula viva con più di tre vicini vivi muore.
  • Una cellula morta con esattamente tre vicini vivi diventa una cellula viva.
func nextGeneration() {
    var newGrid = grid
    for x in 0..<grid.count {
        for y in 0..<grid[x].count {
            let aliveNeighbours = self.aliveNeighbours(x: x, y: y)
            if grid[x][y] {
                if aliveNeighbours < 2 || aliveNeighbours > 3 {
                    newGrid[x][y] = false
                }
            } else if aliveNeighbours == 3 {
                newGrid[x][y] = true
            }
        }
    }
    grid = newGrid
}

Queste regole vengono applicate simultaneamente a tutte le cellule della griglia per calcolare la prossima generazione.