Realizzare un’app di citazioni a caso in Swift utilizzando SwiftUI e Combine

In questo articolo, esploreremo come costruire un’applicazione mobile iOS semplice che visualizza una citazione casuale da un elenco di citazioni memorizzate in un file di testo online. Questo esercizio ci permetterà di imparare a utilizzare alcune importanti tecnologie Swift, tra cui SwiftUI per l’interfaccia utente e Combine per la gestione degli eventi asincroni.

Iniziamo descrivendo l’architettura generale del nostro progetto. L’applicazione sarà composta da una singola schermata che visualizza una citazione casuale e offre un pulsante per caricare una nuova citazione. Le citazioni saranno memorizzate in un file di testo online, con una particolare formattazione per separare ogni citazione.

Partiamo da una semplice applicazione SwiftUI. Creiamo una nuova classe Cancellables per tenere traccia dei nostri processi asincroni. Utilizzeremo la libreria Combine di Swift per gestire i processi asincroni e i cambiamenti di stato.

import SwiftUI
import Combine

class Cancellables: ObservableObject {
    var set = Set<AnyCancellable>()
}

Successivamente, creiamo il nostro ContentView, che è il componente principale della nostra applicazione.

struct ContentView: View {
    @State private var text = "Loading..."
    private let url = URL(string: "https://www.raucci.net/frasi.txt%22!
    @StateObject private var cancellables = Cancellables()
    @State private var rotation = 0.0

Abbiamo definito alcune variabili di stato: text per la citazione attuale, url per l’URL del nostro file di citazioni, cancellables per conservare i processi asincroni e rotation per gestire l’animazione dell’immagine.

A seguire, definiamo la struttura visiva del nostro ContentView.

    var body: some View {
        NavigationView {
            VStack {
                Image("yourImageName") 
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .scaleEffect(0.8) 
                    .rotationEffect(.degrees(rotation))
                    .onTapGesture {
                        fetchQuotes()
                        withAnimation(.easeInOut(duration: 1.0)) {
                            self.rotation += 360
                        }
                    }
                Text(text)
                    .font(.custom("jgs_Font", size: 38)) 
                    .padding() 
                    .frame(maxWidth: .infinity, maxHeight: .infinity) 
            }
            .navigationTitle("fortune...")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button(action: copyQuote) {
                        Image(systemName: "doc.on.doc")
                    }
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: fetchQuotes) {
                        Image(systemName: "arrow.clockwise")
                    }
                }
            }
        }
        .onAppear {
            fetchQuotes()
        }
    }

Qui abbiamo definito una NavigationView con una VStack che contiene un’immagine e un Text. L’immagine è configurata per ridimensionarsi, mantenere le proporzioni, ridursi all’80% della sua dimensione originale e ruotare in base alla variabile di stato rotation. Il Text visualizza la citazione attuale e ha un font personalizzato, del padding e viene centrato nella vista. Abbiamo anche una barra di navigazione con due pulsanti: uno per copiare la citazione attuale e l’altro per caricare una nuova citazione.

La funzione fetchQuotes viene chiamata quando l’utente tocca l’immagine o il pulsante di caricamento e quando la vista appare.

    private func copyQuote() {
        UIPasteboard.general.string = text
    }

La funzione copyQuote copia la citazione attuale negli appunti del dispositivo.

    private func fetchQuotes() {
        URLSession.shared.dataTaskPublisher(for: url)
            .map { data, _ in String(data: data, encoding: .utf8) }
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { completion in
                if case .failure(let err) = completion {
                    self.text = "Failed to load: \(err)"
                }
            }, receiveValue: { text in self.processText(text) })
            .store(in: &cancellables.set)
    }

