SwiftUI-haptiikka ja sensoryFeedback iOS 26:ssa: käytännön opas 2026

SwiftUI:n .sensoryFeedback-modifier on iOS 26:ssa suositeltu tapa laukaista haptista palautetta. Käytännön opas kaikkiin SensoryFeedback-tyyppeihin, Core Hapticsiin ja saavutettavaan haptiikkaan koodiesimerkein ja UX-säännöin.

SwiftUI sensoryFeedback opas iOS 26 (2026)

Päivitetty: 22. elokuuta 2026

SwiftUI:n .sensoryFeedback-modifier on iOS 26:ssa suositeltu tapa laukaista haptista palautetta: se sitoo värähtelyn suoraan tilamuutoksen trigger-arvoon, käsittelee elinkaaren puolestasi ja korvaa UIKitin UIFeedbackGenerator-luokkien manuaalisen orkestroinnin. Tässä oppaassa käyn läpi kaikki SensoryFeedback-tyypit, milloin siirtyä Core Hapticsin puolelle omia AHAP-kuvioita varten, saavutettavuusasetukset sekä konkreettisia suunnittelusääntöjä siitä, milloin värähdys aidosti auttaa käyttäjää, ja milloin se on vain melua.

  • .sensoryFeedback(_:trigger:) on ollut saatavilla iOS 17:stä alkaen ja pysyy iOS 26:ssa SwiftUI-natiivin haptiikan ensisijaisena rajapintana. Apple ei julkaissut WWDC 2026:ssa korvaavaa API:a.
  • Modifier laukaisee palautteen aina kun trigger-arvo (joka on Equatable) muuttuu, joten vältät sekä onChange-ketjut että manuaalisen generaattorin lämmityksen.
  • Yksinkertaisiin tapahtumiin käytä .success, .selection, .impact-tyyppejä; monimutkaisiin sekvensseihin ja audiosynkronointiin käytä Core Hapticsia ja AHAP-tiedostoja.
  • Haptiikka toimii vain fyysisellä iPhonella (iPhone 8 ja uudemmat, Taptic Engine v2+); simulaattori ei tuota värähtelyä.
  • Kunnioita järjestelmätason "Vähennä liikettä" ja "Järjestelmähaptiikka" -asetuksia. .sensoryFeedback tekee sen puolestasi, mutta Core Haptics ei.

Mikä on sensoryFeedback-modifier?

.sensoryFeedback(_:trigger:) on SwiftUI-modifier, joka liittää haptisen palautteen näkymässä olevan tila-arvon muutokseen. Kun annat sille Equatable-arvon, järjestelmä laukaisee halutun palautteen aina kun arvo eroaa edellisestä. Modifier on saatavilla iOS 17:stä, iPadOS 17:stä, watchOS 10:stä ja macOS 14:stä alkaen. iOS 26:ssa API on identtinen alkuperäisen kanssa, mikä on hyvä uutinen: koodisi kääntyy sellaisenaan, ja Apple on selvästi vakiinnuttanut mallin.

Miksi tämä on merkittävää verrattuna vanhaan UIKit-tapaan? UIKitissä olet vastuussa generaattorin luonnista, sen prepare()-kutsusta ennen tarvittua ajanhetkeä ja itse laukaisusta. SwiftUI:ssa lauseoppi on julistava: sanot mihin tila-arvoon palaute liittyy, ja järjestelmä hoitaa ajoituksen. Käytännössä tämä poistaa yleisimmän haptiikkabugin, eli unohtuneen prepare()-kutsun, joka aiheuttaa 20–50 ms viiveen ensimmäiseen värähdykseen. (Törmäsin tähän itse viime projektissa, ja se söi tunteja debuggausta ennen kuin tajusin.)

Yksinkertaisin mahdollinen käyttö näyttää tältä:

import SwiftUI

struct FavoriteButton: View {
    @State private var isFavorite = false

    var body: some View {
        Button {
            isFavorite.toggle()
        } label: {
            Image(systemName: isFavorite ? "heart.fill" : "heart")
                .font(.system(size: 28, weight: .semibold))
                .foregroundStyle(isFavorite ? .pink : .secondary)
                .symbolEffect(.bounce, value: isFavorite)
        }
        .sensoryFeedback(.success, trigger: isFavorite) { oldValue, newValue in
            // Laukaise vain kun käyttäjä lisää suosikkiin, ei poistaessa
            newValue == true
        }
    }
}

Huomaa condition-sulkeuma: se saa vanhan ja uuden arvon parametreina ja palauttaa Bool-arvon, joka kertoo lauetaanko palaute. Tämä on hienovarainen mutta tärkeä yksityiskohta, koska voit rajata palautteen vain merkityksellisiin siirtymiin ilman erillistä tila-lippua.

