UIView katmanında iç gölge efekti?


92

Aşağıdaki CALayer'a sahibim:

CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = CGRectMake(8, 57, 296, 30);
gradient.cornerRadius = 3.0f;
gradient.colors = [NSArray arrayWithObjects:(id)[RGB(130, 0, 140) CGColor], (id)[RGB(108, 0, 120) CGColor], nil];
[self.layer insertSublayer:gradient atIndex:0];

Buna bir iç gölge efekti eklemek istiyorum , ancak bunu nasıl yapacağımdan pek emin değilim. Sanırım drawRect'te çizim yapmam gerekecek, ancak bu katmanı diğer UIView nesnelerinin üstüne ekleyecektir, çünkü bazı düğmelerin arkasında bir çubuk olması gerekiyordu, bu yüzden ne yapacağım konusunda bir kayıp yaşıyorum?

Başka bir katman ekleyebilirdim, ancak yine, iç gölge efektini nasıl elde edeceğimi bilmiyorum (bunun gibi:

görüntü açıklamasını buraya girin

Yardım takdir ...

Yanıtlar:


108

Costique önerisine göre Core Graphics kullanarak nasıl iç gölge çizileceğini merak eden herkes için, o zaman şu şekilde: (iOS'ta gerektiği gibi ayarlayın)

DrawRect'inizde: yöntem ...

CGRect bounds = [self bounds];
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat radius = 0.5f * CGRectGetHeight(bounds);


// Create the "visible" path, which will be the shape that gets the inner shadow
// In this case it's just a rounded rect, but could be as complex as your want
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);

// Fill this path
UIColor *aColor = [UIColor redColor];
[aColor setFill];
CGContextAddPath(context, visiblePath);
CGContextFillPath(context);


// Now create a larger rectangle, which we're going to subtract the visible path from
// and apply a shadow
CGMutablePathRef path = CGPathCreateMutable();
//(when drawing the shadow for a path whichs bounding box is not known pass "CGPathGetPathBoundingBox(visiblePath)" instead of "bounds" in the following line:)
//-42 cuould just be any offset > 0
CGPathAddRect(path, NULL, CGRectInset(bounds, -42, -42));

// Add the visible path (so that it gets subtracted for the shadow)
CGPathAddPath(path, NULL, visiblePath);
CGPathCloseSubpath(path);

// Add the visible paths as the clipping path to the context
CGContextAddPath(context, visiblePath); 
CGContextClip(context);         


// Now setup the shadow properties on the 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]);   

// Now fill the rectangle, so the shadow gets drawn
[aColor setFill];   
CGContextSaveGState(context);   
CGContextAddPath(context, path);
CGContextEOFillPath(context);

// Release the paths
CGPathRelease(path);    
CGPathRelease(visiblePath);

Yani, esasen aşağıdaki adımlar vardır:

  1. Yolunuzu oluşturun
  2. İstediğiniz dolgu rengini ayarlayın, bu yolu bağlama ekleyin ve bağlamı doldurun
  3. Şimdi, görünür yolu sınırlayabilen daha büyük bir dikdörtgen oluşturun. Bu yolu kapatmadan önce görünen yolu ekleyin. Ardından yolu kapatın, böylece ondan çıkarılmış görünür yolla bir şekil oluşturun. Bu yolları nasıl oluşturduğunuza bağlı olarak dolgu yöntemlerini (çift / tek için sıfır olmayan sargı) araştırmak isteyebilirsiniz. Esasen, alt yolları birbirine eklediğinizde "çıkarılmasını" sağlamak için, onları zıt yönlerde, biri saat yönünde ve diğeri saat yönünün tersine çizmeniz (veya daha doğrusu inşa etmeniz) gerekir.
  4. Ardından, görünür yolunuzu bağlamda kırpma yolu olarak ayarlamanız gerekir, böylece ekranın dışına hiçbir şey çizmezsiniz.
  5. Ardından, dengeleme, bulanıklık ve rengi içeren bağlamda gölgeyi ayarlayın.
  6. Sonra büyük şekli içindeki delikle doldurun. Renk önemli değil, çünkü her şeyi doğru yaptıysanız, bu rengi göremezsiniz, sadece gölgeyi.

Teşekkürler, ancak yarıçapı ayarlamak mümkün mü? Şu anda sınırlara dayanıyor, ancak bunun yerine belirli bir yarıçapı temel almak istiyorum (5.0f gibi). Yukarıdaki kodla, çok fazla yuvarlatılmıştır.
runmad

2
@runmad Peki, istediğiniz herhangi bir görünür CGPath oluşturabilirsiniz, burada kullanılan örnek sadece kısalık için seçilen bir örnektir. Yuvarlatılmış bir dikdörtgen oluşturmak istiyorsanız, şu şekilde bir şey yapabilirsiniz: CGPath visiblePath = [UIBezierPath bezierPathWithRoundedRect: rect cornerRadius: radius] .CGPath Yardımcı olacağını umuyoruz.
Daniel Thorpe

4
@DanielThorpe: Güzel cevap için +1. Yuvarlatılmış doğrudan yol kodunu düzelttim (yarıçapı değiştirirken sizinki kırıldı) ve dıştaki düz yol kodunu basitleştirdim. Umarım aldırmazsın
Regexident

İç gölgeyi sadece 2 değil 4 yönden nasıl düzgün bir şekilde kurabilirim?
Protokol

@Protocole ofseti {0,0} olarak ayarlayabilirsiniz, ancak örneğin 4.f gibi bir gölge yarıçapı kullanabilirsiniz.
Daniel Thorpe

47

Bu partiye geç kaldığımı biliyorum ama bu, seyahatlerimde erken bulmama yardımcı olabilirdi ...

Kredinin gerekli olduğu yerde krediyi vermek için, bu esasen Daniel Thorpe'un Costique'in daha büyük bir bölgeden daha küçük bir bölgeyi çıkarma çözümüne ilişkin detaylandırmasının bir değişikliğidir. Bu versiyon, geçersiz kılmak yerine katman kompozisyonu kullananlar içindir.-drawRect:

CAShapeLayerSınıf aynı etkiyi elde etmek için kullanılabilir:

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];

