मैं एक UIView के तहत छाया कैसे खींचूं?


352

मैं UIViewकोको टच में नीचे के किनारे के नीचे एक छाया खींचने की कोशिश कर रहा हूं । मैं समझता हूं कि मुझे CGContextSetShadow()छाया खींचने के लिए उपयोग करना चाहिए , लेकिन क्वार्ट्ज 2 डी प्रोग्रामिंग गाइड थोड़ा अस्पष्ट है:

  1. ग्राफिक्स राज्य को बचाओ।
  2. CGContextSetShadowउपयुक्त मानों को पास करते हुए फ़ंक्शन को कॉल करें ।
  3. उन सभी ड्राइंग को निष्पादित करें, जिन्हें आप छाया लागू करना चाहते हैं।
  4. ग्राफिक्स राज्य को पुनर्स्थापित करें

मैंने एक UIViewउपवर्ग में निम्नलिखित कोशिश की है :

- (void)drawRect:(CGRect)rect {
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);
    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
    CGContextRestoreGState(currentContext);
    [super drawRect: rect];
}

..लेकिन यह मेरे लिए काम नहीं करता है और मैं थोड़ा अटका हुआ हूं (ए) जहां आगे जाना है और (बी) अगर UIViewइस काम को करने के लिए मुझे कुछ भी करने की जरूरत है ?

जवाबों:


98

अपने वर्तमान कोड में, आप GStateवर्तमान संदर्भ को सहेजते हैं , इसे छाया बनाने के लिए कॉन्फ़िगर करते हैं .. और छाया को आकर्षित करने के लिए इसे कॉन्फ़िगर करने से पहले इसे पुनर्स्थापित करें। फिर, अंत में, आप सुपरक्लास के कार्यान्वयन को लागू करते हैं drawRect:।

किसी भी ड्राइंग जो छाया सेटिंग से प्रभावित होना चाहिए, उसके बाद होना चाहिए

CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);

लेकिन पहले

CGContextRestoreGState(currentContext);

तो अगर आप चाहते हैं कि सुपरक्लास drawRect:एक छाया में 'लिपटा' रहे, तो फिर अगर आप अपने कोड को इस तरह से कैसे व्यवस्थित करेंगे?

- (void)drawRect:(CGRect)rect {
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);
    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
    [super drawRect: rect];
    CGContextRestoreGState(currentContext);
}

789

आरंभिक दृष्टिकोण पर दृष्टिकोण की कुछ परत विशेषताओं को निर्धारित करना है:

self.layer.masksToBounds = NO;
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;

आपको क्वार्ट्जकोर आयात करने की आवश्यकता है।

#import <QuartzCore/QuartzCore.h>

4
लेकिन इस बात का ध्यान रखें कि यह केवल iOS 3.2+ पर काम करता है, इसलिए यदि आपके ऐप को पुराने संस्करण पर काम करना चाहिए तो आपको इस विकल्प के होने पर दृश्य के पीछे ईसाई समाधान या स्थिर छवि का उपयोग करना होगा।
फ्लोरिअनुएर्गर

119
इस समाधान को #import <QuartzCore/QuartzCore.h>".h फ़ाइल में जोड़ने की भी आवश्यकता है ।
मुसिएनेसिस

26
masksToBoundsकरने के लिए सेटिंग NOनकारात्मक cornerRadius, नहीं कर सकते हैं?
पिक्सफ्रीक 22

4
ठीक है, इसे हल करने के लिए, बैकग्राउंडकोलर को परत पर सेट करने की आवश्यकता है और दृश्य को पारदर्शी होने की आवश्यकता है।
23

@pixelfreak आप ऐसा कैसे करते हैं? मैंने कोशिश की self.layer.backgroundColor = [[UIColor whiteColor] CGColor];लेकिन कोई किस्मत नहीं। कौन सा दृश्य पारदर्शी होना चाहिए?
विक्टर वान ही

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

यह एप्लिकेशन को धीमा कर देगा। निम्नलिखित पंक्ति जोड़ने से प्रदर्शन में सुधार हो सकता है जब तक कि आपका दृश्य नेत्रहीन आयताकार है:

