WWDC Quick Look 💓 By SwiftGGTeam
Design for spatial interaction

Design for spatial interaction

元の動画を見る

ハイライト

Apple は、iOS 15 で U1 空間認識機能と近距離インタラクション機能をサードパーティ ハードウェアに公開しました。開発者は、距離と方向のフィードバックを使用してデバイス間での近距離インタラクションを設計できます。

主要内容

以前は、クロスデバイス操作の多くはリストを使用して行われていました。

iPhone の音楽をリビング ルームの HomePod に切り替えたい場合は、AirPlay リストを開く必要があります。各スピーカーはリスト内で隣り合って並んでおり、近くにあるデバイスは遠くにあるデバイスと同じように見えます。ユーザーが本当に言いたいことは「この曲をこのスピーカーで流してください」ということですが、インターフェースでは最初にリストを読んでから名前を選択する必要があります。

何かを見つけることも同様です。地図は大まかな場所にしか案内できません。部屋に入ったら、引き続き自分の目、耳、推測に頼って検索を続ける必要があります。ソファの継ぎ目、バッグの中に、またはテーブルの下にアイテムが隠されている場合、画面上の抽象的な位置では役に立ちません。

AppleはこのセッションでAirTag、HomePod mini、iPhoneのデザイン経験を総括した。主な変更点は、U1 チップによってもたらされる空間認識と、iOS 15 のサードパーティ ハードウェア用の新しい API によってもたらされます。デバイスは、近くのオブジェクトの距離、方向、相対関係を理解でき、アプリはこの情報を視覚、音声、および触覚フィードバックに変換できます。

これにより、対話のエントリ ポイントが変更されます。ユーザーは最初にリスト、ボタン、デバイス名を理解する必要はありません。このアプリを使用すると、ユーザーは体を動かし、ターゲットに近づき、方向を向くことができ、継続的なフィードバックを使用して次に何をすべきかを指示できます。

距離決定インターフェース

AirTag の発見エクスペリエンスは、最初の原則を示しています。つまり、同じタスクには、異なる距離にある異なるインターフェイスが必要です。

地図と道順は距離が遠い場合に役立ちます。範囲内に入ると、ボタンが「ルートを検索」から「検索」に変わります。接続が成功すると、矢印がターゲットを指し始めます。腕の届く範囲に入ると、矢印は触覚フィードバックに取って代わられ、ミリメートルレベルの動きは手の感触によって判断する方が適切であるためです。

ニアフィールド インタラクションを設計するときは、最初から最後までインターフェイスを使用しないでください。距離には方向性が必要です。近距離では精度が要求されます。最後の数センチメートルになると、画面の優先度が下がります。

フィードバックは継続的でなければなりません

HomePod mini の音楽送信は、2 番目の原則を実証しています。フィードバックは体の動きに合わせて継続的に変化する必要があります。

iPhone を HomePod mini に近づけると、画面上のバナーの位置、サイズ、背景のぼかしが距離に応じて変化します。 HomePodのトップライトも近接に反応します。そのまま近づけると送信が確定し、遠ざかると送信がキャンセルされます。

ここには余分な「確認」ボタンはありません。物理的な動き自体が確認とキャンセルを伴います。継続的なフィードバックにより、システムがユーザーの行動を理解していることをユーザーに知らせます。

インターフェイスは実際のアクションを提供する必要があります

AirTag は、ユーザーの注意が部屋にあるときに検索します。スクリーンはあくまで補助的なものです。

そのため、Apple は大きな矢印、間隔の広い数字、目立つ色の変化、音、タッチを使用しています。ユーザーは周辺視野を使って方向を読み取ることができ、また音を通じて空間内の AirTag の位置を判断することもできます。腕が届く範囲に入ると、最終的な位置決めには音よりも触覚フィードバックの方が適しています。

このタイプの設計の目標は、画面時間を短縮することです。インターフェースは注意を物理世界に戻す必要があります。

詳細

このセッションはデザイン指向であり、「コード」タブはありません。以下のコードは、設計原則をトランスクリプトで実現可能なアプリケーション状態にまとめます。 API レベルでは、リソースは Nearby Interaction フレームワークを指します。デザインレベルでは、スピーチは距離、方向、視覚、音、感触、中断を強調します。

1. デバイスの機能に応じてダウングレードする

(02:48) Apple は最初に共有パネルを使用して、空間認識低下戦略を説明しました。すべてのデバイスは Bluetooth と Wi-Fi を使用して近くの人を見つけることができます。 U1 を搭載した iPhone では、ユーザーが向いている人、またはユーザーに非常に近い人を表示することをさらに優先できます。

