Для всех, кто интересуется, как нарисовать внутреннюю тень с помощью Core Graphics в соответствии с предложением Costique, тогда вот как: (на iOS настройте по мере необходимости)
В вашем методе drawRect: ...
CGRect bounds = [self bounds];
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat radius = 0.5f * CGRectGetHeight(bounds);
CGMutablePathRef visiblePath = CGPathCreateMutable();
CGRect innerRect = CGRectInset(bounds, radius, radius);
CGPathMoveToPoint(visiblePath, NULL, innerRect.origin.x, bounds.origin.y);
CGPathAddLineToPoint(visiblePath, NULL, innerRect.origin.x + innerRect.size.width, bounds.origin.y);
CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, bounds.origin.y, bounds.origin.x + bounds.size.width, innerRect.origin.y, radius);
CGPathAddLineToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, innerRect.origin.y + innerRect.size.height);
CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height, innerRect.origin.x + innerRect.size.width, bounds.origin.y + bounds.size.height, radius);
CGPathAddLineToPoint(visiblePath, NULL, innerRect.origin.x, bounds.origin.y + bounds.size.height);
CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x, bounds.origin.y + bounds.size.height, bounds.origin.x, innerRect.origin.y + innerRect.size.height, radius);
CGPathAddLineToPoint(visiblePath, NULL, bounds.origin.x, innerRect.origin.y);
CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x, bounds.origin.y, innerRect.origin.x, bounds.origin.y, radius);
CGPathCloseSubpath(visiblePath);
UIColor *aColor = [UIColor redColor];
[aColor setFill];
CGContextAddPath(context, visiblePath);
CGContextFillPath(context);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectInset(bounds, -42, -42));
CGPathAddPath(path, NULL, visiblePath);
CGPathCloseSubpath(path);
CGContextAddPath(context, visiblePath);
CGContextClip(context);
aColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f];
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 3.0f, [aColor CGColor]);
[aColor setFill];
CGContextSaveGState(context);
CGContextAddPath(context, path);
CGContextEOFillPath(context);
CGPathRelease(path);
CGPathRelease(visiblePath);
Итак, по сути, это следующие шаги:
- Создай свой путь
- Задайте нужный цвет заливки, добавьте этот путь в контекст и заполните контекст
- Теперь создайте прямоугольник большего размера, ограничивающий видимый путь. Прежде чем закрыть этот путь, добавьте видимый путь. Затем закройте контур, чтобы создать фигуру, из которой вычтена видимая линия. Возможно, вы захотите изучить методы заполнения (ненулевое обмотка четного / нечетного) в зависимости от того, как вы создали эти пути. По сути, чтобы заставить подпути «вычитаться», когда вы складываете их вместе, вам нужно нарисовать их (или, скорее, построить) в противоположных направлениях, одно по часовой стрелке, а другое - против часовой стрелки.
- Затем вам нужно установить видимый путь в качестве обтравочного контура в контексте, чтобы вы не рисовали на экране ничего за его пределами.
- Затем настройте тень в контексте, включая смещение, размытие и цвет.
- Затем заполните большую форму отверстием. Цвет не имеет значения, потому что, если вы все сделали правильно, вы не увидите этого цвета, только тень.
Я знаю, что опаздываю на эту вечеринку, но это помогло бы мне найти в начале моих путешествий ...
Чтобы отдать должное, где следует отметить, это, по сути, модификация разработки Дэниела Торпа решения Costique по вычитанию меньшей области из большей области. Эта версия предназначена для тех, кто использует композицию слоев вместо переопределения
-drawRect:
CAShapeLayer
Класс может быть использован для достижения того же эффекта:CAShapeLayer* shadowLayer = [CAShapeLayer layer]; [shadowLayer setFrame:[self bounds]]; // Standard shadow stuff [shadowLayer setShadowColor:[[UIColor colorWithWhite:0 alpha:1] CGColor]]; [shadowLayer setShadowOffset:CGSizeMake(0.0f, 0.0f)]; [shadowLayer setShadowOpacity:1.0f]; [shadowLayer setShadowRadius:5]; // Causes the inner region in this example to NOT be filled. [shadowLayer setFillRule:kCAFillRuleEvenOdd]; // Create the larger rectangle path. CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path, NULL, CGRectInset(bounds, -42, -42)); // Add the inner path so it's subtracted from the outer path. // someInnerPath could be a simple bounds rect, or maybe // a rounded one for some extra fanciness. CGPathAddPath(path, NULL, someInnerPath); CGPathCloseSubpath(path); [shadowLayer setPath:path]; CGPathRelease(path); [[self layer] addSublayer:shadowLayer];
На этом этапе, если ваш родительский слой не маскируется до своих границ, вы увидите дополнительную область маскирующего слоя по краям слоя. Это будет 42 пикселя черного, если вы просто скопировали пример напрямую. Чтобы избавиться от него, вы можете просто использовать другой
CAShapeLayer
с тем же путем и установить его как маску теневого слоя:CAShapeLayer* maskLayer = [CAShapeLayer layer]; [maskLayer setPath:someInnerPath]; [shadowLayer setMask:maskLayer];
Я сам не тестировал это, но подозреваю, что использование этого подхода в сочетании с растеризацией более производительно, чем переопределение
-drawRect:
.источник
[[UIBezierPath pathWithRect:[shadowLayer bounds]] CGPath]
самый простой выбор.Можно нарисовать внутреннюю тень с помощью Core Graphics, сделав большой прямоугольный путь за пределами границ, вычтя прямоугольный путь размером с границы и заполнив полученный путь «нормальной» тенью.
Однако, поскольку вам нужно объединить его со слоем градиента, я думаю, что более простым решением является создание прозрачного PNG-изображения внутренней тени из 9 частей и растягивания его до нужного размера. Изображение тени из 9 частей будет выглядеть так (его размер 21x21 пиксель):
CALayer *innerShadowLayer = [CALayer layer]; innerShadowLayer.contents = (id)[UIImage imageNamed: @"innershadow.png"].CGImage; innerShadowLayer.contentsCenter = CGRectMake(10.0f/21.0f, 10.0f/21.0f, 1.0f/21.0f, 1.0f/21.0f);
Затем установите кадр innerShadowLayer, и он должен правильно растянуть тень.
источник
Упрощенная версия с использованием только CALayer в Swift:
import UIKit final class FrameView : UIView { init() { super.init(frame: CGRect.zero) backgroundColor = UIColor.white } @available(*, unavailable) required init?(coder decoder: NSCoder) { fatalError("unavailable") } override func layoutSubviews() { super.layoutSubviews() addInnerShadow() } private func addInnerShadow() { let innerShadow = CALayer() innerShadow.frame = bounds // Shadow path (1pt ring around bounds) let path = UIBezierPath(rect: innerShadow.bounds.insetBy(dx: -1, dy: -1)) let cutout = UIBezierPath(rect: innerShadow.bounds).reversing() path.append(cutout) innerShadow.shadowPath = path.cgPath innerShadow.masksToBounds = true // Shadow properties innerShadow.shadowColor = UIColor(white: 0, alpha: 1).cgColor // UIColor(red: 0.71, green: 0.77, blue: 0.81, alpha: 1.0).cgColor innerShadow.shadowOffset = CGSize.zero innerShadow.shadowOpacity = 1 innerShadow.shadowRadius = 3 // Add layer.addSublayer(innerShadow) } }
Обратите внимание, что слой innerShadow не должен иметь непрозрачного цвета фона, поскольку он будет отображаться перед тенью.
источник
let innerShadow = CALayer(); innerShadow.frame = bounds
. Без правильных границ он не смог бы нарисовать правильную тень. В любом случае спасибоlayoutSubviews()
чтобы синхронизироватьlayoutSubviews()
либо вdraw(_ rect)
Немного обходной путь, но он позволяет избежать использования изображений (читай: легко изменить цвета, радиус тени и т.д.), и это всего лишь несколько строк кода.
Добавьте UIImageView в качестве первого подвида UIView, на котором должна отображаться тень. Я использую IB, но вы можете делать то же самое программно.
Предполагая, что ссылка на UIImageView - innerShadow
`
[[innerShadow layer] setMasksToBounds:YES]; [[innerShadow layer] setCornerRadius:12.0f]; [[innerShadow layer] setBorderColor:[UIColorFromRGB(180, 180, 180) CGColor]]; [[innerShadow layer] setBorderWidth:1.0f]; [[innerShadow layer] setShadowColor:[UIColorFromRGB(0, 0, 0) CGColor]]; [[innerShadow layer] setShadowOffset:CGSizeMake(0, 0)]; [[innerShadow layer] setShadowOpacity:1]; [[innerShadow layer] setShadowRadius:2.0];
Предупреждение: у вас должна быть граница, иначе тень не появится. [UIColor clearColor] не работает. В этом примере я использую другой цвет, но вы можете изменить его, чтобы он имел тот же цвет, что и начало тени. :)
См. Комментарий bbrame о
UIColorFromRGB
макросе ниже.источник
Лучше поздно, чем никогда...
Вот еще один подход, вероятно, не лучше тех, что уже были опубликованы, но он приятный и простой -
-(void)drawInnerShadowOnView:(UIView *)view { UIImageView *innerShadowView = [[UIImageView alloc] initWithFrame:view.bounds]; innerShadowView.contentMode = UIViewContentModeScaleToFill; innerShadowView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [view addSubview:innerShadowView]; [innerShadowView.layer setMasksToBounds:YES]; [innerShadowView.layer setBorderColor:[UIColor lightGrayColor].CGColor]; [innerShadowView.layer setShadowColor:[UIColor blackColor].CGColor]; [innerShadowView.layer setBorderWidth:1.0f]; [innerShadowView.layer setShadowOffset:CGSizeMake(0, 0)]; [innerShadowView.layer setShadowOpacity:1.0]; // this is the inner shadow thickness [innerShadowView.layer setShadowRadius:1.5]; }
источник
Вместо того, чтобы рисовать внутреннюю тень с помощью drawRect или добавить UIView в View. Вы можете напрямую добавить CALayer к границе, например: если мне нужен эффект внутренней тени на нижней части UIView V.
innerShadowOwnerLayer = [[CALayer alloc]init]; innerShadowOwnerLayer.frame = CGRectMake(0, V.frame.size.height+2, V.frame.size.width, 2); innerShadowOwnerLayer.backgroundColor = [UIColor whiteColor].CGColor; innerShadowOwnerLayer.shadowColor = [UIColor blackColor].CGColor; innerShadowOwnerLayer.shadowOffset = CGSizeMake(0, 0); innerShadowOwnerLayer.shadowRadius = 10.0; innerShadowOwnerLayer.shadowOpacity = 0.7; [V.layer addSubLayer:innerShadowOwnerLayer];
Это добавит нижнюю внутреннюю тень для целевого UIView
источник
Вот вариант быстрой смены
startPoint
иendPoint
внесения изменений с каждой стороны.let layer = CAGradientLayer() layer.startPoint = CGPointMake(0.5, 0.0); layer.endPoint = CGPointMake(0.5, 1.0); layer.colors = [UIColor(white: 0.1, alpha: 1.0).CGColor, UIColor(white: 0.1, alpha: 0.5).CGColor, UIColor.clearColor().CGColor] layer.locations = [0.05, 0.2, 1.0 ] layer.frame = CGRectMake(0, 0, self.view.frame.width, 60) self.view.layer.insertSublayer(layer, atIndex: 0)
источник
Это ваше решение, которое я экспортировал из PaintCode :
-(void) drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); //// Shadow Declarations UIColor* shadow = UIColor.whiteColor; CGSize shadowOffset = CGSizeMake(0, 0); CGFloat shadowBlurRadius = 10; //// Rectangle Drawing UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: self.bounds]; [[UIColor blackColor] setFill]; [rectanglePath fill]; ////// Rectangle Inner Shadow CGContextSaveGState(context); UIRectClip(rectanglePath.bounds); CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL); CGContextSetAlpha(context, CGColorGetAlpha([shadow CGColor])); CGContextBeginTransparencyLayer(context, NULL); { UIColor* opaqueShadow = [shadow colorWithAlphaComponent: 1]; CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, [opaqueShadow CGColor]); CGContextSetBlendMode(context, kCGBlendModeSourceOut); CGContextBeginTransparencyLayer(context, NULL); [opaqueShadow setFill]; [rectanglePath fill]; CGContextEndTransparencyLayer(context); } CGContextEndTransparencyLayer(context); CGContextRestoreGState(context); }
источник
Я очень опаздываю на вечеринку, но я хотел бы вернуть его сообществу .. Это метод, который я написал для удаления фонового изображения UITextField, поскольку я поставлял статическую библиотеку и НИКАКИХ ресурсов ... Я использовал это для экран ввода ПИН-кода из четырех экземпляров UITextField, которые могут отображать один необработанный символ или (BOOL) [self isUsingBullets] или (BOOL) [self usingAsterisks] в ViewController. Приложение предназначено для iPhone / iPhone Retina / iPad / iPad Retina, поэтому мне не нужно предоставлять четыре изображения ...
#import <QuartzCore/QuartzCore.h> - (void)setTextFieldInnerGradient:(UITextField *)textField { [textField setSecureTextEntry:self.isUsingBullets]; [textField setBackgroundColor:[UIColor blackColor]]; [textField setTextColor:[UIColor blackColor]]; [textField setBorderStyle:UITextBorderStyleNone]; [textField setClipsToBounds:YES]; [textField.layer setBorderColor:[[UIColor blackColor] CGColor]]; [textField.layer setBorderWidth:1.0f]; // make a gradient off-white background CAGradientLayer *gradient = [CAGradientLayer layer]; CGRect gradRect = CGRectInset([textField bounds], 3, 3); // Reduce Width and Height and center layer gradRect.size.height += 2; // minimise Bottom shadow, rely on clipping to remove these 2 pts. gradient.frame = gradRect; struct CGColor *topColor = [UIColor colorWithWhite:0.6f alpha:1.0f].CGColor; struct CGColor *bottomColor = [UIColor colorWithWhite:0.9f alpha:1.0f].CGColor; // We need to use this fancy __bridge object in order to get the array we want. gradient.colors = [NSArray arrayWithObjects:(__bridge id)topColor, (__bridge id)bottomColor, nil]; [gradient setCornerRadius:4.0f]; [gradient setShadowOffset:CGSizeMake(0, 0)]; [gradient setShadowColor:[[UIColor whiteColor] CGColor]]; [gradient setShadowOpacity:1.0f]; [gradient setShadowRadius:3.0f]; // Now we need to Blur the edges of this layer "so it blends" // This rasterizes the view down to 4x4 pixel chunks then scales it back up using bilinear filtering... // it's EXTREMELY fast and looks ok if you are just wanting to blur a background view under a modal view. // To undo it, just set the rasterization scale back to 1.0 or turn off rasterization. [gradient setRasterizationScale:0.25]; [gradient setShouldRasterize:YES]; [textField.layer insertSublayer:gradient atIndex:0]; if (self.usingAsterisks) { [textField setFont:[UIFont systemFontOfSize:80.0]]; } else { [textField setFont:[UIFont systemFontOfSize:40.0]]; } [textField setTextAlignment:UITextAlignmentCenter]; [textField setEnabled:NO]; }
Надеюсь, это кому-то поможет, так как этот форум помог мне.
источник
Посмотрите замечательную статью Криса Эмери Inner Shadows in Quartz , в которой объясняется, как отрисовываются внутренние тени с помощью PaintCode, и дается чистый и аккуратный фрагмент кода:
- (void)drawInnerShadowInContext:(CGContextRef)context withPath:(CGPathRef)path shadowColor:(CGColorRef)shadowColor offset:(CGSize)offset blurRadius:(CGFloat)blurRadius { CGContextSaveGState(context); CGContextAddPath(context, path); CGContextClip(context); CGColorRef opaqueShadowColor = CGColorCreateCopyWithAlpha(shadowColor, 1.0); CGContextSetAlpha(context, CGColorGetAlpha(shadowColor)); CGContextBeginTransparencyLayer(context, NULL); CGContextSetShadowWithColor(context, offset, blurRadius, opaqueShadowColor); CGContextSetBlendMode(context, kCGBlendModeSourceOut); CGContextSetFillColorWithColor(context, opaqueShadowColor); CGContextAddPath(context, path); CGContextFillPath(context); CGContextEndTransparencyLayer(context); CGContextRestoreGState(context); CGColorRelease(opaqueShadowColor); }
источник
Вот мое решение в Swift 4.2. Хотите попробовать?
final class ACInnerShadowLayer : CAShapeLayer { var innerShadowColor: CGColor? = UIColor.black.cgColor { didSet { setNeedsDisplay() } } var innerShadowOffset: CGSize = .zero { didSet { setNeedsDisplay() } } var innerShadowRadius: CGFloat = 8 { didSet { setNeedsDisplay() } } var innerShadowOpacity: Float = 1 { didSet { setNeedsDisplay() } } override init() { super.init() masksToBounds = true contentsScale = UIScreen.main.scale setNeedsDisplay() } override init(layer: Any) { if let layer = layer as? InnerShadowLayer { innerShadowColor = layer.innerShadowColor innerShadowOffset = layer.innerShadowOffset innerShadowRadius = layer.innerShadowRadius innerShadowOpacity = layer.innerShadowOpacity } super.init(layer: layer) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func draw(in ctx: CGContext) { ctx.setAllowsAntialiasing(true) ctx.setShouldAntialias(true) ctx.interpolationQuality = .high let colorspace = CGColorSpaceCreateDeviceRGB() var rect = bounds var radius = cornerRadius if borderWidth != 0 { rect = rect.insetBy(dx: borderWidth, dy: borderWidth) radius -= borderWidth radius = max(radius, 0) } let innerShadowPath = UIBezierPath(roundedRect: rect, cornerRadius: radius).cgPath ctx.addPath(innerShadowPath) ctx.clip() let shadowPath = CGMutablePath() let shadowRect = rect.insetBy(dx: -rect.size.width, dy: -rect.size.width) shadowPath.addRect(shadowRect) shadowPath.addPath(innerShadowPath) shadowPath.closeSubpath() if let innerShadowColor = innerShadowColor, let oldComponents = innerShadowColor.components { var newComponets = Array<CGFloat>(repeating: 0, count: 4) // [0, 0, 0, 0] as [CGFloat] let numberOfComponents = innerShadowColor.numberOfComponents switch numberOfComponents { case 2: newComponets[0] = oldComponents[0] newComponets[1] = oldComponents[0] newComponets[2] = oldComponents[0] newComponets[3] = oldComponents[1] * CGFloat(innerShadowOpacity) case 4: newComponets[0] = oldComponents[0] newComponets[1] = oldComponents[1] newComponets[2] = oldComponents[2] newComponets[3] = oldComponents[3] * CGFloat(innerShadowOpacity) default: break } if let innerShadowColorWithMultipliedAlpha = CGColor(colorSpace: colorspace, components: newComponets) { ctx.setFillColor(innerShadowColorWithMultipliedAlpha) ctx.setShadow(offset: innerShadowOffset, blur: innerShadowRadius, color: innerShadowColorWithMultipliedAlpha) ctx.addPath(shadowPath) ctx.fillPath(using: .evenOdd) } } } }
источник
let ctx = UIGraphicsGetCurrentContext
UIGraphicsGetCurrentContext
для извлечения, когда некоторые представления помещают свой контекст в стек.Масштабируемое решение с использованием CALayer в Swift
С помощью описанного
InnerShadowLayer
вы также можете включить внутренние тени только для определенных краев, исключая другие. (например, вы можете включить внутренние тени только на левом и верхнем краях вашего обзора)Затем вы можете добавить
InnerShadowLayer
в свое представление, используя:init(...) { // ... your initialization code ... super.init(frame: .zero) layer.addSublayer(shadowLayer) } public override func layoutSubviews() { super.layoutSubviews() shadowLayer.frame = bounds }
InnerShadowLayer
реализация/// Shadow is a struct defining the different kinds of shadows public struct Shadow { let x: CGFloat let y: CGFloat let blur: CGFloat let opacity: CGFloat let color: UIColor } /// A layer that applies an inner shadow to the specified edges of either its path or its bounds public class InnerShadowLayer: CALayer { private let shadow: Shadow private let edge: UIRectEdge public init(shadow: Shadow, edge: UIRectEdge) { self.shadow = shadow self.edge = edge super.init() setupShadow() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } public override func layoutSublayers() { updateShadow() } private func setupShadow() { shadowColor = shadow.color.cgColor shadowOpacity = Float(shadow.opacity) shadowRadius = shadow.blur / 2.0 masksToBounds = true } private func updateShadow() { shadowOffset = { let topWidth: CGFloat = 0 let leftWidth = edge.contains(.left) ? shadow.y / 2 : 0 let bottomWidth: CGFloat = 0 let rightWidth = edge.contains(.right) ? -shadow.y / 2 : 0 let topHeight = edge.contains(.top) ? shadow.y / 2 : 0 let leftHeight: CGFloat = 0 let bottomHeight = edge.contains(.bottom) ? -shadow.y / 2 : 0 let rightHeight: CGFloat = 0 return CGSize(width: [topWidth, leftWidth, bottomWidth, rightWidth].reduce(0, +), height: [topHeight, leftHeight, bottomHeight, rightHeight].reduce(0, +)) }() let insets = UIEdgeInsets(top: edge.contains(.top) ? -bounds.height : 0, left: edge.contains(.left) ? -bounds.width : 0, bottom: edge.contains(.bottom) ? -bounds.height : 0, right: edge.contains(.right) ? -bounds.width : 0) let path = UIBezierPath(rect: bounds.inset(by: insets)) let cutout = UIBezierPath(rect: bounds).reversing() path.append(cutout) shadowPath = path.cgPath } }
источник
этот код работал у меня
class InnerDropShadowView: UIView { override func draw(_ rect: CGRect) { //Drawing code let context = UIGraphicsGetCurrentContext() //// Shadow Declarations let shadow: UIColor? = UIColor.init(hexString: "a3a3a3", alpha: 1.0) //UIColor.black.withAlphaComponent(0.6) //UIColor.init(hexString: "d7d7da", alpha: 1.0) let shadowOffset = CGSize(width: 0, height: 0) let shadowBlurRadius: CGFloat = 7.5 //// Rectangle Drawing let rectanglePath = UIBezierPath(rect: bounds) UIColor.groupTableViewBackground.setFill() rectanglePath.fill() ////// Rectangle Inner Shadow context?.saveGState() UIRectClip(rectanglePath.bounds) context?.setShadow(offset: CGSize.zero, blur: 0, color: nil) context?.setAlpha((shadow?.cgColor.alpha)!) context?.beginTransparencyLayer(auxiliaryInfo: nil) do { let opaqueShadow: UIColor? = shadow?.withAlphaComponent(1) context?.setShadow(offset: shadowOffset, blur: shadowBlurRadius, color: opaqueShadow?.cgColor) context!.setBlendMode(.sourceOut) context?.beginTransparencyLayer(auxiliaryInfo: nil) opaqueShadow?.setFill() rectanglePath.fill() context!.endTransparencyLayer() } context!.endTransparencyLayer() context?.restoreGState() } }
источник
Существует некоторый код здесь , который может сделать это для вас. Если вы измените слой в своем представлении (путем переопределения
+ (Class)layerClass
) на JTAInnerShadowLayer, вы можете установить внутреннюю тень на уровне отступа в своем методе инициализации, и он сделает всю работу за вас. Если вы также хотите нарисовать исходный контент, убедитесь, что вы вызываетеsetDrawOriginalImage:yes
слой с отступом. О том, как это работает, написано в блоге .источник
Использование слоя градиента:
UIView * mapCover = [UIView new]; mapCover.frame = map.frame; [view addSubview:mapCover]; CAGradientLayer * vertical = [CAGradientLayer layer]; vertical.frame = mapCover.bounds; vertical.colors = [NSArray arrayWithObjects:(id)[UIColor whiteColor].CGColor, (id)[[UIColor whiteColor] colorWithAlphaComponent:0.0f].CGColor, (id)[[UIColor whiteColor] colorWithAlphaComponent:0.0f].CGColor, (id)[UIColor whiteColor].CGColor, nil]; vertical.locations = @[@0.01,@0.1,@0.9,@0.99]; [mapCover.layer insertSublayer:vertical atIndex:0]; CAGradientLayer * horizontal = [CAGradientLayer layer]; horizontal.frame = mapCover.bounds; horizontal.colors = [NSArray arrayWithObjects:(id)[UIColor whiteColor].CGColor, (id)[[UIColor whiteColor] colorWithAlphaComponent:0.0f].CGColor, (id)[[UIColor whiteColor] colorWithAlphaComponent:0.0f].CGColor, (id)[UIColor whiteColor].CGColor, nil]; horizontal.locations = @[@0.01,@0.1,@0.9,@0.99]; horizontal.startPoint = CGPointMake(0.0, 0.5); horizontal.endPoint = CGPointMake(1.0, 0.5); [mapCover.layer insertSublayer:horizontal atIndex:0];
источник
Есть простое решение - просто нарисуйте нормальную тень и поверните, как это
@objc func shadowView() -> UIView { let shadowView = UIView(frame: .zero) shadowView.backgroundColor = .white shadowView.layer.shadowColor = UIColor.grey.cgColor shadowView.layer.shadowOffset = CGSize(width: 0, height: 2) shadowView.layer.shadowOpacity = 1.0 shadowView.layer.shadowRadius = 4 shadowView.layer.compositingFilter = "multiplyBlendMode" return shadowView } func idtm_addBottomShadow() { let shadow = shadowView() shadow.transform = transform.rotated(by: 180 * CGFloat(Double.pi)) shadow.transform = transform.rotated(by: -1 * CGFloat(Double.pi)) shadow.translatesAutoresizingMaskIntoConstraints = false addSubview(shadow) NSLayoutConstraint.activate([ shadow.leadingAnchor.constraint(equalTo: leadingAnchor), shadow.trailingAnchor.constraint(equalTo: trailingAnchor), shadow.bottomAnchor.constraint(equalTo: bottomAnchor), shadow.heightAnchor.constraint(equalToConstant: 1), ]) }
источник