self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;

1
यह संभवतः ध्यान देने योग्य है कि यह अनुकूलन केवल तभी उपयोगी है जब आपका दृश्य नेत्रहीन आयताकार हो।
बेंजामिन डोबेल

1
self.layer.shadowPath ... के बजाय क्या? या बस इसे जोड़ने के लिए
Chrizzz

2
बस दूसरों के अलावा उस अतिरिक्त लाइन को जोड़ें।
क्रिस्टोफरकॉटन

11
@ नथनगस्किन - छायांकन करना महंगा ऑपरेशन है, इसलिए उदाहरण के लिए यदि आपका एप्लिकेशन अन्य इंटरफ़ेस ओरिएंटेशन की अनुमति देता है और आप डिवाइस को घुमाना शुरू कर देते हैं, तो स्पष्ट रूप से उस छाया के पथ को निर्दिष्ट किए बिना जिसे उस एनीमेशन के दौरान कई बार प्रस्तुत करना पड़ता है, जो कि निर्भर करता है आकार, संभवत: एनीमेशन को धीमा कर देगा
पीटर पजेल

9
@BenjaminDobell यह विशेष पंक्ति केवल आयतों के लिए काम करती है, लेकिन आप गैर-आयताकार पथ भी बना सकते हैं। उदाहरण के लिए, यदि आपके पास एक गोल आयत है तो आप bezierPathWithRoundedRect: कोनेरेडियस का उपयोग कर सकते हैं:
डैन डायर

160

एक ही समाधान, लेकिन सिर्फ आपको याद दिलाने के लिए: आप छाया को सीधे स्टोरीबोर्ड में परिभाषित कर सकते हैं।

उदाहरण के लिए:

यहां छवि विवरण दर्ज करें


2
अफसोस की बात है, मुझे लगता है कि CGColor स्टोरीबोर्ड से पहुंच से बाहर है :(
Antzi

1
सिर्फ एक श्रेणी को परिभाषित करें UIViewया CGLayerजो UIColorकि CGColorसंपत्ति के लिए एक आवरण है ;)
DeFrenZ

1
इस लिंक का वर्णन है कि यह कैसे करना है: cocoadventures.org/post/104137872754/…
Kamchatka

8
लोगों को कॉपी और पेस्ट करना आसान बनाने के लिए: layer.masksToBound, layer.shadowOffset, layer.shadowRadius, layer.shadowOpacity। टाइपो तुम्हें यहाँ मार देगा।
etayluz

3
layer.shadowOpacity वैल्यू 0.5 नहीं 0,5 होनी चाहिए
डेजर्ट रोज 11

43

आप यह कोशिश कर सकते हैं .... आप मूल्यों के साथ खेल सकते हैं। shadowRadiusतय कलंक की राशि। shadowOffsetहुक्म देता है कि छाया कहां जाए।

स्विफ्ट 2.0

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

स्विफ्ट 3.0

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

प्रसार के साथ उदाहरण

प्रसार के साथ उदाहरण

एक मूल छाया बनाने के लिए

    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

बेसिक शैडो उदाहरण स्विफ्ट 2.0 में

आउटपुट


19

इंटरफ़ेस बिल्डर का उपयोग करके सरल और साफ समाधान

अपनी परियोजना में UIView.swift नाम की एक फ़ाइल जोड़ें (या किसी भी फ़ाइल में इसे पेस्ट करें):

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

तब यह यूटिलिटीज पैनल में प्रत्येक दृश्य के लिए इंटरफ़ेस बिल्डर में उपलब्ध होगा> गुण निरीक्षक:

उपयोगिताएँ पैनल

अब आप आसानी से छाया सेट कर सकते हैं।

नोट:
- आईबी में केवल रनटाइम पर छाया दिखाई नहीं देगी।
- जैसा कि मजन कासर ने कहा

उन लोगों के लिए जो यह काम करने में असफल रहे [...] सुनिश्चित करें कि क्लिप साक्षात्कार ( clipsToBounds) सक्षम नहीं है


