Как мне оживить эффект gaussian blur в iOS?

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

Почти так же, как если бы в Photoshop вы поменяли значение gaussian blur по биту от 0 до 10, вместо 0 до 10 за один раз.

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

Это работает нормально, но не очень приятно, так как нет перехода, это просто оверлей. Пример:

enter image description here

Что было бы лучшим способом добиться такого эффекта? Я знаком с GPUImage, но не знаю, как это сделать с этим.

Было бы здорово, если бы я мог контролировать, какой процент от размытия, в котором он находится, поэтому он может применяться в интерактивном режиме от пользователя. (например: пользователь перетаскивается на полпути, размытие применяется наполовину и т.д.)

Ответ 1

С iOS 9 и выше вы можете анимировать эффект визуальных эффектов:

[UIView animateWithDuration:0.3 animations: ^ {
    visualEffectView.effect = blurEffect;
} completion:nil];

Это позволит достичь ожидаемого эффекта оживления радиуса размытия.


Я думаю, что если вы аниматируете кроссфейд из не-размытого вида → размытый вид, он будет достаточно приятным. Вам нужно анимировать отображение размытия в верхней части не-размытого вида.

Пусть предположим, что blurView - это вид, содержащий эффект размытия.

Вот два примера того, как выполнить анимированный переход:

[UIView transitionWithView:self.view duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations: ^ {
    [self.view addSubview:blurView];
} completion:nil];

Здесь я предполагаю, что blurView уже настроен и просто blurView добавление как subview в анимацию.

Вы также можете достичь этого по-другому:

blurView.alpha = 0.0f;
[UIView animateWithDuration:0.3 animations: ^ {
    blurView.alpha = 1.0f;
} completion:nil];

Здесь я делаю вид размытия прозрачным и оживляю его внешний вид. Обратите внимание, что использование этого метода не будет работать со hidden, поэтому вы хотите использовать свойство alpha.


Если вы хотите контролировать количество размытия в интерактивном режиме, вот пример того, как достичь:

Сначала создайте образ вида, который вы хотите размыть:

UIGraphicsBeginImageContextWithOptions(nonBlurredView.bounds.size, NO, self.view.window.screen.scale);
[nonBlurredView drawViewHierarchyInRect:nonBlurredView.bounds afterScreenUpdates:NO];
UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext();

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

Теперь, когда вам нужно размыть, используйте следующий код:

CIImage *imageToBlur = [CIImage imageWithCGImage:snapshotImage.CGImage];    
CIFilter *gaussianBlurFilter = [CIFilter filterWithName: @"CIGaussianBlur"]; 
[gaussianBlurFilter setValue:imageToBlur forKey: @"inputImage"]; 
[gaussianBlurFilter setValue:@10 forKey: @"inputRadius"]; //Here you input the blur radius - the larger, the 
CIImage *resultImage = [gaussianBlurFilter valueForKey: @"outputImage"]; 
UIImage *endImage = [[UIImage alloc] initWithCIImage:resultImage];

Вход inputRadius дает настройку количества выполняемого размытия. Если вы оживите это, вы достигнете интерактивного ощущения.

Но я по-прежнему считаю, что прежний метод намного проще и будет очень приятным для пользователя.

Ответ 2

Это легко сделать на OS X, где одно представление может CIFilter воздействовать на представление позади него и где процессор является мощным и быстрым. Однако на iOS нет фильтров для компоновки изображений, и размытие занимает некоторое время: вы не можете делать это многократно, живите так же быстро, как и в кадрах анимации. Поэтому необходима некоторая форма компромисса.

Если вы заранее знаете заранее, что хотите размыть, вы можете подготовить серию изображений, используя градацию размытия: не размытие, небольшое размытие, немного более размытие и т.д. Затем вы собираете изображения и воспроизводите их как "кадры" анимационного UIImageView.

Это, однако, не то, что я бы на самом деле посоветовал. То, что я сделал бы, это подготовить изображение размытия и не размытое изображение и использовать CIDissolveTransition для растворения от не-размытого до размытого изображения. Нет, это не то же самое, что постепенно применять размытие, но оно является недорогостоящим и является лучшим "растворяющим" эффектом в панели инструментов.

Ответ 3

Я редко предлагаю решения на полке для проблем с кодированием. Тем не менее, в этом случае я бы всем сердцем рекомендовал LiveFrost. Это действительно солидный подкласс UIView, ориентированный исключительно на репликацию популярного эффекта размытия iOS 7 gaussian.

Я также призываю вас прочитать блог автора относительно его проектных решений для этого класса. Я сделал много исследований специально в этой теме с момента выпуска iOS 7, и у меня есть буквально ZERO COMPLAINTS об этом коде.

Он даже размывает анимацию из коробки! Удачи тебе :)

Ответ 4

Я разработал небольшой проект, который использует GPUImage для живого размытия с переменным размытием и частотой кадров (MSLiveBlur) - это похоже на то, что вам нужно.

В примере приложения у меня есть ползунок, который увеличивает/уменьшает уровень размытия в ответ на действие пользователя, как вы упомянули в своем вопросе. Если вы хотите анимировать его без взаимодействия с пользователем, вы можете сделать таймер, который медленно увеличивает радиус размытия до достижения конечного значения. Однако вы не сможете использовать CoreAnimation с этим решением.

[[MSLiveBlurView sharedInstance] setBlurInterval:0.2];
[[MSLiveBlurView sharedInstance] blurRect:someView.frame];

// Count x from 0 to your final radius
[MSLiveBlurView sharedInstance].blurRadius = x;

Ответ 5

Здесь функция, которую вы можете использовать для анимации размытия с предварительно вычисленными изображениями. Вы говорите, что знакомы с GPUImage, поэтому я использовал его (но он может работать и с простым алгоритмом размытия).

С этим вы можете оживить реальное размытие в реальном времени со стоимостью 1-2% CPU

// configuration
let imageCount: Int = 10
let blurMax: Float = 40.0
// keep images in memory
var currentIdx: Int = 0
var imgs: [UIImage] = []

// func that create the precomputed images with GPUImage (call it in the viewDidLoad for example)
func initBlur() {
    let blur = GaussianBlur()
    let myImage: UIImage = UIImage(named: "your_image")!
    let pictureInput = PictureInput(image: myImage)
    let pictureOutput = PictureOutput()
    pictureOutput.onlyCaptureNextFrame = false

    pictureOutput.imageAvailableCallback = { image in
        self.imgs.append(image)
    }

    pictureInput --> blur --> pictureOutput

    for i in 0...imageCount {
        blur.blurRadiusInPixels = (Float(i) / Float(imageCount)) * blurMax
        pictureInput.processImage(synchronously: true)
    }
}

// function that return the correct image from the image set with a blur percentage from a value between 0 and 1 where 0 = no blur and 1 full blurred.
func getBlurredImage(value: Float) -> UIImage? {
    // return nil if there isn't precompiled images
    if imgs.count == 0 {
        return nil
    }

    // get the desired blurred image index
    let idx = Int(value * Float(imageCount - 1))
    // if the index changed, check the correctness of the index
    if currentIdx != idx {
        if idx < 0 {
            currentIdx = 0
        }
        else if idx >= imgs.count {
            currentIdx = imageCount - 1
        }
        else {
            currentIdx = idx
        }
    }
    return imgs[currentIdx]
}

И это он, затем вы можете использовать его, например, с помощью таймера или в функции scrollViewDidScroll, например:

imageView.image = getBlurredImage(value: yOffset / height)