fetchQuotes avvia un processo asincrono per caricare il contenuto del nostro file di citazioni. Una volta che il contenuto è stato caricato, viene invocata la funzione processText.

    private func processText(_ text: String?) {
        guard let text

 = text else {
            self.text = "Failed to load"
            return
        }

        let trimmedText = text.replacingOccurrences(of: "\n", with: " ")
        let components = trimmedText.components(separatedBy: "#-")
        var phrases: [String] = []

        for component in components.dropFirst() {
            if let endRange = component.range(of: "-#") {
                let phrase = String(component[..<endRange.lowerBound]).trimmingCharacters(in: .whitespacesAndNewlines)
                if !phrase.isEmpty {
                    phrases.append(phrase)
                }
            }
        }

        if phrases.isEmpty {
            self.text = "No phrases found"
        } else {
            self.text = phrases.randomElement() ?? "No phrases found"
        }
    }
}

La funzione processText processa il contenuto del file di citazioni, estrae le singole citazioni e seleziona una citazione casuale.

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.

Realizzazione di TicTacToe in SwiftUI

Il presente articolo fornisce una dettagliata panoramica della realizzazione di un gioco di Tic Tac Toe (o Tris) utilizzando SwiftUI, il potente framework UI di Apple. Esploreremo le funzionalità fondamentali di SwiftUI, come le proprietà @State, le View, e i Button, e come queste possano essere utilizzate per creare un’app di gioco semplice ma interattiva.

L’app del gioco che abbiamo progettato si compone principalmente di un tabellone di gioco 3×3, rappresentato da una matrice 2D (board). Questa matrice è inizializzata con zeri, con l’idea che 1 rappresenta un giocatore (X) e 2 l’altro (O). L’app tiene traccia del giocatore corrente (currentPlayer) e se il gioco è terminato (gameOver) o meno.

Per rappresentare visivamente il tabellone di gioco, abbiamo utilizzato un anello esterno di VStack che contiene tre HStack. Ogni HStack contiene a sua volta tre Button, creando così un tabellone 3×3. Ogni Button rappresenta una cella del tabellone di gioco.

Per creare la struttura del nostro gioco, abbiamo utilizzato una combinazione di VStack (Vertical Stack) e HStack (Horizontal Stack). Questi sono tipi speciali di View in SwiftUI che permettono di impilare altre View in senso verticale o orizzontale. Guardiamo un frammento di codice:

VStack {
    ForEach(0..<3) { i in
        HStack {
            ForEach(0..<3) { j in
                ...
            }
        }
    }
}

In questo codice, creiamo un VStack che contiene tre HStack. Ogni HStack, a sua volta, contiene tre Button, creando così un layout 3×3.

Per risolvere la sfida della gestione delle mosse dei giocatori, abbiamo utilizzato la potente proprietà @State di SwiftUI. Questa proprietà ci permette di monitorare le modifiche al suo valore, aggiornando automaticamente la UI quando il valore cambia. Quando un giocatore tocca una cella del tabellone, la cella viene assegnata al currentPlayer, e il currentPlayer viene quindi alternato.

Ogni Button è collegato a un’azione che si attiva quando viene premuto. L’azione controlla se la cella del tabellone può essere occupata dal giocatore corrente:

Button(action: {
    if self.board[i][j] == 0 && !self.gameOver {
        self.board[i][j] = self.currentPlayer
        self.winner = self.checkWin()
        if self.winner != 0 {
            self.gameOver = true
        } else {
            self.currentPlayer = 3 - self.currentPlayer
        }
    }
})

Nell’azione del Button, controlliamo se la cella corrente è vuota e se il gioco non è finito. Se entrambe le condizioni sono vere, la cella viene assegnata al currentPlayer e viene verificato se c’è un vincitore. Se c’è un vincitore, gameOver viene impostato su true; altrimenti, currentPlayer viene alternato.

Una caratteristica fondamentale del Tic Tac Toe è la determinazione del vincitore. Per farlo, abbiamo implementato la funzione checkWin(), che verifica le righe, le colonne e le diagonali per un tris. Se trova un tris, restituisce il numero del giocatore che ha vinto. Se tutte le celle sono occupate senza un tris, la funzione dichiara un pareggio restituendo -1.