यह सही उत्तर है - भविष्य के इंटरफेस के लिए कोड पर कॉन्फ़िगरेशन
क्रेक

यह समाधान मुझे निम्न चेतावनी संदेश (प्रत्येक संपत्ति के लिए एक) के साथ एक गैर-कार्यशील व्यवहार की ओर ले जाता है:Failed to set (shadowColor) user defined inspected property on (UICollectionViewCell): [<UICollectionViewCell> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key shadowColor.
Xvolks

1
मुझे UIKitइसे काम करने के लिए आयात करना पड़ा , FoundationXCode द्वारा बनाया गया मूल आयात पर्याप्त नहीं है, लेकिन ठीक संकलन करता है। मुझे पूरे सोर्स कोड की कॉपी करनी चाहिए थी। इस अच्छे समाधान के लिए धन्यवाद एक्सल।
Xvolks

13

मैं इसे अपने बर्तनों के हिस्से के रूप में उपयोग करता हूं। इसके साथ हम न केवल छाया निर्धारित कर सकते हैं, बल्कि किसी के लिए एक गोल कोना भी प्राप्त कर सकते हैं UIView। इसके अलावा आप यह निर्धारित कर सकते हैं कि आप किस रंग की छाया पसंद करते हैं। आम तौर पर काले रंग को पसंद किया जाता है लेकिन कभी-कभी, जब पृष्ठभूमि गैर-सफेद होती है तो आप कुछ और चाहते हैं। यहाँ मैं क्या उपयोग है -

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

इसका उपयोग करने के लिए हमें इसे कॉल करने की आवश्यकता है - [utils roundedLayer:yourview.layer radius:5.0f shadow:YES];


7

स्विफ्ट 3

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

यदि आप इस पद्धति पर जा रहे थे, तो मैं मापदंडों को जोड़ने पर विचार करूंगा ताकि आप मूल्यों को अपने गतिशील रूप से समायोजित कर सकें
जोश ओ'कॉनर

यह मेरे लिए गोल कोना नहीं दे रहा है, क्या मैं कुछ गलत कर रहा हूं?
निखिल मानपुरे

@ निखिलमनपुरे कुछ नहीं हो रहा है, ऐसा इसलिए हो सकता है क्योंकि इस फंक्शन installShadow()से viewDidLoad()viewWillAppear()
मंगवाया

मैं इसे drawदेखने के अतिभारित तरीके से बुला रहा था । छाया सही ढंग से आ रही है लेकिन गोल कोना नहीं है।
निखिल मनपुरे

@ निखिलमनपूरे की कोई गोल सीमा नहीं है, ऐसा इसलिए हो सकता है क्योंकि दृश्य वह है UIStackViewजो केवल लेआउट करता है और जिसका कोई दृश्य नहीं है। आपको UIViewहर चीज के लिए एक कंटेनर के रूप में एक नियमित सम्मिलित करना पड़ सकता है ।
नीनी

6

यदि आप StoryBoard का उपयोग करना चाहते हैं और रनटाइम विशेषताओं में टाइप करना जारी रखना चाहते हैं, तो आप आसानी से दृश्यों में विस्तार कर सकते हैं और उन्हें स्टोरीबोर्ड में उपयोग करने योग्य बना सकते हैं।

चरण 1. विस्तार बनाएँ

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

चरण 2. अब आप इन विशेषताओं का उपयोग स्टोरीबोर्ड में कर सकते हैंस्टोरीबोर्ड छवि


3

उन लोगों के लिए जो यह काम करने में विफल रहे हैं (खुद के रूप में!) यहां सभी उत्तरों की कोशिश करने के बाद, बस सुनिश्चित करें कि क्लिप सबवेरीज़ इंस्पेक्टर में क्लिप सक्षम नहीं हैं ...


1

स्विफ्ट 3

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

1

आप छाया और कोने त्रिज्या के लिए बनाए गए मेरे उपयोगिता समारोह का उपयोग नीचे कर सकते हैं:

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

आशा है इससे आपकी मदद होगी!!!


1

सभी अच्छे से उत्तर दें लेकिन मैं एक और बात जोड़ना चाहता हूं

यदि आपके पास टेबल सेल होने पर कोई समस्या आती है, तो एक नई सेल को हटाएं क्योंकि छाया में एक बेमेल है इसलिए इस मामले में, आपको अपने छाया कोड को एक लेआउटसुब्यू विधि में रखने की आवश्यकता है ताकि यह सभी परिस्थितियों में अच्छी तरह से व्यवहार करे।

-(void)layoutSubviews{
    [super layoutSubviews];

    [self.contentView setNeedsLayout];
    [self.contentView layoutIfNeeded];
    [VPShadow applyShadowView:self];
}

या विशिष्ट विधि के लिए ViewControllers में निम्न विधि के अंदर छाया कोड है ताकि यह अच्छी तरह से काम कर सके

-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];

    [self.viewShadow layoutIfNeeded];
    [VPShadow applyShadowView:self.viewShadow];
}