Bu noktada, ana katmanınız sınırlarını maskelemiyorsa, katmanın kenarlarında maske katmanının fazladan alanını görürsünüz. Örneği doğrudan kopyaladıysanız, bu 42 piksel siyah olacaktır. Ondan kurtulmak için, CAShapeLayeraynı yola sahip başka bir tane kullanabilir ve onu gölge katmanının maskesi olarak ayarlayabilirsiniz:

CAShapeLayer* maskLayer = [CAShapeLayer layer];
[maskLayer setPath:someInnerPath];
[shadowLayer setMask:maskLayer];

Bunu kendim kıyaslamadım, ancak bu yaklaşımı rasterleştirme ile birlikte kullanmanın geçersiz kılmaktan daha performanslı olduğundan şüpheleniyorum -drawRect:.


3
someInnerPath? Bunu biraz daha açıklar mısınız lütfen?
Moe

4
@Moe İstediğiniz herhangi bir rastgele CGPath olabilir. [[UIBezierPath pathWithRect:[shadowLayer bounds]] CGPath]en basit seçimdir.
Matt Wilding

Şerefe Matt :-)
Moe

ShadowLayer.path için iç gölgeyi doğru şekilde çizen siyah (dış) bir dikdörtgen alıyorum. Ondan (siyah dış dikdörtgen) nasıl kurtulabilirim? Görünüşe göre fillColor'u yalnızca bir bağlamın içinde ayarlayabiliyorsunuz ve bir tane kullanmıyorsunuz.
Olivier

11
Bu çok güzel çalışıyor! Bazı eklemelerle github'a yükledim. Bir deneyin :) github.com/inamiy/YIInnerShadowView
inamiy

35

Sınırların dışında geniş bir dikdörtgen yol yaparak, sınır boyutlu bir dikdörtgen yolu çıkararak ve ortaya çıkan yolu "normal" bir gölge ile doldurarak Core Graphics ile bir iç gölge çizmek mümkündür.

Bununla birlikte, onu bir gradyan katmanıyla birleştirmeniz gerektiğinden, iç gölgenin 9 parçalı şeffaf bir PNG görüntüsünü oluşturmak ve onu doğru boyuta uzatmak için daha kolay bir çözüm olduğunu düşünüyorum. 9 parçalı gölge görüntü şu şekilde görünecektir (boyutu 21x21 pikseldir):

alternatif metin

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);

Ardından, innerShadowLayer'ın çerçevesini ayarlayın ve gölgeyi düzgün bir şekilde uzatmalıdır.


Evet, sanırım haklısın. Katmanın olabildiğince düz olmasını istedim. Görüntüyü Photoshop'ta iç gölge ve gradyan görünümüyle oluşturabilirim, yalnızca bir görüntüyü kullanırken cihazda% 100 uyan renklerle ilgili sorunlar yaşıyorum.
runmad