sensoryFeedback vs Core Haptics: milloin käyttää kumpaa?

Yleisin päätös, jonka joudut tekemään, on: riittääkö .sensoryFeedback vai tarvitsenko Core Hapticsin täyden ohjauksen? Vastaus riippuu siitä, mallinnatko yksittäistä tapahtumaa (napin painallus, siirtymä, arvon muutos) vai kokemusta (pelin räjähdys, ajallisesti muotoiltu kuvio, äänen kanssa synkronoitu tuntemus).

Ominaisuus.sensoryFeedback (SwiftUI)Core Haptics (CHHapticEngine)
JulkaistuiOS 17 (2023)iOS 13 (2019)
Julistava vai imperatiivinenJulistava, sidottu tila-arvoonImperatiivinen, moottori luodaan ja käynnistetään käsin
Mukautetut kuviotEi. Vain ennalta määritellyt tyypitKyllä. AHAP-tiedostot, ohjelmalliset CHHapticPattern-oliot
AudiosynkronointiEi tuettuKyllä. CHHapticEvent tukee audiokerroksia
Intensiteetin/terävyyden säätöOsittain (vain .impact-tyyppi)Täydellinen ohjaus reaaliajassa
Kunnioittaa "Järjestelmähaptiikka"-asetusta automaattisestiKylläEi. Sinun tulee tarkistaa CHHapticEngine.capabilitiesForHardware()
Koodirivit yksinkertaiseen tapaukseen115–30
Sopiva käyttötarkoitusNapit, listavalinnat, sovellustilan muutoksetPelit, luovat sovellukset, audio-haptiikka, jatkuvat värähdykset

Honestly, käytännön nyrkkisääntöni yli 60 shippatun näkymän jälkeen on selkeä: aloita aina .sensoryFeedback-modifierilla. Siirry Core Hapticsiin vain silloin kun tarvitset joko (a) yli 30 ms:n mittaisen jatkuvan tuntemuksen, (b) mukautetun kuvion, jossa transienttien intensiteetti muuttuu, tai (c) tarkkaan synkronoidun ääni-haptiikkakokemuksen. Yhdenkään tuotantosovelluksemme "onnistumis-toaster" tai "tyhjennä lista" -tyyppinen palaute ei ole vaatinut Core Hapticsia.

Kaikki SensoryFeedback-tyypit yhdellä silmäyksellä

iOS 26:ssa SensoryFeedback-tyyppi tarjoaa 12 valmista palautetta, jotka kartoittuvat semanttisiin tapahtumiin — ei fyysisiin tuntemuksiin. Tämä on tärkeää: Apple varaa oikeuden muuttaa esimerkiksi .success-tuntemuksen mekaniikkaa iOS-versioiden välillä pitääkseen sen sopusoinnussa järjestelmän omien palautteiden kanssa. Älä ajattele tyyppejä ääninä vaan merkityksinä.

  • .success: onnistunut toiminto. Kaksi kevyttä pulssia. Käytä esim. tallennus onnistui, viesti lähti, tehtävä valmis.
  • .warning: varoitus, joka vaatii huomiota mutta ei estä toimintaa. Käytä säästeliäästi.
  • .error: virhe tai epäonnistuminen. Voimakas, tunnistettavissa oleva kolmoisvärähdys. Älä käytä lomakkeen validointiin, koska se on liikaa.
  • .selection: kevyt "tikk", kun käyttäjä liikkuu vaihtoehdosta toiseen (esim. Picker, segmented control).
  • .impact(weight:intensity:): yleiskäyttöinen. Painot: .light, .medium, .heavy, .soft, .rigid. Intensiteetti 0.0–1.0.
  • .increase / .decrease: arvo kasvoi tai pieneni. Esim. stepper, slider-pysäkki.
  • .start / .stop: pitkän toiminnon alku ja loppu (esim. äänitys).
  • .alignment: kohdistus, esim. kun kuvaa raahataan ja se napsahtaa ruudukkoon.
  • .levelChange: tasomuutos, esim. edistymispalkki ylittää välitavoitteen.
  • .pathComplete: käyttäjän piirtämä polku sulkeutuu (esim. Apple Watch -tapainen turvakuvio).

Miten lisään haptista palautetta SwiftUI-nappiin?

Yleisin PAA-kysymys: miten laukaisen haptiikan Button-napista SwiftUI:ssa? Sinulla on kolme tapaa, ja valinta riippuu siitä, kuinka tarkkaan haluat ajoittaa tuntemuksen.