import Foundation

struct ShareCandidate {
    let name: String
    let communicationScore: Double
    let isNearby: Bool
    let isFacing: Bool
}

enum SpatialAwarenessLevel {
    case bluetoothAndWiFi
    case u1DirectionAware
}

func rankCandidates(
    _ candidates: [ShareCandidate],
    level: SpatialAwarenessLevel
) -> [ShareCandidate] {
    candidates.sorted { first, second in
        score(first, level: level) > score(second, level: level)
    }
}

func score(_ candidate: ShareCandidate, level: SpatialAwarenessLevel) -> Double {
    var result = candidate.communicationScore

    if candidate.isNearby {
        result += 10
    }

    if level == .u1DirectionAware && candidate.isFacing {
        result += 20
    }

    return result
}

let people = [
    ShareCandidate(name: "Arian", communicationScore: 42, isNearby: true, isFacing: false),
    ShareCandidate(name: "Taylor", communicationScore: 30, isNearby: true, isFacing: true),
    ShareCandidate(name: "Linus", communicationScore: 50, isNearby: false, isFacing: false)
]

let ranked = rankCandidates(people, level: .u1DirectionAware)
print(ranked.map(\.name))

キーポイント:

  • ShareCandidate候補者の通信頻度、候補者が近くにいるかどうか、およびこれらの入力に直面しているかどうかを保存します。
  • SpatialAwarenessLevelデバイスの機能を、基本的な近距離無線機能と U1 方向認識機能に分割します。
  • rankCandidatesソートのみを担当し、インターフェイスは同じ候補セットを再利用できます。
  • scoreベースデバイスでは、近くのウェイトのみが増加します。
  • level == .u1DirectionAware次の場合にのみ使用しますisFacing
  • この構造は、「すべてのデバイスが機能を備えているわけではなく、設計はさまざまな機能に適応する必要がある」というトランスクリプトの要件に対応しています。

2. 距離に基づいてメインフィードバックを切り替える

(04:00) AirTag ルックアップ プロセスは距離に応じて変化します。遠くからでも地図と道順を利用してください。接続後に矢印を使用します。近づくと方向判定が厳しくなります。腕が届く範囲に入ると、触覚が主なフィードバックになります。

import Foundation

enum PrimaryFeedback: String {
    case map
    case arrow
    case haptics
}

struct FindingFeedback {
    let primaryFeedback: PrimaryFeedback
    let facingToleranceInDegrees: Double
    let showsDistance: Bool
}

func feedbackForFindingItem(distanceInMeters: Double) -> FindingFeedback {
    if distanceInMeters > 10 {
        return FindingFeedback(
            primaryFeedback: .map,
            facingToleranceInDegrees: 90,
            showsDistance: true
        )
    }

    if distanceInMeters > 1 {
        return FindingFeedback(
            primaryFeedback: .arrow,
            facingToleranceInDegrees: 35,
            showsDistance: true
        )
    }

    return FindingFeedback(
        primaryFeedback: .haptics,
        facingToleranceInDegrees: 15,
        showsDistance: false
    )
}

let far = feedbackForFindingItem(distanceInMeters: 12)
let near = feedbackForFindingItem(distanceInMeters: 0.6)

print(far.primaryFeedback.rawValue)
print(near.primaryFeedback.rawValue)

キーポイント:

  • PrimaryFeedback現段階で最も重要なフィードバックの形式を示します。
  • distanceInMeters > 10いつ戻る.map、「大まかな位置」の段階に相当します。
  • distanceInMeters > 1いつ戻る.arrow、矢印誘導ステージに対応します。
  • ようやく戻ってきました.haptics、腕の範囲内の細かいサーチに対応します。
  • facingToleranceInDegrees距離に応じて縮小し、スピーチでは「遠くではより寛容な角度、近くではより正確な」デザインを表現しています。
  • showsDistance画面上の数字が触覚から注意を奪うことを避けるために、最終段階でオフにします。

3. 継続的なフィードバックを使用して確認とキャンセルを表現します

(07:23) HomePod mini の送信エクスペリエンスは 2 つの距離境界を使用します。最初の境界に到達すると、フィードバックが始まります。そのまま近づくと確認エリアに入ります。ズームアウトは自然に解除できます。

import Foundation

enum TransferPhase: String {
    case idle
    case ready
    case confirming
    case completed
}

struct TransferFeedback {
    let phase: TransferPhase
    let bannerScale: Double
    let backgroundBlur: Double
    let hapticIntensity: Double
}