Dopo ogni mossa, la funzione checkWin() viene chiamata per verificare se la partita è finita. Se un giocatore ha vinto o se la partita è finita in pareggio, viene visualizzato un messaggio appropriato. Inoltre, il conto delle vittorie di ciascun giocatore viene aggiornato di conseguenza.

La funzione checkWin() è fondamentale per determinare l’esito del gioco. Verifica le righe, le colonne e le diagonali per vedere se c’è un tris. Se c’è un tris, restituisce il numero del giocatore che ha vinto. Se tutte le celle sono occupate e non c’è un tris, dichiara un pareggio restituendo -1:

func checkWin() -> Int {
    // Verifica righe e colonne
    for i in 0..<3 {
        if board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != 0 {
            return board[i][0]
        }
        if board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != 0 {
            return board[0][i]
        }
    }
    // Verifica diagonali
    if board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != 0 {
        return board[0][0]
    }
    if board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[2][0] != 0 {
        return board[2][0]
    }
    // Verifica pareggio
    for i in 0..<3 {
        for j in 0..<3 {
            if board[i][j] == 0 {
                return 0
            }
        }
    }
    return -1  // Pareggio
}

Il codice, ancora, per ripristinare il gioco, resetGame(), viene chiamato alla fine di ogni partita, per riportare tutte le variabili allo stato iniziale e preparare il tabellone per una nuova partita.

Un’altra sfida da affrontare era rendere ogni cella del gioco interamente sensibile al tocco. Inizialmente, solo la parte centrale della cella (il punto in cui appare la X o la O) rispondeva al tocco. Per risolvere questo problema, abbiamo utilizzato .background(Color.white) per la View Text quando è vuota, fornendo così un’area sensibile al tocco in tutta la cella.

Infine, per garantire un’esperienza di gioco fluida, abbiamo aggiunto un Button per ripristinare la partita una volta terminata. Questo resetta il tabellone e assegna il turno al giocatore che inizia in maniera casuale, preparando tutto per una nuova partita.

In sintesi, l’utilizzo di SwiftUI ha reso relativamente semplice la realizzazione di un gioco di Tic Tac Toe funzionante. La sua reattività integrata e l’uso di proprietà @State, insieme alla struttura componibile delle view, rendono SwiftUI un ottimo strumento per creare interfacce utente dinamiche e interattive su piattaforme Apple.

Tracciare il progresso dell’anno corrente in SwiftUI

Nell’ambito dello sviluppo di applicazioni, esistono numerose situazioni in cui potrebbe essere utile visualizzare una progress bar per tracciare il progresso di un’attività. In questo articolo, discuteremo come abbiamo implementato una progress bar in SwiftUI che traccia il progresso dell’anno corrente, mostrando non solo una barra di progresso visiva, ma anche una rappresentazione testuale che può essere facilmente copiata negli appunti.

Parte 1: Scelte progettuali

Il nostro obiettivo principale era creare un’app che potesse tracciare il progresso dell’anno corrente in modo visivo e testuale. Per realizzare ciò, abbiamo utilizzato SwiftUI, uno dei framework più potenti di Apple per lo sviluppo di interfacce utente. Ecco le variabili di stato che abbiamo definito all’inizio del nostro ContentView:

@State private var progress: Float = 0.0
@State private var monthsElapsed: Int = 0
@State private var daysElapsed: Int = 0
@State private var hoursElapsed: Int = 0
@State private var minutesElapsed: Int = 0
@State private var secondsElapsed: Int = 0

Queste variabili rappresentano il progresso dell’anno corrente e le unità di tempo trascorse dall’inizio dell’anno. Utilizzando @State, SwiftUI rileva automaticamente quando queste variabili vengono aggiornate e aggiorna l’interfaccia utente di conseguenza.

Parte 2: Calcolo del progresso

Il cuore del nostro codice risiede nella funzione calculateProgress(). Questa funzione calcola l’intervallo di tempo dall’inizio dell’anno ad ora, e lo divide per l’intervallo di tempo totale dell’anno per ottenere il progresso come un valore frazionario. Questo valore viene poi moltiplicato per 100 per ottenere una percentuale.