1. Tila-sidonnainen (suositeltu)

Sido palaute samaan tila-arvoon, jota nappi muuttaa. Palaute laukeaa animaation kanssa samassa runkopäivityksessä, mikä on olennaista havaitulle nopeudelle.

@State private var count = 0

Button("Lisää") {
    withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
        count += 1
    }
}
.sensoryFeedback(.increase, trigger: count)

Huomaa jousiarvot: response: 0.4 ja dampingFraction: 0.7 tuottavat noin 400 ms mittaisen liikkeen, joka on ihanteellinen pariksi .increase-palautteen 30 ms transientin kanssa. Käyttäjä tuntee ja näkee muutoksen synkronissa.

2. Ehdollinen laukaisu

Jos sama tila voi laueta useassa tilanteessa mutta haluat värähdyksen vain yhdessä, käytä condition-sulkeumaa:

.sensoryFeedback(.success, trigger: uploadState) { _, new in
    new == .completed
}

3. Räätälöity semantiikka näkymätasolla

Jos rakennat useita samanlaisia nappeja, ekstraktoi ViewModifier:

struct HapticToggleStyle: ViewModifier {
    let value: Bool

    func body(content: Content) -> some View {
        content.sensoryFeedback(
            .impact(weight: .light, intensity: 0.7),
            trigger: value
        )
    }
}

extension View {
    func hapticToggle(bound value: Bool) -> some View {
        modifier(HapticToggleStyle(value: value))
    }
}

Tämä pitää designjärjestelmäsi haptiikan johdonmukaisena, ja jokainen togglenappi tuntuu samalta ilman että toistat parametrejä.

Miten luon mukautettuja haptiikkakuvioita Core Hapticsilla?

Kun tarvitset jotain, mitä .sensoryFeedback ei tarjoa (esimerkiksi kolmen kevenevän pulssin sekvenssin tai jatkuvan tekstuurin, joka reagoi sormen paineeseen), hyppäät Core Hapticsin puolelle. Core Haptics on Applen matalatason haptiikka- ja audio-API, joka antaa sinun kuvata palautteen tapahtumina (transientit, jatkuvat) ja parametreinä (intensiteetti, terävyys, aika).

Voit määritellä kuvion joko ohjelmallisesti tai AHAP-tiedostona (Apple Haptic and Audio Pattern). Kyseessä on JSON-muoto, joka on dokumentoitu Apple Developer -sivuilla. Suosin AHAP-tiedostoja, koska ne pysyvät versionhallinnassa siisteinä ja design-tiimi voi säätää niitä ilman Xcode-käännöstä.

import CoreHaptics

@Observable
final class HapticEngineManager {
    private var engine: CHHapticEngine?

    func prepare() {
        guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return }
        do {
            engine = try CHHapticEngine()
            engine?.stoppedHandler = { [weak self] _ in
                try? self?.engine?.start()
            }
            engine?.resetHandler = { [weak self] in
                try? self?.engine?.start()
            }
            try engine?.start()
        } catch {
            print("Haptiikkamoottorin käynnistys epäonnistui: \(error)")
        }
    }

    func playTripleTap() {
        guard let engine else { return }
        let events = (0..<3).map { i in
            CHHapticEvent(
                eventType: .hapticTransient,
                parameters: [
                    CHHapticEventParameter(parameterID: .hapticIntensity, value: 1.0 - Float(i) * 0.25),
                    CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.5)
                ],
                relativeTime: TimeInterval(i) * 0.12
            )
        }
        do {
            let pattern = try CHHapticPattern(events: events, parameters: [])
            let player = try engine.makePlayer(with: pattern)
            try player.start(atTime: CHHapticTimeImmediate)
        } catch {
            print("Kuvion toisto epäonnistui: \(error)")
        }
    }
}

Huomaa stoppedHandler ja resetHandler: nämä ovat kriittisiä tuotannossa. Moottori pysähtyy, kun sovellus siirtyy taustalle tai järjestelmä painostaa resursseja. Ilman käsittelijää ensimmäinen laukaisu paluun jälkeen on hiljainen. Törmäsin tähän itse ensimmäistä kertaa shippatessani peliprototyyppiä, ja seuraava build-ilta meni pelkkään moottorin elinkaaren opetteluun.

UIKit-yhteensopivuus: UIImpactFeedbackGenerator SwiftUI:ssa

Jos ylläpidät legacy-koodia tai kohdennat iOS 16:ta ja vanhempia, tarvitset yhä UIImpactFeedbackGenerator-luokkia. Ne toimivat SwiftUI:n sisällä normaalisti: laukaise ne Button-toiminnossa tai .onChange-modifierissa. Muista prepare()-kutsu ennen odotettua laukaisua, jotta Taptic Engine on lämmennyt. Applen Human Interface Guidelines -haptiikkasivu avaa hyvin myös taustan siitä, milloin generaattoreita on syytä käyttää edelleen.