func transferFeedback(distanceToSpeaker: Double) -> TransferFeedback {
    let firstThreshold = 0.6
    let secondThreshold = 0.12

    if distanceToSpeaker > firstThreshold {
        return TransferFeedback(
            phase: .idle,
            bannerScale: 0,
            backgroundBlur: 0,
            hapticIntensity: 0
        )
    }

    if distanceToSpeaker > secondThreshold {
        let progress = (firstThreshold - distanceToSpeaker) / (firstThreshold - secondThreshold)

        return TransferFeedback(
            phase: .ready,
            bannerScale: 0.8 + progress * 0.2,
            backgroundBlur: progress,
            hapticIntensity: progress
        )
    }

    return TransferFeedback(
        phase: .completed,
        bannerScale: 1,
        backgroundBlur: 1,
        hapticIntensity: 1
    )
}

let movingCloser = transferFeedback(distanceToSpeaker: 0.3)
let reachedSpeaker = transferFeedback(distanceToSpeaker: 0.08)

print(movingCloser.phase.rawValue)
print(reachedSpeaker.phase.rawValue)

キーポイント:

  • firstThresholdフィードバックが始まる境界です。
  • secondThreshold確認された送信の境界です。
  • 距離が最初の境界よりも大きい場合、ステータスは.idle、インターフェイスは注目を集めません。
  • 2 つの境界間の計算progress、バナー、ブラー、ハプティクスを継続的に変化させることができます。
  • 距離が 2 番目の境界より小さい場合、状態に入ります。.completed
  • ユーザーがiPhoneを引き離すと、機能は元の状態に戻ります。.idleまたは.ready、これは自然なキャンセルです。

4. 周辺視野の読み取り用のインターフェイス テキストをデザインする

(15:23) AirTag を検索するとき、ユーザーの注意は周囲の環境にある必要があります。 Apple は、ユーザーが長時間画面を見つめる必要がないように、より間隔をあけたテキスト、中央の矢印、明確な色の変化、サウンド、および触覚を使用しています。

import Foundation

enum FindingState {
    case connecting
    case pointing(distanceInMeters: Double, isFacingTarget: Bool)
    case withinReach
    case signalLost
}

struct PeripheralMessage {
    let title: String
    let detail: String
    let usesLargeType: Bool
    let shouldPlaySound: Bool
    let shouldUseHaptics: Bool
}

func message(for state: FindingState) -> PeripheralMessage {
    switch state {
    case .connecting:
        return PeripheralMessage(
            title: "正在连接",
            detail: "移动 iPhone 搜索附近物品",
            usesLargeType: true,
            shouldPlaySound: false,
            shouldUseHaptics: false
        )
    case .pointing(let distance, let isFacingTarget):
        return PeripheralMessage(
            title: String(format: "%.1f 米", distance),
            detail: isFacingTarget ? "继续向前" : "转动 iPhone 寻找方向",
            usesLargeType: true,
            shouldPlaySound: isFacingTarget,
            shouldUseHaptics: isFacingTarget
        )
    case .withinReach:
        return PeripheralMessage(
            title: "就在附近",
            detail: "缓慢移动 iPhone,跟随触觉反馈",
            usesLargeType: true,
            shouldPlaySound: false,
            shouldUseHaptics: true
        )
    case .signalLost:
        return PeripheralMessage(
            title: "信号丢失",
            detail: "回到刚才的位置重新连接",
            usesLargeType: true,
            shouldPlaySound: false,
            shouldUseHaptics: false
        )
    }
}

let state = FindingState.pointing(distanceInMeters: 2.4, isFacingTarget: true)
let currentMessage = message(for: state)
print(currentMessage.title)
print(currentMessage.detail)

キーポイント:

  • FindingState接続、ポインティング、アーム範囲内、信号損失の 4 つの状態をカバーします。
  • PeripheralMessageコピーライティング、フォントサイズ戦略、音声、タッチを統合します。
  • .pointingステータスには距離が大きく表示されるため、ユーザーは周辺視野で距離を読み取ることができます。
  • isFacingTarget音と感触は真実である場合にのみ再生され、安定した因果関係が形成されます。
  • .withinReach音声をオフにし、触覚フィードバックを使用し、トランスクリプト内で近距離で音声なしの指示を見つけます。
  • .signalLost中断後にユーザーが次のステップを知らないことがないよう、回復アクションを提供します。

5. 五感全体でフィードバックを機能させる

(12:45) Apple は、視覚的なフィードバックを連続レイヤーとして使用し、サウンドとタッチを使用して重要な瞬間を強調することを推奨しています。音とタッチの間には明確で再現可能な因果関係がなければなりません。

import Foundation

struct FeedbackCue {
    let visualProgress: Double
    let soundName: String?
    let hapticPattern: String?
}