Evet, bu tüm gradyanlar ve gölgelerle ilgili bir sorun, bu Photoshop efektlerini 1: 1 iOS'ta denediğim kadar zor bir şekilde yeniden üretemiyorum.
Costique

31

Swift'de sadece bir CALayer kullanan basitleştirilmiş bir sürüm:

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)
    }
}

İç Gölge katmanının, gölgenin önünde oluşturulacağı için opak bir arka plan rengine sahip olmaması gerektiğini unutmayın.


Son satır 'katman' içerir. Bu nereden geliyor?
Charlie Seligman

@CharlieSeligman Herhangi bir katman olabilen ana katmandır. Özel bir katman veya görünümün katmanını kullanabilirsiniz (UIView, bir katman özelliğine sahiptir).
Patrick Pijnappel

olmalıdır let innerShadow = CALayer(); innerShadow.frame = bounds. Uygun sınırlar olmadan uygun gölgeyi çekemezdi. Yine de teşekkürler
haik.ampardjian

@noir_eagle Doğru, muhtemelen layoutSubviews()onu senkronize etmek için ayarlamak
isteseniz de

Sağ! Ya içinde layoutSubviews()ya da içindedraw(_ rect)
haik.ampardjian

24

Biraz yuvarlak bir yol, ancak görüntüleri kullanmak zorunda kalmaz (okuyun: renkleri değiştirmek, gölge yarıçapı, vb.) Ve bu sadece birkaç satır kod.

  1. Drophadow'un açılmasını istediğiniz UIView'ın ilk alt görünümü olarak bir UIImageView ekleyin. IB kullanıyorum, ancak siz de aynısını programlı olarak yapabilirsiniz.

  2. UIImageView başvurusunun 'innerShadow' olduğunu varsayarsak

'

[[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];

Uyarı: Bir sınırınız olmalıdır, aksi takdirde gölge görünmez. [UIColor clearColor] çalışmıyor. Örnekte farklı bir renk kullanıyorum, ancak gölgenin başlangıcıyla aynı renge sahip olmasını sağlamak için onunla uğraşabilirsiniz. :)

UIColorFromRGBMakro hakkında bbrame'in aşağıdaki yorumuna bakın .


Bunu dışarıda bıraktım, ancak bunu görüntü görünümünü eklemenin bir parçası olarak yapacağınızı varsayın - çerçeveyi ana UIView ile aynı doğrultuya ayarladığınızdan emin olun. IB kullanıyorsanız, ana görünümün çerçevesini değiştirecekseniz, dikmeleri ve yayları görünümle birlikte gölge boyutuna sahip olacak şekilde ayarlayın. Kodda, AFAIK'i yapmak için OR yapabileceğiniz bir yeniden boyutlandırma maskesi olmalıdır.
jinglesthula

Şu an için en kolay yol bu, ancak CALayer gölge yöntemlerinin yalnızca iOS 3.2 ve sonraki sürümlerde kullanılabildiğini unutmayın. 3.1'i destekliyorum, bu nedenle bu öznitelikleri bir if ([layer responsesToSelector: @selector (setShadowColor :)]) içinde
ayarlayarak çevreliyorum

Bu benim için işe yaramıyor gibi görünüyor. En azından xcode 4.2 ve ios simulator 4.3'te. Gölgenin görünmesini sağlamak için bir arka plan rengi eklemem gerekiyor ... bu noktada gölge yalnızca dışarıda görünür.
Andrea

@Andrea - yukarıda bahsettiğim uyarıyı aklınızda bulundurun. Bir arka plan renginin veya bir kenarlığın 'ona gölge eklemek için bir şeyler vermek' ile aynı etkiye sahip olabileceğini düşünüyorum. Dışarıda görünmesine gelince, UIImageView, iç gölgeyi istediğiniz birinin bir alt görünümü değilse, bu olabilir - görmek için kodunuza bakmam gerekir.
jinglesthula

Sadece önceki ifademi düzeltmek için ... kod gerçekten çalışıyor ... Bir şeyi kaçırıyordum ama ne yazık ki şu anda hatırlayamıyorum. :) Yani ... bu kod parçacığını paylaştığınız için teşekkürler.
Andrea

17

Geç olsun güç olmasın...

İşte başka bir yaklaşım, muhtemelen daha önce yayınlananlardan daha iyi değil, ama güzel ve basit -

-(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];
}

@SomaMan sadece belirli bir tarafla gölge ayarlamak mümkün mü? Sadece üstte veya üstte / altta veya
yukarı

8

