как нарисовать тень под uiview?

<p>я пытаюсь нарисовать тень под нижним краем <code>uiview</code> в cocoa touch. я понимаю, что должен использовать <code>cgcontextsetshadow()</code> для рисования тени, но руководство по программированию в quartz 2d немного расплывчато:</p><ol> <li>сохранить графическое состояние.</li> <li>вызовите функцию <code>cgcontextsetshadow</code>, передав соответствующие значения.</li> <li>выполните все рисунки, к которым вы хотите применить тени.</li> <li>восстановить состояние графики</li> </ol><p>я пробовал следующее в подклассе <code>uiview</code>:</p><pre><code>- (void)drawrect:(cgrect)rect { cgcontextref currentcontext = uigraphicsgetcurrentcontext(); cgcontextsavegstate(currentcontext); cgcontextsetshadow(currentcontext, cgsizemake(-15, 20), 5); cgcontextrestoregstate(currentcontext); [super drawrect: rect]; } </code></pre><p>... но это не работает для меня, и я немного застрял в (а), куда идти дальше и (б), если есть что-то, что мне нужно сделать с моей <code>uiview</code>, чтобы сделать эту работу?</p>

<p>в своем текущем коде вы сохраняете <code>gstate</code> текущего контекста, настраиваете его для рисования тени ... и восстанавливаете его до того, что было до того, как вы настроили его для рисования тени. затем, наконец, вы вызываете реализацию суперкласса <code>drawrect</code>:.</p><p>любой рисунок, на который должны повлиять настройки тени, должен произойти <i>после</i> </p><pre><code>cgcontextsetshadow(currentcontext, cgsizemake(-15, 20), 5); </code></pre><p>но <i>до</i></p><pre><code>cgcontextrestoregstate(currentcontext); </code></pre><p>так что если вы хотите, чтобы <code>drawrect:</code> суперкласса был «обернут» в тень, то как насчет того, чтобы перестроить свой код следующим образом?</p><pre><code>- (void)drawrect:(cgrect)rect { cgcontextref currentcontext = uigraphicsgetcurrentcontext(); cgcontextsavegstate(currentcontext); cgcontextsetshadow(currentcontext, cgsizemake(-15, 20), 5); [super drawrect: rect]; cgcontextrestoregstate(currentcontext); } </code></pre>

<p>намного более простой подход состоит в том, чтобы установить некоторые атрибуты слоя представления при инициализации:</p><pre><code>self.layer.maskstobounds = no; self.layer.shadowoffset = cgsizemake(-15, 20); self.layer.shadowradius = 5; self.layer.shadowopacity = 0.5; </code></pre><p>вам необходимо импортировать quartzcore.</p><pre><code>#import <quartzcore/quartzcore.h> </code></pre>

<pre><code>self.layer.maskstobounds = no; self.layer.cornerradius = 8; // if you like rounded corners self.layer.shadowoffset = cgsizemake(-15, 20); self.layer.shadowradius = 5; self.layer.shadowopacity = 0.5; </code></pre><p>это замедлит работу приложения. добавление следующей строки может улучшить производительность, если ваш вид визуально прямоугольный:</p><pre><code>self.layer.shadowpath = [uibezierpath bezierpathwithrect:self.bounds].cgpath; </code></pre>

<p>то же решение, но только для того, чтобы напомнить вам: вы можете определить тень прямо в раскадровке.</p><p>пример:</p><p><img src="https://i.stack.imgur.com/bprqv.png" alt="enter image description here"></p>