func cueForArrowAlignment(wasAligned: Bool, isAligned: Bool, distanceInMeters: Double) -> FeedbackCue {
    let visualProgress = max(0, min(1, 1 - distanceInMeters / 5))

    if !wasAligned && isAligned {
        return FeedbackCue(
            visualProgress: visualProgress,
            soundName: "direction-lock",
            hapticPattern: "snap"
        )
    }

    if isAligned {
        return FeedbackCue(
            visualProgress: visualProgress,
            soundName: nil,
            hapticPattern: "pulse"
        )
    }

    return FeedbackCue(
        visualProgress: visualProgress,
        soundName: nil,
        hapticPattern: nil
    )
}

let cue = cueForArrowAlignment(
    wasAligned: false,
    isAligned: true,
    distanceInMeters: 3
)

print(cue.visualProgress)
print(cue.soundName ?? "no sound")
print(cue.hapticPattern ?? "no haptics")

キーポイント:

  • visualProgressこれは継続的に変化するビジュアル レイヤーです。距離が近いほど値は大きくなります。
  • !wasAligned && isAlignedターゲットが位置合わせされたばかりであることを示します。
  • 最初に整列したときに音を返しますdirection-lockそして触れるsnap、スピーチの「矢が戻る」瞬間に対応します。
  • 継続的な位置合わせ中にのみ保持されますpulse、音が繰り返されるのを避けるため。
  • 方向を逸脱した場合は音とタッチをオフにし、フィードバックとアクションが一対一の対応を保つことができます。

重要ポイント

1.「近づくとスイッチ」オーディオコントロールを作成する

  • 対処方法: ユーザーが iPhone をスピーカーに近づけると、インターフェースに自動的に送信プロンプトが表示されます。さらに近づけると再生切り替えが完了し、離れるとキャンセルされます。
  • 価値がある理由: HomePod mini の例は、距離の境界と継続的なフィードバックがデバイス リストの代わりに使用できることを証明しています。
  • 開始方法: まず、近くのデバイスを抽象化します。TransferPhase、距離に応じてバナー、ぼかし、触覚の強さを更新します。ハードウェア層は、Nearby Interaction または既存のニアフィールド検出機能に接続されます。

2. 倉庫アイテム検索ツールを作成する

  • やるべきこと: ツールボックス、サンプルボックス、または倉庫ラベルにニアフィールドポジショニングをサポートするアクセサリを追加し、iPhone を使用して従業員を目標に誘導します。
  • やる価値がある理由: AirTag の検索プロセスは、「まず大まかなエリアに行き、次にピンポイントで場所を特定する」というタスクに適しています。
  • 開始方法: プロセスを 3 つの段階 (マップ、矢印、触覚) に分割します。遠距離表示領域、近距離表示方向、触覚走査は腕の範囲内で使用されます。

3.「会う人全員と共有する」チーム コラボレーション ポータルを作成する

  • 対処法: 会議で共有パネルを開くときは、前または近くにいる同僚を優先してください。
  • 実行する価値がある理由: 講演では、共有パネルが向いている方向、物理的距離、通信頻度、最近の通信履歴に基づいて受信者を予測することが述べられました。
  • 開始方法: まず通信頻度と周囲のステータスで並べ替えます。次に、U1 をサポートするデバイスに方向の重みを追加します。容量が足りない場合は元のリストを保管してください。

4. 画面を見つめる必要のないナビゲーション補助機能を作成する

  • やるべきこと: 大きな方向指示、音、タッチを使用して、展示ホール、病院、キャンパス内で自分の部屋を見つけるようにユーザーを誘導します。
  • 実行する価値がある理由: セッションは空間インタラクションを強調し、ユーザーが周囲の環境に注意を戻すことができるようにします。
  • 開始方法: ナビゲーション状態ごとに生成PeripheralMessage。インターフェースには大きなフォントとハイコントラストな色彩を採用し、キーの状態を音とタッチで確認します。

5. 割り込み可能なデバイスのペアリング プロセスを作成する

  • 対処方法: ユーザーはアクセサリに近づくとペアリングが開始され、さらに近づき続けると確認され、離れるとペアリングが開始されます。
  • 価値がある理由: HomePod mini の二重境界デザインにより、自然な体の動きから確認とキャンセルが可能になります。
  • 開始方法: 2 つの距離のしきい値を定義します。最初のしきい値はフィードバックを開始し、2 番目のしきい値はアクションを実行します。距離が戻ったら準備状態を解除し、明確なフィードバックを与えます。

関連セッション

コメント

GitHub Issues · utterances