let elapsedSeconds = now.timeIntervalSince(startOfYear)
let totalSeconds = endOfYear.timeIntervalSince(startOfYear)
progress = Float(elapsedSeconds / totalSeconds)

Oltre a ciò, calculateProgress() calcola anche il numero di mesi, giorni, ore, minuti e secondi trascorsi dall’inizio dell’anno. Fa ciò utilizzando il metodo dateComponents(_:from:to:) dell’oggetto Calendar.current.

Parte 3: Visualizzazione del progresso

Per visualizzare il progresso, abbiamo utilizzato la classe ProgressView di SwiftUI. Questa vista è configurata per mostrare il progresso come una barra di avanzamento, con il valore del progresso impostato sul valore della variabile di stato progress.

ProgressView(value: progress)
    .progressViewStyle(LinearProgressViewStyle(tint: .blue))
    .frame(width: UIScreen.main.bounds.width * 0.8)
    .padding(.bottom, 20)

Oltre alla progress bar, abbiamo aggiunto delle etichette per mostrare il numero di mesi, giorni, ore, minuti e secondi trascorsi dall’inizio dell’anno.

Parte 4: Generazione di una barra di avanzamento testuale

Abbiamo anche voluto dare agli utenti la possibilità di copiare una rappresentazione testuale della progress bar negli appunti. Per realizzare ciò, abbiamo aggiunto un pulsante alla barra di navigazione che genera una stringa di progress bar quando viene premuto.

let filledSection = String(repeating: "▓", count: Int(progress * 20))
let unfilledSection = String(repeating: "░", count: 20 - Int(progress * 20))
let progressBarString = "\(filledSection)\(unfilledSection) \(Int(progress * 100))%"

Questo codice genera una stringa di progress bar con 20 caratteri totali. Il numero di “▓” nella stringa corrisponde alla percentuale del progresso.

Parte 5: Copia della barra di avanzamento negli appunti

Per copiare la barra di avanzamento negli appunti, abbiamo utilizzato il framework UIKit di Apple. Poiché UIKit non è nativamente compatibile con SwiftUI, abbiamo dovuto usare UIPasteboard.general.string per interagire con gli appunti.

Button(action: {
    UIPasteboard.general.string = progressBarString
}) {
    Image(systemName: "doc.on.doc")
}

Con questo codice, ogni volta che l’utente preme il pulsante, la stringa di progress bar viene copiata negli appunti, pronta per essere incollata in un’altra app o condivisa con qualcun altro.

Questo un esempio della barra testuale generata dal codice:

▓▓▓▓▓▓▓░░░░░░░ 50%

Qui, il reposity della app su GitHub.

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.

SwiftUi e le notifiche a tempo…

SwiftUI, il framework di sviluppo dell’interfaccia utente di Apple, ha semplificato la creazione di app iOS. In questo articolo, esploreremo un’applicazione SwiftUI che calcola un orario futuro in base ai valori di ore, minuti e secondi inseriti dall’utente, e invia una notifica al raggiungimento di quel tempo.

La nostra applicazione avrà un’interfaccia utente semplice ma funzionale, che consiste in tre campi di testo per l’input dell’utente (ore, minuti e secondi), un pulsante per avviare il calcolo dell’orario futuro e un campo di testo per visualizzare l’orario calcolato.

Inoltre, una volta raggiunto l’orario futuro calcolato, l’app invierà una notifica al dispositivo, riproducendo il suono di notifica predefinito del sistema.

ContentView

ContentView è la view principale dell’app. Contiene l’interfaccia utente e la logica per calcolare l’orario futuro.

I campi di testo TextField per le ore, i minuti e i secondi sono associati a variabili di stato (hours, minutes, seconds). Grazie al binding in SwiftUI, ogni modifica ai TextField viene automaticamente riflessa nelle rispettive variabili.