struct LegacyHapticButton: View {
    private let generator = UIImpactFeedbackGenerator(style: .medium)

    var body: some View {
        Button("Toimi") {
            generator.impactOccurred(intensity: 0.8)
        }
        .onAppear { generator.prepare() }
    }
}

Ero UIImpactFeedbackGenerator-luokan ja .sensoryFeedback(.impact)-modifierin välillä on käytännössä käytettävyydellinen: SwiftUI-modifier hoitaa prepare()-elinkaaren puolestasi ja välttää sudenkuoppia, kuten generaattorin luonti näkymän body:ssä (mikä loisi uuden instanssin joka renderissä).

Vastaavasti UISelectionFeedbackGenerator vastaa .selection-tyyppiä, ja UINotificationFeedbackGenerator vastaa .success, .warning, .error -tyyppejä. iOS 17+ -koodissa suosi aina SwiftUI-modifieria.

Toimiiko sensoryFeedback iOS-simulaattorissa?

Ei. Tämä on toistuvin PAA-kysymys, ja vastaus on lyhyt: iOS-simulaattorilla ei ole fyysistä Taptic Engineä, joten .sensoryFeedback, UIImpactFeedbackGenerator ja Core Haptics eivät tuota mitään tuntuvaa. Xcode ei myöskään anna varoitusta. Koodi kääntyy ja "ajetaan", mutta laite ei tee mitään. Testaa haptiikka aina fyysisellä iPhonella.

Vähimmäislaitteistoksi tarvitset iPhone 8:n tai uudemman (Taptic Engine v2). Vanhemmilla iPhoneilla, iPadilla ja Apple TV:llä .sensoryFeedback palautuu hiljaisesti ilman virhettä. watchOS-laitteilla haptiikka toimii Apple Watch Series 3:sta alkaen, mutta tuntemukset ovat erilaiset (Taptic Engine on kellossa erityyppinen kompakti moottori).

Haptiikan saavutettavuus ja käyttäjäasetukset

Käyttäjät voivat poistaa järjestelmähaptiikan käytöstä kolmella tasolla: Asetukset → Ääni ja haptiikka → Järjestelmähaptiikka, Asetukset → Saavutettavuus → Kosketus → Värähdys, tai koko laitteen mykistys (fyysinen kytkin, joka joissakin malleissa hiljentää myös haptiikan). .sensoryFeedback kunnioittaa näitä asetuksia automaattisesti, mikä on syy suosia sitä Core Hapticsin yli aina kun mahdollista.

Jos rakennat sovelluksen, jossa haptiikka on osa käyttöliittymän ydintä (esim. pelisovellus, luova työkalu), tarjoa oma sovellustason haptiikkapainike. Jos rakennat välttämättömän palautteen (esim. maksun vahvistus), varmista että sama informaatio välittyy myös visuaalisesti ja auditiivisesti. WCAG:n kolme aistikanavaa -periaate koskee myös iOS-sovelluksia.

Vinkkinä UX-suunnittelusta: haptiikka on lisä, ei ensisijainen kanava. Kun luen suunnittelijan lähettämiä prototyyppejä, teen aina kolmen kanavan tarkistuksen: näkyykö tila ilman värähdystä? Kuuluuko se, jos käyttäjä kuuntelee VoiceOveria? Vasta sitten lisään haptiikan.

Suunnitteluperiaatteet: milloin haptiikka aidosti auttaa

