Другое

Как сделать элементы декора VisionOS видимыми при взгляде на них

Узнайте, как сделать элементы декора VisionOS видимыми при взгляде на них с помощью эффектов наведения и системного определения взгляда вместо данных отслеживания глаз. Полное руководство с практическими примерами кода и стратегиями реализации.

Как сделать так, чтобы орнамент VisionOS оставался видимым, пока пользователь на него смотрит?

У меня есть орнамент в visionOS, который автоматически скрывается. Есть ли способ определить, смотрит ли пользователь на него, чтобы оставить его видимым? В идеале он также должен появляться, когда пользователь смотрит на его местоположение — аналогично тому, как представление вкладок автоматически расширяется и остается расширенным, пока на него смотрят.

Я пробовал использовать @FocusState, но это не сработало. Поскольку Apple не предоставляет данные отслеживания взгляда, какие альтернативные методы существуют для достижения такого поведения?

swift
struct TestModifier: ViewModifier {
    @State var showOrnament = true

    func body(content: Content) -> some View {
        content
            .ornament(
                visibility: showOrnament ? .visible : .hidden,
                attachmentAnchor: .scene(.bottom)
            ) {
                Text("Ornament Content")
            }
            .task {
                try await Task.sleep(for: .seconds(2))
                showOrnament = false
            }
    }
}

Орнаменты visionOS не предоставляют прямой доступ к данным отслеживания взгляда по соображениям конфиденциальности, но вы можете добиться поведения автоматического расширения с помощью эффектов наведения и системного обнаружения взгляда. Платформа автоматически применяет эффекты наведения, когда пользователи смотрят на интерактивные элементы, что вы можете использовать для поддержания видимости орнаментов во время взаимодействия взглядом.

Содержание

Поведение орнаментов visionOS

Орнаменты visionOS разработаны как ненавязчивые компоненты пользовательского интерфейса, которые предоставляют контекстные элементы управления и информацию, не заслоняя основной контент. По умолчанию орнаменты автоматически скрываются для уменьшения визуального шума, когда они неактивно требуются. Это поведение контролируется параметром visibility в модификаторе .ornament(), который может быть установлен в .visible, .hidden или автоматические состояния.

Ключевое замечание из Документации для разработчиков Apple заключается в том, что орнаменты должны “предоставлять элементы управления и информацию, связанные с окном, не загромождая и не заслоняя содержимое окна”. Эта философия дизайна означает, что система автоматически управляет видимостью на основе шаблонов взаимодействия пользователя.

Эффекты наведения для обнаружения взгляда

Хотя прямые данные отслеживания взгляда недоступны разработчикам, visionOS предоставляет эффекты наведения, которые автоматически активируются, когда пользователи смотрят на интерактивные элементы. Согласно документации Unity PolySpatial, “компонент VisionOSHoverEffect предоставляет подсказку платформе применить системный эффект наведения, когда пользователь наводит курсор на этот объект. Обычно это используется для визуальной подсказки пользователю, что объект интерактивен. Этот эффект активируется взглядом или касанием руки.”

Вы можете использовать это системное обнаружение взгляда, сделав содержимое орнамента интерактивным:

swift
struct GazeResponsiveOrnament: ViewModifier {
    @State private var isHovered = false
    
    func body(content: Content) -> some View {
        content
            .ornament(
                visibility: isHovered ? .visible : .automatic,
                attachmentAnchor: .scene(.bottom)
            ) {
                // Делаем содержимое орнамента интерактивным для активации наведения
                Button(action: {}) {
                    Text("Содержимое орнамента")
                        .padding()
                        .background(Color.blue.opacity(0.3))
                        .cornerRadius(8)
                }
                .hoverEffect(.lift) // Система отреагирует на взгляд
                .onHover { hovering in
                    isHovered = hovering
                }
            }
    }
}

Пользовательские эффекты наведения в visionOS 2

В visionOS 2 был представлен мощный API пользовательских эффектов наведения, который позволяет разработчикам создавать сложные эффекты наведения, реагирующие на взгляд пользователя. Как упоминалось в сессии WWDC, “пользовательские эффекты наведения могут применяться к представлениям SwiftUI в любом месте вашего приложения, включая орнаменты и вложения Reality View”.

