Dla każdego, kto zastanawia się, jak narysować wewnętrzny cień za pomocą Core Graphics zgodnie z sugestią Costique, oto jak: (na iOS dostosuj w razie potrzeby)
W twoim drawRect: metoda ...
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);
Tak więc zasadniczo są następujące kroki:
- Stwórz swoją ścieżkę
- Ustaw żądany kolor wypełnienia, dodaj tę ścieżkę do kontekstu i wypełnij kontekst
- Teraz utwórz większy prostokąt, który może wiązać widoczną ścieżkę. Przed zamknięciem tej ścieżki dodaj widoczną ścieżkę. Następnie zamknij ścieżkę, aby utworzyć kształt z odjętą widoczną ścieżką. Możesz chcieć zbadać metody wypełniania (niezerowe nawijanie parzystych / nieparzystych) w zależności od tego, jak utworzyłeś te ścieżki. Zasadniczo, aby ścieżki podrzędne „odejmowały” po dodaniu ich do siebie, należy je narysować (a raczej skonstruować) w przeciwnych kierunkach, jedną zgodnie z ruchem wskazówek zegara, a drugą przeciwnie do ruchu wskazówek zegara.
- Następnie musisz ustawić widoczną ścieżkę jako ścieżkę przycinającą w kontekście, aby nie rysować niczego poza nią na ekranie.
- Następnie ustaw cień w kontekście, który obejmuje przesunięcie, rozmycie i kolor.
- Następnie wypełnij duży kształt otworem w nim. Kolor nie ma znaczenia, ponieważ jeśli zrobiłeś wszystko dobrze, nie zobaczysz tego koloru, tylko cień.
Wiem, że spóźniłem się na to przyjęcie, ale pomogłoby mi to znaleźć na początku moich podróży ...
By przyznać kredyt tam, gdzie należy się kredyt, jest to zasadniczo modyfikacja opracowania Daniela Thorpe'a na temat rozwiązania Costique polegającego na odejmowaniu mniejszego regionu od większego. Ta wersja jest przeznaczona dla osób używających kompozycji warstw zamiast nadpisywania
-drawRect:
CAShapeLayer
Klasy mogą być wykorzystane do osiągnięcia tego samego efektu: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];
W tym momencie, jeśli twoja warstwa nadrzędna nie maskuje do granic, zobaczysz dodatkowy obszar warstwy maski wokół krawędzi warstwy. Będzie to 42 piksele czerni, jeśli bezpośrednio skopiowałeś przykład. Aby się go pozbyć, możesz po prostu użyć innej
CAShapeLayer
z tą samą ścieżką i ustawić ją jako maskę warstwy cienia:CAShapeLayer* maskLayer = [CAShapeLayer layer]; [maskLayer setPath:someInnerPath]; [shadowLayer setMask:maskLayer];
Sam tego nie testowałem, ale podejrzewam, że użycie tego podejścia w połączeniu z rasteryzacją jest bardziej wydajne niż nadpisywanie
-drawRect:
.źródło
[[UIBezierPath pathWithRect:[shadowLayer bounds]] CGPath]
jest najprostszym wyborem.Możliwe jest narysowanie wewnętrznego cienia za pomocą Core Graphics, tworząc dużą prostokątną ścieżkę poza granicami, odejmując prostokątną ścieżkę o rozmiarze granic i wypełniając wynikową ścieżkę „normalnym” cieniem.
Ponieważ jednak musisz połączyć go z warstwą gradientową, myślę, że łatwiejszym rozwiązaniem jest utworzenie 9-częściowego przezroczystego obrazu PNG wewnętrznego cienia i rozciągnięcie go do odpowiedniego rozmiaru. 9-częściowy obraz w cieniu wyglądałby następująco (jego rozmiar to 21x21 pikseli):
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);
Następnie ustaw oprawkę innerShadowLayer, która powinna odpowiednio rozciągnąć cień.
źródło
Uproszczona wersja wykorzystująca tylko CALayer, w 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) } }
Zwróć uwagę, że warstwa innerShadow nie powinna mieć nieprzezroczystego koloru tła, ponieważ zostanie on wyrenderowany przed cieniem.
źródło
let innerShadow = CALayer(); innerShadow.frame = bounds
. Bez odpowiednich granic nie rysowałby właściwego cienia. W każdym razie dziękilayoutSubviews()
aby zachować synchronizacjęlayoutSubviews()
lub wdraw(_ rect)
Trochę okrężny sposób, ale pozwala uniknąć używania obrazów (czytaj: łatwe do zmiany kolorów, promienia cienia itp.) I to tylko kilka wierszy kodu.
Dodaj UIImageView jako pierwszy widok podrzędny UIView, na którym chcesz umieścić cień. Używam IB, ale możesz zrobić to samo programowo.
Zakładając, że odwołanie do UIImageView to „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];
Uwaga: musisz mieć obramowanie, inaczej cień się nie pojawi. [UIColor clearColor] nie działa. W przykładzie używam innego koloru, ale możesz z nim zadzierać, aby miał ten sam kolor, co początek cienia. :)
Zobacz komentarz bbrame poniżej dotyczący
UIColorFromRGB
makra.źródło
Lepiej późno niż wcale...
Oto inne podejście, prawdopodobnie nie lepsze niż te już opublikowane, ale jest ładne i proste -
-(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]; }
źródło
Zamiast rysować wewnętrzny cień przez drawRect lub dodać UIView do widoku. Możesz bezpośrednio dodać CALayer do obramowania, na przykład: jeśli chcę uzyskać efekt wewnętrznego cienia na dnie 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];
Spowoduje to dodanie dolnego wewnętrznego cienia dla docelowego UIView
źródło
Oto wersja szybka, zmiana
startPoint
iendPoint
zrobienie tego z każdej strony.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)
źródło
Oto Twoje rozwiązanie, które wyeksportowałem z 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); }
źródło
Jestem bardzo spóźniony na imprezę, ale chciałbym oddać to społeczności. To jest metoda, którą napisałem, aby usunąć obraz tła UITextField, ponieważ dostarczałem bibliotekę statyczną i BEZ zasobów ... Użyłem tego do ekran wprowadzania kodu PIN zawierający cztery instancje UITextField, które mogą wyświetlać jeden znak surowy lub (BOOL) [self isUsingBullets] lub (BOOL) [self usingAsterisks] w programie ViewController. Aplikacja jest przeznaczona na iPhone / iPhone Retina / iPad / iPad Retina, więc nie muszę dostarczać czterech obrazów ...
#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]; }
Mam nadzieję, że to komuś pomogło, ponieważ to forum pomogło mi.
źródło
Sprawdź świetny artykuł Chrisa Emery'ego z Inner Shadows in Quartz , który wyjaśnia, w jaki sposób cienie wewnętrzne są rysowane przez PaintCode i podaje czysty i schludny fragment kodu:
- (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); }
źródło
Oto moje rozwiązanie w Swift 4.2. Chcesz spróbować?
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) } } } }
źródło
let ctx = UIGraphicsGetCurrentContext
UIGraphicsGetCurrentContext
aby pobrać, gdy niektóre widoki umieszczają swój kontekst na stosie.Skalowalne rozwiązanie wykorzystujące CALayer w Swift
Z opisanym
InnerShadowLayer
możesz również włączyć wewnętrzne cienie tylko dla określonych krawędzi, z wyłączeniem innych. (np. możesz włączyć wewnętrzne cienie tylko na lewej i górnej krawędzi widoku)Następnie możesz dodać
InnerShadowLayer
do widoku za pomocą:init(...) { // ... your initialization code ... super.init(frame: .zero) layer.addSublayer(shadowLayer) } public override func layoutSubviews() { super.layoutSubviews() shadowLayer.frame = bounds }
InnerShadowLayer
realizacja/// 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 } }
źródło
ten kod zadziałał dla mnie
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() } }
źródło
Jest tutaj kod , który może to dla Ciebie zrobić. Jeśli zmienisz warstwę w swoim widoku (przez nadpisanie
+ (Class)layerClass
), na JTAInnerShadowLayer, możesz ustawić wewnętrzny cień na warstwie wcięcia w górę w swojej metodzie init i wykona pracę za Ciebie. Jeśli chcesz również narysować oryginalną zawartość, pamiętaj, aby wywołaćsetDrawOriginalImage:yes
warstwę wcięć. Jest blogu o tym, jak to działa tutaj .źródło
Korzystanie z warstwy gradientu:
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];
źródło
Jest proste rozwiązanie - po prostu narysuj zwykły cień i obróć w ten sposób
@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), ]) }
źródło