Haptiikka on tehokkain, kun se vahvistaa fyysistä metaforaa. Napsahduksen, kytkennän tai tarttumisen tunne. Se on tehotonta (ja usein ärsyttävää), kun sitä käytetään sisältöilmoituksena ("uusi viesti", "ladattu"). Nämä ovat sääntöjä, joita seuraan tuotannossa:

  • Yksi merkitys per tuntemus. Älä käytä .success-palautetta sekä "tallennettu"- että "kopioitu leikepöydälle"-tapahtumissa samassa näkymässä. Käyttäjä oppii yhdistämään tuntemuksen tapahtumaan; kahden merkityksen antaminen aiheuttaa hämmennystä.
  • Pariuta liikkeen kanssa. Haptiikan tulee osua näkyvän muutoksen alkuun — ei loppuun. Käytä withAnimation(.spring(response: 0.4)) ja laukaise palaute samassa runkopäivityksessä. Jos jousi on tiukempi (esim. response: 0.25), harkitse .impact(weight: .light)-tyyppiä; pehmeämpi jousi (0.55) sopii .soft-painolle.
  • Alle 40 ms välitys. Käyttäjän aivot tulkitsevat kaiken alle 40 millisekunnin viiveen "reaktiivisena". Yli 100 ms:n viive tuntuu "erillisenä tapahtumana" ja rikkoo kausaalisuuden tunteen. Sido palaute samaan renderiin kuin animaatio, äläkä DispatchQueue.main.asyncAfter-kutsuun.
  • Ei kirjoitusliikkeeseen. Näppäimistön tuottaman kirjoituksen aikana haptiikka on melua. Poikkeus: kohdistuvat säätimet (slider snap, alignment).
  • Testaa aina hiljaisessa tilassa. Käyttäjät kokevat värähdyksen voimakkaammin, kun ympäristö on hiljainen. Se, mikä tuntuu subtiililtä toimistossa, on häiritsevän voimakas yöllä sängyssä.

Näiden periaatteiden hallitseminen erottaa "toimiva SwiftUI-koodi" -tason "pikselipuhdas UX" -tasosta. Kun rakennat komponenttikirjastoa, keskustele animaatioiden ja haptiikan yhteensovittamisesta myös liittyvissä artikkeleissamme. Esimerkiksi iOS 26:n Liquid Glass -suunnittelukielessä materiaalin syvyys ja haptinen palaute vahvistavat toisiaan, ja NavigationStack-siirtymissä hienovarainen .selection-palaute helpottaa suunnistautumista syvissä hierarkioissa.

Vielä yksi huomio: jos sovelluksesi käyttää Live Activities- ja Dynamic Island -toimintoja, muista että Dynamic Islandin ilmestyminen laukaisee järjestelmän oman haptiikan, joten älä lisää siihen omaa värähdystäsi päällekkäin. Sama pätee järjestelmän Toast- ja alert-elementteihin.

Usein kysytyt kysymykset

Mikä on Core Hapticsin ja sensoryFeedbackin ero?

.sensoryFeedback on korkean tason SwiftUI-modifier, joka laukaisee ennalta määritellyn palautteen (esim. .success, .impact) tila-arvon muuttuessa. Core Haptics on matalan tason API, joka antaa täyden ohjauksen kuvion tapahtumiin, intensiteettiin ja terävyyteen. Käytä sitä vain, kun tarvitset mukautetun sekvenssin tai audiosynkronoinnin.

Miten poistan haptisen palautteen SwiftUI-sovelluksesta?

Käyttäjä voi poistaa sen laitteen asetuksista (Ääni ja haptiikka → Järjestelmähaptiikka), ja .sensoryFeedback kunnioittaa tätä automaattisesti. Sovellustasolla voit lisätä oman kytkimen ja kääriä modifierit if-lauseeseen tai omaan ViewModifieriin, joka tarkistaa käyttäjäasetuksen ennen laukaisua.

Mikä iOS-versio tukee sensoryFeedback-modifieria?

iOS 17.0, iPadOS 17.0, watchOS 10.0, macOS 14.0 ja tvOS 17.0. iOS 26:ssa API on identtinen, joten koodi kääntyy sellaisenaan. Jos kohdennat iOS 16:ta tai vanhempaa, käytä UIImpactFeedbackGenerator-luokkaa Button-toiminnossa.

Miksi haptiikka toimii eri tavalla eri iPhoneilla?

Taptic Engine on kehittynyt sukupolvien välillä. iPhone 8 ja uudemmat käyttävät Taptic Engine v2:ta, ja iPhone 15 Pron ja uudempien moottori tarjoaa hienojakoisempaa .hapticSharpness-ohjausta. Sama .impact(weight: .light) voi tuntua hieman erilaiselta iPhone 12:lla kuin iPhone 16 Pro:lla, joten testaa aina tuoreimmalla ja vanhimmalla kohdelaitteellasi.

Voinko yhdistää sensoryFeedbackin ja Core Hapticsin samassa sovelluksessa?

Kyllä, ja usein sinun kannattaakin. Käytä .sensoryFeedback-modifieria kaikkiin standardeihin käyttöliittymän vuorovaikutuksiin ja Core Hapticsia vain erikoistapauksiin (pelit, luovat työkalut, audio-haptiikka). Tämä pitää yleisimmän tapauksen puhtaana ja kunnioittaa käyttäjän saavutettavuusasetuksia automaattisesti.

Diana Kowalski
Tietoa Kirjoittajasta Diana Kowalski

Mobile UX engineer translating design intent into pixel-perfect SwiftUI. Has strong opinions about haptics.