Ключевое преимущество заключается в том, что эти пользовательские эффекты могут быть “предварительно вычислены при запуске представления, предотвращая отслеживание взгляда для защиты конфиденциальности пользователя”, при этом все еще обеспечивая необходимую визуальную обратную связь:

swift
struct CustomHoverOrnament: ViewModifier {
    @State private var isHovered = false
    
    func body(content: Content) -> some View {
        content
            .ornament(
                visibility: isHovered ? .visible : .automatic,
                attachmentAnchor: .scene(.bottom)
            ) {
                VStack {
                    Text("Интерактивные элементы управления")
                        .padding()
                    
                    Button("Действие") { }
                        .buttonStyle(.borderedProminent)
                }
                .frame(width: 200)
                .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12))
                .customHoverEffect(
                    .continuous, // Плавная анимация
                    isHovered: $isHovered,
                    effect: { isHovering in
                        // Пользовательский эффект на основе состояния наведения
                        scaleEffect(isHovering ? 1.05 : 1.0)
                            .shadow(color: .blue.opacity(isHovering ? 0.3 : 0), radius: isHovering ? 12 : 4)
                    }
                )
            }
    }
}

Практические примеры реализации

Вот полная реализация, объединяющая обнаружение наведения с автоматическим управлением видимостью:

swift
struct SmartOrnamentModifier: ViewModifier {
    @State private var isHovered = false
    @State private var isVisible = false
    @State private var hideTimer: Timer?
    
    func body(content: Content) -> some View {
        content
            .ornament(
                visibility: isVisible ? .visible : .hidden,
                attachmentAnchor: .scene(.bottom)
            ) {
                InteractiveOrnamentContent(
                    isHovered: $isHovered,
                    onInteractionStart: { startTimer() },
                    onInteractionEnd: { stopTimer() }
                )
            }
            .onChange(of: isHovered) { newValue in
                if newValue {
                    showOrnament()
                }
            }
    }
    
    private func showOrnament() {
        isVisible = true
        stopTimer()
    }
    
    private func startTimer() {
        hideTimer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { _ in
            if !isHovered {
                hideOrnament()
            }
        }
    }
    
    private func stopTimer() {
        hideTimer?.invalidate()
        hideTimer = nil
    }
    
    private func hideOrnament() {
        isVisible = false
    }
}

struct InteractiveOrnamentContent: View {
    @Binding var isHovered: Bool
    let onInteractionStart: () -> Void
    let onInteractionEnd: () -> Void
    
    var body: some View {
        VStack(spacing: 12) {
            Text("Умные элементы управления")
                .font(.headline)
            
            HStack(spacing: 16) {
                Button("Опция 1") { }
                    .buttonStyle(.bordered)
                
                Button("Опция 2") { }
                    .buttonStyle(.bordered)
            }
        }
        .padding(16)
        .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12))
        .shadow(color: .blue.opacity(0.2), radius: 8)
        .onHover { hovering in
            if hovering && !isHovered {
                isHovered = true
                onInteractionStart()
            } else if !hovering && isHovered {
                isHovered = false
                onInteractionEnd()
            }
        }
        .customHoverEffect(
            .interactive,
            isHovered: $isHovered,
            effect: { isHovering in
                scaleEffect(isHovering ? 1.05 : 1.0)
                    .animation(.spring(response: 0.3, dampingFraction: 0.7), value: isHovering)
            }
        )
    }
}

// Использование:
struct ContentView: View {
    var body: some View {
        Text("Основное содержимое")
            .modifier(SmartOrnamentModifier())
    }
}

Соображения конфиденциальности и ограничения

Важно понимать ограничения, связанные с обнаружением взгляда в visionOS. Как отмечено в Форумах для разработчиков Apple, “Информация о положении глаз/взгляда недоступна для приложений visionOS”. Это ограничение существует для защиты конфиденциальности пользователя.

Системные эффекты наведения работают путем обнаружения, когда взгляд пользователя задерживается на интерактивном элементе, но эта обработка происходит на системном уровне без предоставления необработанных данных взгляда приложениям. Этот подход позволяет обеспечить интуитивное взаимодействие при сохранении конфиденциальности.

Альтернативные подходы

Если эффекты наведения не соответствуют вашим потребностям, рассмотрите эти альтернативные подходы:

1. Видимость на основе жестов

swift
struct GestureResponsiveOrnament: ViewModifier {
    @State private var isVisible = false
    @State private var tapCount = 0
    
