it-swarm.com.ru

Как анимировать эффект "размытия по Гауссу" в iOS?

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

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

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

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

enter image description here

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

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

24
user2005643

В 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.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 задает количество выполненного размытия. Если вы оживите это, вы достигнете интерактивного ощущения. 

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

44
Leo Natan

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

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

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

6
matt

Я разработал небольшой проект, который использует 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;
3
michaels

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

Я также призываю вас прочитать блог автора о его дизайнерских решениях для этого класса. Я провел много исследований именно в этой теме с момента выпуска iOS 7, и у меня есть буквально НУЛЕВЫЕ ЖАЛОБЫ об этом коде.

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

3
Sam

Вот функция, которую вы можете использовать для анимации размытия с помощью предварительно рассчитанных изображений. Вы говорите, что знакомы с 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)
1
Anthony