Il pulsante “Calcola Orario”, al click, avvia la funzione addTime(), che gestisce il calcolo dell’orario futuro e la programmazione della notifica.

Infine, l’orario futuro calcolato viene visualizzato in un elemento Text.

addTime()

La funzione addTime() gestisce il calcolo dell’orario futuro. Prima di tutto, estrae i valori dalle variabili di stato e li converte in interi. Se nessun valore viene inserito, imposta un valore di default di 0 per le ore e i minuti, e 1 per i secondi.

Successivamente, utilizza questi valori per calcolare l’orario futuro, aggiungendoli all’orario corrente. Infine, formatta l’orario futuro in una stringa e lo assegna a futureTime.

La funzione addTime() chiama anche scheduleNotification(), passando l’orario futuro, per programmare la notifica.

requestNotificationPermission()

All’avvio dell’app, viene richiesta l’autorizzazione per inviare notifiche. Questo viene gestito da requestNotificationPermission().

scheduleNotification()

La funzione scheduleNotification() gestisce la creazione e la programmazione della notifica.

Crea un UNMutableNotificationContent, che rappresenta il contenuto della notifica, e imposta il titolo e il suono della notifica.

Crea un UNTimeIntervalNotificationTrigger che farà scattare la notifica all’orario futuro, e infine aggiunge la richiesta di notifica al UNUserNotificationCenter.

Di seguito si riporta il codice completo per la App

import SwiftUI
import UserNotifications
struct ContentView: View {
    @State private var hours: String = ""
    @State private var minutes: String = ""
    @State private var seconds: String = ""
    @State private var futureTime: String = ""
    var body: some View {
        VStack {
            Text("Inserisci ore, minuti e secondi:")
            TextField("Ore", text: $hours)
                .keyboardType(.numberPad)
                .padding()
            TextField("Minuti", text: $minutes)
                .keyboardType(.numberPad)
                .padding()
            TextField("Secondi", text: $seconds)
                .keyboardType(.numberPad)
                .padding()
            Button(action: {
                addTime()
            }) {
                Text("Calcola Orario Futuro")
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(10)
            }
            Text("Orario futuro: \(futureTime)")
                .font(.system(size: 12))
                .fontWeight(.bold)
                .padding()
        }
        .padding()
        .onAppear(perform: {
            requestNotificationPermission()
        })
    }
    func addTime() {
        let h = Int(self.hours) ?? 0
        let m = Int(self.minutes) ?? 0
        var s = Int(self.seconds) ?? 0
        if h == 0 && m == 0 && s == 0 {
            s = 1
        }
        let currentTime = Date()
        let future = Calendar.current.date(byAdding: .hour, value: h, to: currentTime)
        let future1 = Calendar.current.date(byAdding: .minute, value: m, to: future ?? currentTime)
        let futureDate = Calendar.current.date(byAdding: .second, value: s, to: future1 ?? currentTime) ?? Date()
        let formatter = DateFormatter()
        formatter.dateStyle = .none
        formatter.timeStyle = .medium
        futureTime = formatter.string(from: futureDate)
        scheduleNotification(futureDate)
    }
    func requestNotificationPermission() {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { success, error in
            if success {
                print("All set!")
            } else if let error = error {
                print(error.localizedDescription)
            }
        }
    }
    func scheduleNotification(_ date: Date) {
        let content = UNMutableNotificationContent()
        content.title = "Alert Time!"
        content.sound = UNNotificationSound.default
        let interval = date.timeIntervalSince(Date())
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: interval, repeats: false)
        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
        UNUserNotificationCenter.current().add(request)
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Il peso di un mazzo di fogli di carta…

SwiftUI, il framework di Apple per lo sviluppo di interfacce utente, offre un modello di programmazione dichiarativo che rende semplice la creazione di interfacce utente complesse. In questo articolo, esploreremo come utilizzare SwiftUI per creare un’applicazione che calcola il peso totale di un mazzo di fogli di carta data la dimensione, la grammatura e il numero di fogli.

Questa applicazione può essere molto utile in vari contesti. Ad esempio, può essere utilizzata in un ufficio per stimare il peso di un mazzo di documenti prima di spedirli, o in una tipografia per calcolare il peso di un lotto di carta prima di stamparlo. Inoltre, può essere utilizzata in ambito educativo per insegnare concetti di matematica e fisica, come l’area, la densità e il peso.

import SwiftUI

struct ContentView: View {
    @State private var length = ""
    @State private var width = ""
    @State private var grammage = ""
    @State private var numberOfSheets = ""
    @State private var weight: String = "0"