<p>вы можете попробовать это .... вы можете играть со значениями . <code>shadowradius</code> определяет степень размытия. <code>shadowoffset</code> определяет, куда идет тень.</p><blockquote> <p>swift 2.0</p> </blockquote><pre><code>let radius: cgfloat = demoview.frame.width / 2.0 //change it to .height if you need spread for height let shadowpath = uibezierpath(rect: cgrect(x: 0, y: 0, width: 2.1 * radius, height: demoview.frame.height)) //change 2.1 to amount of spread you need and for height replace the code for height demoview.layer.cornerradius = 2 demoview.layer.shadowcolor = uicolor.blackcolor().cgcolor demoview.layer.shadowoffset = cgsize(width: 0.5, height: 0.4) //here you control x and y demoview.layer.shadowopacity = 0.5 demoview.layer.shadowradius = 5.0 //here your control your blur demoview.layer.maskstobounds = false demoview.layer.shadowpath = shadowpath.cgpath </code></pre><blockquote> <p>swift 3.0</p> </blockquote><pre><code>let radius: cgfloat = demoview.frame.width / 2.0 //change it to .height if you need spread for height let shadowpath = uibezierpath(rect: cgrect(x: 0, y: 0, width: 2.1 * radius, height: demoview.frame.height)) //change 2.1 to amount of spread you need and for height replace the code for height demoview.layer.cornerradius = 2 demoview.layer.shadowcolor = uicolor.black.cgcolor demoview.layer.shadowoffset = cgsize(width: 0.5, height: 0.4) //here you control x and y demoview.layer.shadowopacity = 0.5 demoview.layer.shadowradius = 5.0 //here your control your blur demoview.layer.maskstobounds = false demoview.layer.shadowpath = shadowpath.cgpath </code></pre><blockquote> <p>пример со спредом</p> </blockquote><p> <a href="https://i.stack.imgur.com/z4pwm.png"> <img src="https://i.stack.imgur.com/z4pwm.png" alt="example with spread"> </a> </p><blockquote> <p>чтобы создать основную тень </p> </blockquote><pre><code> demoview.layer.cornerradius = 2 demoview.layer.shadowcolor = uicolor.blackcolor().cgcolor demoview.layer.shadowoffset = cgsizemake(0.5, 4.0); //here your control your spread demoview.layer.shadowopacity = 0.5 demoview.layer.shadowradius = 5.0 //here your control your blur </code></pre><blockquote> <p>базовый пример тени в swift 2.0</p> </blockquote><p> <a href="https://i.stack.imgur.com/ojgbu.png"> <img src="https://i.stack.imgur.com/ojgbu.png" alt="output"> </a> </p>

<p><em>простое и чистое решение с использованием interface builder</em></p><p>добавьте файл с именем uiview.swift в свой проект (или просто вставьте его в любой файл):</p><pre><code>import uikit @ibdesignable extension uiview { /* the color of the shadow. defaults to opaque black. colors created * from patterns are currently not supported. animatable. */ @ibinspectable var shadowcolor: uicolor? { set { layer.shadowcolor = newvalue!.cgcolor } get { if let color = layer.shadowcolor { return uicolor(cgcolor:color) } else { return nil } } } /* the opacity of the shadow. defaults to 0. specifying a value outside the * [0,1] range will give undefined results. animatable. */ @ibinspectable var shadowopacity: float { set { layer.shadowopacity = newvalue } get { return layer.shadowopacity } } /* the shadow offset. defaults to (0, -3). animatable. */ @ibinspectable var shadowoffset: cgpoint { set { layer.shadowoffset = cgsize(width: newvalue.x, height: newvalue.y) } get { return cgpoint(x: layer.shadowoffset.width, y:layer.shadowoffset.height) } } /* the blur radius used to create the shadow. defaults to 3. animatable. */ @ibinspectable var shadowradius: cgfloat { set { layer.shadowradius = newvalue } get { return layer.shadowradius } } } </code></pre><p>тогда это будет доступно в интерфейсном разработчике для каждого представления на панели утилит> инспектор атрибутов:</p><p> <a href="https://i.stack.imgur.com/n5uox.png"> <img src="https://i.stack.imgur.com/n5uox.png" alt="utilities panel"> </a> </p><p>вы можете легко установить тень сейчас.</p><p>заметки:<br> - тень не появится в ib, только во время выполнения.<br> - как сказал мазен кассер</p><blockquote> <p>тем, кто не смог заставить это работать [...], убедитесь, что clip subviews (<code>clipstobounds</code>) не включен</p> </blockquote>

<p>я использую это как часть моих утилит. при этом мы можем не только установить тень, но и получить закругленный угол для любой <code>uiview</code>. также вы можете установить, какой цвет тени вы предпочитаете. обычно черный цвет предпочтительнее, но иногда, когда фон не белый, вам может понадобиться что-то еще. вот что я использую -</p><pre><code>in utils.m + (void)roundedlayer:(calayer *)viewlayer radius:(float)r shadow:(bool)s { [viewlayer setmaskstobounds:yes]; [viewlayer setcornerradius:r]; [viewlayer setbordercolor:[rgb(180, 180, 180) cgcolor]]; [viewlayer setborderwidth:1.0f]; if(s) { [viewlayer setshadowcolor:[rgb(0, 0, 0) cgcolor]]; [viewlayer setshadowoffset:cgsizemake(0, 0)]; [viewlayer setshadowopacity:1]; [viewlayer setshadowradius:2.0]; } return; } </code></pre><p>чтобы использовать это, нам нужно вызвать это - <code>[utils roundedlayer:yourview.layer radius:5.0f shadow:yes];</code></p>

<h1>свифт 3</h1><pre><code>extension uiview { func installshadow() { layer.cornerradius = 2 layer.maskstobounds = false layer.shadowcolor = uicolor.black.cgcolor layer.shadowoffset = cgsize(width: 0, height: 1) layer.shadowopacity = 0.45 layer.shadowpath = uibezierpath(rect: bounds).cgpath layer.shadowradius = 1.0 } } </code></pre>