    func body(content: Content) -> some View {
        content
            .ornament(
                visibility: isVisible ? .visible : .hidden,
                attachmentAnchor: .scene(.bottom)
            ) {
                VStack {
                    Text("Нажмите для взаимодействия")
                        .onTapGesture {
                            tapCount += 1
                            isVisible = true
                            
                            // Автоматическое скрытие после задержки
                            DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                                if tapCount == 1 { tapCount = 0 }
                                isVisible = false
                            }
                        }
                    
                    if tapCount > 1 {
                        Button("Оставаться видимым") { tapCount = 2 }
                    }
                }
                .padding()
            }
    }
}

2. Контекстно-зависимая видимость

swift
struct ContextAwareOrnament: ViewModifier {
    @State private var isVisible = false
    @State private var currentActivity: ActivityType = .idle
    
    enum ActivityType {
        case idle, selecting, editing, viewing
    }
    
    func body(content: Content) -> some View {
        content
            .onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
                updateVisibility(for: .viewing)
            }
            .ornament(
                visibility: isVisible ? .visible : .hidden,
                attachmentAnchor: .scene(.bottom)
            ) {
                ContextAwareContent(currentActivity: $currentActivity)
            }
    }
    
    private func updateVisibility(for activity: ActivityType) {
        currentActivity = activity
        isVisible = shouldShow(for: activity)
    }
    
    private func shouldShow(for activity: ActivityType) -> Bool {
        switch activity {
        case .selecting, .editing: return true
        case .viewing: return true // Показывать, когда пользователь активно просматривает
        case .idle: return false
        }
    }
}

3. Интеграция с голосовыми командами

swift
struct VoiceControlledOrnament: ViewModifier {
    @State private var isVisible = false
    @FocusState private var isListening: Bool
    
    func body(content: Content) -> some View {
        content
            .ornament(
                visibility: isVisible ? .visible : .hidden,
                attachmentAnchor: .scene(.bottom)
            ) {
                VStack {
                    Image(systemName: "mic.fill")
                        .foregroundColor(isListening ? .red : .blue)
                        .onTapGesture {
                            toggleListening()
                        }
                    
                    if isListening {
                        Text("Слушаю...")
                            .font(.caption)
                    }
                }
                .padding()
            }
            .onAppear {
                // Можно интегрировать с системой голосовых команд
                setupVoiceCommands()
            }
    }
    
    private func toggleListening() {
        isListening.toggle()
        isVisible = isListening
    }
    
    private func setupVoiceCommands() {
        // Реализация интегрировалась бы с системными голосовыми командами
        // для показа/скрытия орнамента на основе голосового ввода
    }
}

Источники

  1. Орнаменты | Документация для разработчиков Apple
  2. Отслеживание взгляда в VisionOS | Форумы для разработчиков Apple
  3. Эффекты наведения | PolySpatial visionOS | 2.0.4
  4. Создание пользовательских эффектов наведения в visionOS - WWDC24 - Apple Developer
  5. Советы по SwiftUI и эксклюзивные материалы об иммерсивном видео | Medium
  6. Создание орнаментов в visionOS | Create with Swift
  7. Функции доступности visionOS | AppleMagazine

Заключение

Чтобы поддерживать видимость орнаментов visionOS, пока пользователи на них смотрят, следует использовать встроенные в систему эффекты наведения, а не пытаться получить прямой доступ к данным отслеживания взгляда. Ключевые выводы:

  • Используйте @FocusState и эффекты наведения для обнаружения взаимодействия пользователя взглядом с содержимым орнамента
  • Реализуйте API CustomHoverEffect в visionOS 2 для создания сложной визуальной обратной связи
  • Объединяйте обнаружение наведения с автоматическим скрытием на основе таймера для оптимального пользовательского опыта
  • Помните, что прямые данные отслеживания взгляда недоступны из-за ограничений конфиденциальности
  • Рассмотрите альтернативные подходы, такие как голосовые команды или контекстно-зависимая видимость, если эффекты наведения не соответствуют вашим потребностям

Наиболее эффективное решение объединяет onHover обнаружение с модификатором .customHoverEffect для создания орнаментов, реагирующих на взгляд пользователя, при сохранении системных мер защиты конфиденциальности.

Авторы
Проверено модерацией
Модерация