DrawRect ile iç gölge çizmek yerine veya Görünüme UIView ekleyin. CALayer'ı doğrudan sınıra ekleyebilirsiniz, örneğin: UIView V'nin alt kısmında iç gölge efekti istiyorsam.

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];

Bu, hedef UIView için bir alt iç gölge ekler


6

İşte hızlı, değişim startPointve endPointher iki tarafta da yapmanın bir versiyonu .

        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)

Benim için çalıştı !! Teşekkür ederim.
iUser

5

PaintCode'dan dışa aktardığım çözümünüz bu :

-(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);
}

3

Partiye çok geç kaldım ama topluluğa geri vermek istiyorum .. Bu, Statik Kitaplık ve Kaynak YOK sağladığım için UITextField arka plan görüntüsünü kaldırmak için yazdığım bir yöntem ... ViewController'da Bir karakter ham veya (BOOL) [self isUsingBullets] veya (BOOL) [self usingAsterisks] görüntüleyebilen dört UITextField örneğinin PIN Giriş ekranı. Uygulama iPhone / iPhone retina / iPad / iPad Retina içindir, bu yüzden dört görüntü sağlamam gerekmiyor ...

#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];
}

Umarım bu forum bana yardımcı olduğu için bu birine yardımcı olur.


3

Büyük makaleye kontrol Kuvars İç Gölgeler tarafından Chris Emery wich açıklıyor iç gölgeler tarafından çizilir nasıl PaintCode ve temiz ve düzgün kod parçası verecektir:

- (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);
}

3

İşte Swift 4.2'deki çözümüm. Denemek ister misin

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)
      }
    } 
  }
}

Ya onu ayrı bir sınıf olarak kullanmıyorsam, ancak kodumda kullanmak gibi, bunu aldığımda bağlam (ctx) sıfır olur:let ctx = UIGraphicsGetCurrentContext
Mohsin Khubaib Ahmed

@MohsinKhubaibAhmed UIGraphicsGetCurrentContext Bazı görünümler bağlamlarını yığına ittiğinde almak için geçerli bağlamı yöntemle elde edebilirsiniz .
Arco

@Arco Cihazı döndürdüğümde bazı sorunlar yaşadım. 'Kolaylığı geçersiz kılma init (katman: Any) {self.init ()}' ekledim. Artık hiçbir hata görüntülenmiyor!
Yuma Technical Inc.

Çökmeyi düzeltmek için init (katman: Herhangi biri) eklendi.
Nik Kov

2

Swift'de CALayer kullanarak ölçeklenebilir çözüm

Açıklanan ile InnerShadowLayer , diğerlerini hariç tutarak yalnızca belirli kenarlar için iç gölgeleri etkinleştirebilirsiniz. (örneğin, görünümünüzün yalnızca sol ve üst kenarlarında iç gölgeleri etkinleştirebilirsiniz)

Daha sonra aşağıdakileri InnerShadowLayerkullanarak görünümünüze bir ekleyebilirsiniz :

init(...) {

    // ... your initialization code ...

    super.init(frame: .zero)
    layer.addSublayer(shadowLayer)
}

public override func layoutSubviews() {
    super.layoutSubviews()
    shadowLayer.frame = bounds
}

InnerShadowLayer uygulama

/// 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
    }
}


1

bu kod benim için çalıştı

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()
    }
}

0

Burada bunu sizin için yapabilecek bir kod var . Görünümünüzdeki katmanı değiştirirseniz (geçersiz kılarak + (Class)layerClass), JTAInnerShadowLayer olarak değiştirirseniz, init yönteminizdeki girinti katmanındaki iç gölgeyi ayarlayabilirsiniz ve bu sizin için çalışacaktır. Orijinal içeriği de çizmek istiyorsanız setDrawOriginalImage:yes, girinti katmanını çağırdığınızdan emin olun . Burada bunun nasıl çalıştığına dair bir blog yazısı var .


@MiteshDobareeya Her iki bağlantıyı da test ettim ve iyi çalışıyor gibi görünüyorlar (özel bir sekmede dahil). Hangi bağlantı size sorun yaratıyordu?
James Snook

İç gölge kodunun bu uygulamasına bakabilir misiniz? Sadece ViewDidAppear metodunda çalışıyor. Ve biraz titriyor. drive.google.com/open?id=1VtCt7UFYteq4UteT0RoFRjMfFnbibD0E
Mitesh Dobareeya

0

Gradyan Katmanını Kullanma:

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];

0

Basit bir çözüm var - sadece normal gölgeyi çizin ve şu şekilde döndürün

@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),
            ])
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.