<p>тем, кто не смог заставить это работать (как я!), попробовав все ответы здесь, просто убедитесь, что<strong> clip subviews </strong>не включен в инспекторе атрибутов ...</p>

<p>если вы хотите использовать storyboard и не хотите продолжать вводить атрибуты времени выполнения, вы можете легко создать расширение для представлений и использовать их в раскадровке.</p><p>шаг 1. создать расширение</p><pre><code>extension uiview { @ibinspectable var shadowradius: cgfloat { get { return layer.shadowradius } set { layer.shadowradius = newvalue } } @ibinspectable var shadowopacity: float { get { return layer.shadowopacity } set { layer.shadowopacity = newvalue } } @ibinspectable var shadowoffset: cgsize { get { return layer.shadowoffset } set { layer.shadowoffset = newvalue } } @ibinspectable var masktobound: bool { get { return layer.maskstobounds } set { layer.maskstobounds = newvalue } } } </code></pre><p>шаг 2. теперь вы можете использовать эти атрибуты в раскадровке <a href="https://i.stack.imgur.com/ulfpr.png"> <img src="https://i.stack.imgur.com/ulfpr.png" alt="storyboard image"> </a> </p>

<p>все отвечать все хорошо, но я хочу добавить еще один момент </p><p>если вы столкнулись с проблемой, когда у вас есть ячейки таблицы, замените новую ячейку на наличие несоответствия в тени, поэтому в этом случае вам нужно поместить свой теневой код в метод layoutsubviews, чтобы он хорошо работал в любых условиях.</p><pre><code>-(void)layoutsubviews{ [super layoutsubviews]; [self.contentview setneedslayout]; [self.contentview layoutifneeded]; [vpshadow applyshadowview:self]; } </code></pre><p>или в viewcontrollers для конкретного вида поместите теневой код в следующий метод, чтобы он работал хорошо</p><pre><code>-(void)viewdidlayoutsubviews{ [super viewdidlayoutsubviews]; [self.viewshadow layoutifneeded]; [vpshadow applyshadowview:self.viewshadow]; } </code></pre><p>я изменил свою теневую реализацию для новых разработчиков для более обобщенной формы ex:</p><pre><code>/*! @brief add shadow to a view. @param layer calayer of the view. */ +(void)applyshadowonview:(calayer *)layer offsetx:(cgfloat)x offsety:(cgfloat)y blur:(cgfloat)radius opacity:(cgfloat)alpha roundingcorners:(cgfloat)cornerradius{ uibezierpath *shadowpath = [uibezierpath bezierpathwithroundedrect:layer.bounds cornerradius:cornerradius]; layer.maskstobounds = no; layer.shadowcolor = [uicolor blackcolor].cgcolor; layer.shadowoffset = cgsizemake(x,y);// shadow x and y layer.shadowopacity = alpha; layer.shadowradius = radius;// blur effect layer.shadowpath = shadowpath.cgpath; } </code></pre>

<p>вы можете использовать мою служебную функцию, созданную для тени и радиуса угла, как показано ниже:</p><pre><code>- (void)addshadowwithradius:(cgfloat)shadowradius withshadowopacity:(cgfloat)shadowopacity withshadowoffset:(cgsize)shadowoffset withshadowcolor:(uicolor *)shadowcolor withcornerradius:(cgfloat)cornerradius withbordercolor:(uicolor *)bordercolor withborderwidth:(cgfloat)borderwidth forview:(uiview *)view{ // drop shadow [view.layer setshadowradius:shadowradius]; [view.layer setshadowopacity:shadowopacity]; [view.layer setshadowoffset:shadowoffset]; [view.layer setshadowcolor:shadowcolor.cgcolor]; // border radius [view.layer setcornerradius:cornerradius]; // border [view.layer setbordercolor:bordercolor.cgcolor]; [view.layer setborderwidth:borderwidth]; } </code></pre><p>надеюсь, это поможет вам!</p>

<p>свифт 3</p><pre><code>self.paddingview.layer.maskstobounds = false self.paddingview.layer.shadowoffset = cgsize(width: -15, height: 10) self.paddingview.layer.shadowradius = 5 self.paddingview.layer.shadowopacity = 0.5 </code></pre>

<p>для поддерживающих xamarians версия ответа xamarin.ios/c # будет выглядеть следующим образом:</p><pre><code>public override void drawrect(cgrect area, uiviewprintformatter formatter) { cgcontext currentcontext = uigraphics.getcurrentcontext(); currentcontext.savestate(); currentcontext.setshadow(new cgsize(-15, 20), 5); base.drawrect(area, formatter); currentcontext.restorestate(); } </code></pre><p>основное отличие состоит в том, что вы получаете экземпляр <code>cgcontext</code>, для которого вы напрямую вызываете соответствующие методы.</p>