    var body: some View {
        Form {
            Section(header: Text("Dimensioni del foglio")) {
                TextField("Lunghezza in cm", text: $length, onEditingChanged: { _ in weight = "0" })
                    .keyboardType(.decimalPad)
                TextField("Larghezza in cm", text: $width, onEditingChanged: { _ in weight = "0" })
                    .keyboardType(.decimalPad)
            }
    
            Section(header: Text("Grammatura")) {
                TextField("Grammatura in g/m^2", text: $grammage, onEditingChanged: { _ in weight = "0" })
                    .keyboardType(.decimalPad)
            }
    
            Section(header: Text("Numero di fogli")) {
                TextField("Numero di fogli", text: $numberOfSheets, onEditingChanged: { _ in weight = "0" })
                    .keyboardType(.decimalPad)
            }
    
            Section(header: Text("Peso totale dei fogli")) {
                TextField("", text: $weight)
                    .disabled(true)
            }
    
            Button(action: calculatePaperWeight) {
                Text("Calcola Peso")
            }
        }
    }
    
    func calculatePaperWeight() {
        guard let length = Double(length), let width = Double(width), let grammage = Double(grammage), let numberOfSheets = Double(numberOfSheets) else {
            weight = "0"
            return
        }
    
        // Convert cm to m
        let lengthInM = length / 100
        let widthInM = width / 100
    
        let area = lengthInM * widthInM
        let weightValue = area * grammage * numberOfSheets
    
        // Convert weight to kilograms
        let weightInKg = weightValue / 1000
    
        weight = String(format: "%.2f", weightInKg)
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

L’interfaccia utente dell’applicazione è composta da quattro sezioni per l’inserimento dei dati e un pulsante per attivare il calcolo. Le sezioni sono create utilizzando l’oggetto Section di SwiftUI, che consente di raggruppare logicamente i controlli dell’interfaccia utente. Le prime tre sezioni contengono TextField per l’inserimento della lunghezza e della larghezza del foglio di carta, la grammatura del foglio e il numero di fogli. La quarta sezione contiene un TextField disabilitato che visualizza il peso totale dei fogli di carta in chilogrammi.

Il pulsante “Calcola Peso” è creato utilizzando l’oggetto Button di SwiftUI. Quando l’utente preme questo pulsante, viene chiamata la funzione calculatePaperWeight, che calcola il peso totale dei fogli di carta.

SwiftUI utilizza un modello di gestione dello stato reattivo. In questo caso, utilizziamo la proprietà @State per creare delle variabili di stato per la lunghezza, la larghezza, la grammatura, il numero di fogli e il peso totale dei fogli. Queste variabili sono collegate ai TextField corrispondenti nell’interfaccia utente tramite il meccanismo di binding di SwiftUI, che consente di mantenere sincronizzati lo stato dell’applicazione e l’interfaccia utente.

Il peso totale dei fogli di carta viene calcolato utilizzando la formula:

Peso totale = Area * Grammatura * Numero di fogli

dove l’Area è calcolata come Lunghezza * Larghezza (in metri quadrati), la Grammatura è data in grammi per metro quadrato (g/m^2), e il Numero di fogli è il numero di fogli nel mazzo. Questo calcolo viene eseguito nella funzione calculatePaperWeight, che viene chiamata quando l’utente preme il pulsante “Calcola Peso”.