मैंने और अधिक सामान्यीकृत रूप पूर्व के लिए नए देवों के लिए अपनी छाया कार्यान्वयन को संशोधित किया है:

/*!
 @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;
}

1

साथी Xamarians के लिए, उत्तर का Xamarin.iOS / C # संस्करण निम्नलिखित की तरह दिखेगा:

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

मुख्य अंतर यह है कि आप एक उदाहरण प्राप्त CGContextकरते हैं, जिस पर आप सीधे उपयुक्त तरीकों को कहते हैं।


1

आप Extensionछाया को जोड़ने के लिए इसका उपयोग कर सकते हैं

extension UIView {

    func addShadow(offset: CGSize, color: UIColor, radius: CGFloat, opacity: Float)
    {
        layer.masksToBounds = false
        layer.shadowOffset = offset
        layer.shadowColor = color.cgColor
        layer.shadowRadius = radius
        layer.shadowOpacity = opacity

        let backgroundCGColor = backgroundColor?.cgColor
        backgroundColor = nil
        layer.backgroundColor =  backgroundCGColor
    }
}

आप इसे पसंद कर सकते हैं

your_Custom_View.addShadow(offset: CGSize(width: 0, height: 1), color: UIColor.black, radius: 2.0, opacity: 1.0)

0

स्केच 4 में स्विफ्ट आईबीडीजेबल और आईबीआईनेस्पेक्टेबल का उपयोग करते हुए

इसका इस्तेमाल कैसे करें

डेमो

स्केच और एक्सकोड साइड साइड से

छाया परीक्षा

कोड

@IBDesignable class ShadowView: UIView {

    @IBInspectable var shadowColor: UIColor? {
        get {
            if let color = layer.shadowColor {
                return UIColor(cgColor: color)
            }
            return nil
        }
        set {
            if let color = newValue {
                layer.shadowColor = color.cgColor
            } else {
                layer.shadowColor = nil
            }
        }
    }

    @IBInspectable var shadowOpacity: Float {
        get {
            return layer.shadowOpacity
        }
        set {
            layer.shadowOpacity = newValue
        }
    }

    @IBInspectable var shadowOffset: CGPoint {
        get {
            return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
        }
        set {
            layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
        }

     }

    @IBInspectable var shadowBlur: CGFloat {
        get {
            return layer.shadowRadius
        }
        set {
            layer.shadowRadius = newValue / 2.0
        }
    }

    @IBInspectable var shadowSpread: CGFloat = 0 {
        didSet {
            if shadowSpread == 0 {
                layer.shadowPath = nil
            } else {
                let dx = -shadowSpread
                let rect = bounds.insetBy(dx: dx, dy: dx)
                layer.shadowPath = UIBezierPath(rect: rect).cgPath
            }
        }
    }
}

आउटपुट

डेमो


आप विशेषताओं से छाया उपसर्गों को हटाने वाले कोड में सुधार कर सकते हैं। शैडो व्यू का उद्देश्य छाया संचालन के बारे में स्पष्ट है। एक और बात आप इस वर्ग में कोने की त्रिज्या जोड़ सकते हैं। (आज में, सबसे छाया विचारों कोने की त्रिज्या शामिल है)
mathema
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.