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

<p>для всего ощущения ios 7 я хочу применить эффект размытия к определенной части экрана, чтобы скрыть его, но я не хочу просто мгновенно включать размытие, я хочу анимировать его и анимировать так, пользователь почти видит применяемый эффект размытия.</p><p>почти как в photoshop, вы меняете значение размытия по гауссу побитно с 0 до 10 вместо 0 до 10 за один раз.</p><p>я попробовал несколько решений для этого, наиболее популярное предложение - просто поместить размытое изображение поверх не размытого, а затем понизить альфа-значение размытого изображения.</p><p>это работает <em>ok</em>, но не очень приятно для глаз, так как нет перехода, это просто наложение. пример:</p><p><img src="https://i.stack.imgur.com/cayof.png" alt="enter image description here"></p><p>что может быть лучше для достижения такого эффекта? я знаком с <a href="https://github.com/bradlarson/gpuimage">gpuimage</a> , но не уверен, как этого добиться.</p><p>было бы также здорово, если бы я мог контролировать процент размытия, чтобы он мог применяться в интерактивном режиме от пользователя. (например: пользователь перетаскивает наполовину, размытие наполовину применяется и т. д.)</p>

<p>в ios 9 и выше вы можете анимировать эффект в представлениях визуальных эффектов:</p><pre><code>[uiview animatewithduration:0.3 animations: ^ { visualeffectview.effect = blureffect; } completion:nil]; </code></pre><p>это достигнет ожидаемого эффекта анимации радиуса размытия.</p><hr><p>я думаю, что если вы анимируете кроссфейд из не размытого вида -> размытого вида, это будет достаточно приятно. вам необходимо анимировать отображение размытого вида поверх не размытого вида.</p><p>давайте предположим, что <code>blurview</code> - это вид, содержащий эффект размытия.</p><p>вот два примера того, как выполнить анимированный переход:</p><pre><code>[uiview transitionwithview:self.view duration:0.3 options:uiviewanimationoptiontransitioncrossdissolve animations: ^ { [self.view addsubview:blurview]; } completion:nil]; </code></pre><p>здесь я предполагаю, что <code>blurview</code> уже настроен, и просто переведите добавление как подпредставление в анимации.</p><p>вы также можете достичь этого по-другому:</p><pre><code>blurview.alpha = 0.0f; [uiview animatewithduration:0.3 animations: ^ { blurview.alpha = 1.0f; } completion:nil]; </code></pre><p>здесь я делаю вид размытия прозрачным и оживляю его внешний вид. обратите внимание, что использование этого метода не будет работать с <code>hidden</code>, поэтому вы хотите использовать свойство <code>alpha</code>.</p><hr><p>если вы хотите интерактивно контролировать степень размытия, вот пример того, как этого добиться:</p><p>сначала создайте изображение вида, который вы хотите размыть:</p><pre><code>uigraphicsbeginimagecontextwithoptions(nonblurredview.bounds.size, no, self.view.window.screen.scale); [nonblurredview drawviewhierarchyinrect:nonblurredview.bounds afterscreenupdates:no]; uiimage *snapshotimage = uigraphicsgetimagefromcurrentimagecontext(); </code></pre><p>сохраните это изображение. вы хотите перерисовать аналогичным образом, только если вид был изменен. в противном случае вам следует повторно использовать это изображение, поскольку рисование иерархии обходится дорого.</p><p>теперь, когда вам нужно размыть, используйте следующий код:</p><pre><code>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]; </code></pre><p>вход <code>inputradius</code> задает количество выполненного размытия. если вы оживите это, вы достигнете интерактивного ощущения. </p><p>но я все еще думаю, что первый метод намного проще и будет чувствовать себя так же хорошо для пользователя.</p>

<p>это легко сделать в os x, где одно представление может cifilter влиять на представление позади него, а процессор мощный и быстрый. в ios, однако, нет фильтров компоновки представлений, а размытие занимает некоторое время: вы не можете делать это многократно, вживую, так же быстро, как кадры анимации. поэтому необходима некоторая форма компромисса.</p><p>однако если вы заранее знаете, что вы хотите размыть, вы можете подготовить серию изображений, используя градацию размытия: без размытия, с небольшим размытием, с небольшим размытием и так далее. затем вы собираете изображения и «воспроизводите» их как «кадры» анимированного uiimageview.</p><p>это, однако, я бы не советовал. то, что я хотел бы сделать, это подготовить размытое изображение и не размытое изображение и использовать переход cidissolvetransition для перехода от не размытого к размытому изображению. нет, это не то же самое, что постепенное применение размытия, но это недорогое в вычислительном отношении и лучший «растворяющий» эффект в наборе инструментов.</p>

<p>я разработал небольшой проект, который использует gpuimage для размытия в реальном времени с переменным радиусом размытия и частотой кадров ( <a href="https://github.com/mspensieri/msliveblur">msliveblur</a> ) - это звучит как то, что вам нужно.</p><p>в примере приложения у меня есть ползунок, который увеличивает/уменьшает уровень размытия в ответ на действия пользователя, как вы упомянули в своем вопросе. если вы хотите анимировать его без взаимодействия с пользователем, вы можете сделать таймер, который медленно увеличивает радиус размытия, пока не достигнете конечного значения. вы не сможете использовать coreanimation с этим решением, хотя.</p><pre><code>[[msliveblurview sharedinstance] setblurinterval:0.2]; [[msliveblurview sharedinstance] blurrect:someview.frame]; // count x from 0 to your final radius [msliveblurview sharedinstance].blurradius = x; </code></pre>

<p>я редко предлагаю готовые решения проблем кодирования. однако в этом случае я бы искренне рекомендовал <a href="https://github.com/radi/livefrost">livefrost</a> . это действительно солидный подкласс uiview, ориентированный исключительно на воспроизведение популярного в ios 7 эффекта размытия по гауссу. </p><p>я также призываю вас прочитать блог автора о его дизайнерских решениях для этого класса. я провел много исследований именно в этой теме с момента выпуска ios 7, и у меня есть буквально нулевые жалобы об этом коде.</p><p>он даже имеет анимацию размытия из коробки! удачи тебе :)</p>

<p>вот функция, которую вы можете использовать для анимации размытия с помощью предварительно рассчитанных изображений. вы говорите, что знакомы с gpuimage, поэтому я использовал его (но он может работать и с простым алгоритмом размытия). </p><p>с этим вы можете анимировать<strong> реальное размытие </strong>в<strong> в режиме реального времени </strong>со стоимостью 1-2% cpu</p><pre><code>// 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] } </code></pre><p>и это все, затем вы можете использовать его, например, с таймером или в<strong> <em>scrollviewdidscroll</em> </strong>, например:</p><pre><code>imageview.image = getblurredimage(value: yoffset / height) </code></pre>