Connect with us

Разработка

Эффект свечения в стиле Apple Intelligence в SwiftUI

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

Опубликовано

/

     
     

Новый язык дизайна Apple представил эффект светящейся анимированной обводки, которая изящно и динамично подсвечивает формы и компоненты. Давайте рассмотрим, как воссоздать этот эффект в SwiftUI с помощью многоразовых расширений.

Расширение View с помощью фона и наложения

Первый шаг — сделать эффект свечения доступным для применения в любом месте. Предусмотрены два модификатора: один для фона и один для оверлея.

extension View {
    @MainActor
    func intelligenceBackground<S: InsettableShape>(
        in shape: S
    ) -> some View {
        background(
            shape.intelligenceStroke()
        )
    }
    
    @MainActor
    func intelligenceOverlay<S: InsettableShape>(
        in shape: S
    ) -> some View {
        overlay(
            shape.intelligenceStroke()
        )
    }
}

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

Добавление светящейся обводки к фигурам

Основная работа выполняется в кастомном методе InsettableShape. Это позволяет отображать любую фигуру, например, Capsule, Rectangle  или Circle, со светящейся анимированной рамкой.

extension InsettableShape {
    @MainActor
    func intelligenceStroke(
        lineWidths: [CGFloat] = [6, 9, 11, 15],
        blurs: [CGFloat] = [0, 4, 12, 15],
        updateInterval: TimeInterval = 0.4,
        animationDurations: [TimeInterval] = [0.5, 0.6, 0.8, 1.0],
        gradientGenerator: @MainActor @Sendable @escaping () -> [Gradient.Stop] = { .intelligenceStyle }
    ) -> some View {
        IntelligenceStrokeView(
            shape: self,
            lineWidths: lineWidths,
            blurs: blurs,
            updateInterval: updateInterval,
            animationDurations: animationDurations,
            gradientGenerator: gradientGenerator
        )
        .allowsHitTesting(false)
    }
}

Этот метод помещает фактическую логику рендеринга в отдельное представление.

Рендеринг слоёв обводки

IntelligenceStrokeView применяет несколько слоёв градиентной обводки, каждый со своей собственной толщиной линии, размытием и временем анимации. Вместе они создают мягкий светящийся ореол, который анимируется с течением времени.

private struct IntelligenceStrokeView<S: InsettableShape>: View {
    let shape: S
    let lineWidths: [CGFloat]
    let blurs: [CGFloat]
    let updateInterval: TimeInterval
    let animationDurations: [TimeInterval]
    let gradientGenerator: @MainActor @Sendable () -> [Gradient.Stop]

    @Environment(\.accessibilityReduceMotion) private var reduceMotion
    @State private var stops: [Gradient.Stop] = .intelligenceStyle

    var body: some View {
        let layerCount = min(lineWidths.count, blurs.count, animationDurations.count)
        let gradient = AngularGradient(
            gradient: Gradient(stops: stops),
            center: .center
        )

        ZStack {
            ForEach(0..<layerCount, id: \.self) { i in
                shape
                    .strokeBorder(gradient, lineWidth: lineWidths[i])
                    .blur(radius: blurs[i])
                    .animation(
                        reduceMotion ? .linear(duration: 0) : easeInOut(duration: animationDurations[i]),
                        value: stops
                    )
            }
        }
        .task(id: updateInterval) {
            while !Task.isCancelled {
                stops = gradientGenerator()
                try? await Task.sleep(for: .seconds(updateInterval))
            }
        }
    }
}

Каждая итерация цикла рисует один слой свечения, размытый по-разному для придания глубины. Ограничители градиента периодически обновляются, создавая анимированный эффект «перетекания».

Палитра градиентов

Эффект свечения основан на палитре мягких, но ярких цветов. Их расположение слегка хаотично, чтобы анимация выглядела естественно.

private extension Array where Element == Gradient.Stop {
    static var intelligenceStyle: [Gradient.Stop] {
        [
            Color(red: 188/255, green: 130/255, blue: 243/255),
            Color(red: 245/255, green: 185/255, blue: 234/255),
            Color(red: 141/255, green: 159/255, blue: 255/255),
            Color(red: 255/255, green: 103/255, blue: 120/255),
            Color(red: 255/255, green: 186/255, blue: 113/255),
            Color(red: 198/255, green: 134/255, blue: 255/255)
        ]
        .map { Gradient.Stop(color: $0, location: Double.random(in: 0...1)) }
        .sorted { $0.location < $1.location }
    }
}

Пример использования

Добавить свечение просто:

VStack(spacing: 30) {
    Text("Some text here")
        .padding(22)
        .intelligenceBackground(in: .capsule)

    Text("Some text here")
        .padding(22)
        .intelligenceOverlay(in: .rect(cornerRadius: 22))
}

В первом примере свечение располагается за капсулой, а во втором — отображается как наложение поверх прямоугольника.

Заключение

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

Пакет Swift на GitHub: https://github.com/Livsy90/IntelligenceGlow/tree/main

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: