UIView परत पर आंतरिक छाया प्रभाव?


92

मेरे पास निम्नलिखित CALayer है:

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

मैं इसमें एक आंतरिक छाया प्रभाव जोड़ना चाहूंगा , लेकिन मुझे यह निश्चित नहीं है कि यह कैसे करना है। मुझे लगता है कि मुझे ड्राअरक्ट में आकर्षित करने की आवश्यकता होगी, हालांकि यह अन्य यूआईईयूवाई ऑब्जेक्ट्स के ऊपर की परत को जोड़ देगा, क्योंकि यह कुछ बटन के पीछे एक बार माना जाता है, इसलिए मैं नुकसान में हूं कि क्या करना है?

मैं एक और परत जोड़ सकता था, लेकिन फिर से, यह सुनिश्चित नहीं कर सकता कि आंतरिक छाया प्रभाव कैसे प्राप्त किया जाए (इस तरह:

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

मदद की सराहना की ...

जवाबों:


108

किसी और के लिए सोच रहा था कि कॉस्टयूम सुझाव के अनुसार कोर ग्राफिक्स का उपयोग करके एक आंतरिक छाया कैसे खींचना है, तो यह है कि: (आईओएस समायोजन आवश्यकतानुसार)

आपके ड्राअर में: विधि ...

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

तो, अनिवार्य रूप से निम्नलिखित चरण हैं:

  1. अपना रास्ता बनाएं
  2. इच्छित रंग भरें, इस पथ को संदर्भ में जोड़ें, और संदर्भ भरें
  3. अब एक बड़ा आयत बनाएँ जो दृश्य पथ को बाध्य कर सके। इस पथ को बंद करने से पहले, दृश्य पथ जोड़ें। फिर पथ को बंद करें, ताकि आप इसे से घटाए गए दृश्य पथ के साथ एक आकृति बनाएं। आप इन विधियों को बनाने के तरीके के आधार पर भरण विधियों (सम-विषम की गैर-शून्य वाइंडिंग) की जांच करना चाह सकते हैं। संक्षेप में, जब आप उन्हें एक साथ जोड़ते हैं, तो "सबट्रेक्ट" करने के लिए उपप्रकारों को प्राप्त करने के लिए, आपको उन्हें विपरीत दिशाओं में, एक घड़ी की दिशा में और दूसरे एंटी-क्लॉकवाइज़ को खींचना होगा।
  4. फिर आपको अपने दृश्य पथ को संदर्भ पर क्लिपिंग पथ के रूप में सेट करने की आवश्यकता है, ताकि आप इसे स्क्रीन के बाहर कुछ भी आकर्षित न करें।
  5. फिर संदर्भ पर छाया को सेटअप करें, जिसमें ऑफसेट, ब्लर और रंग शामिल हैं।
  6. फिर इसमें छेद के साथ बड़े आकार को भरें। रंग मायने नहीं रखता, क्योंकि अगर आपने सब कुछ सही किया है, तो आप इस रंग को सिर्फ छाया नहीं देखेंगे।

धन्यवाद, लेकिन क्या त्रिज्या को समायोजित करना संभव है? यह वर्तमान में सीमा पर आधारित है, लेकिन मैं इसके बजाय एक सेट त्रिज्या (5.0f की तरह) पर आधारित करना चाहता हूं। उपरोक्त कोड के साथ, यह बहुत अधिक गोल है।
runmad

2
@runmad खैर, आप किसी भी प्रकार का दृश्यमान सीजीपीथ बना सकते हैं, जिसे आप चाहते हैं, यहाँ उपयोग किया गया उदाहरण सिर्फ एक उदाहरण है, जो संक्षिप्तता के लिए चुना गया है। यदि आप एक गोल आयत बनाना चाहते हैं, तो आप बस कुछ ऐसा कर सकते हैं: CGPath दृश्यपाठ = [UIBezierPath bezierPathWithRoundedRect: rect cornerRadius: radius] .CPPath आशा है कि मदद करता है।
डैनियल थोर्प

4
@DanielThorpe: +1 अच्छे उत्तर के लिए। मैंने राउंडेड रेक्ट पाथ कोड (आपकी त्रिज्या को बदलते समय तोड़ दिया) तय किया और बाहरी रेक्ट पाथ कोड को सरल बनाया। आशा है कि आप बुरा नहीं मानेंगे
रेगेक्सिडेंट

मैं सिर्फ 2 नहीं, 4 दिशाओं से आंतरिक छाया को कैसे ठीक से सेट कर सकता हूं?
प्रोटोकोल

@Protocole आप ऑफ़सेट को {0,0} पर सेट कर सकते हैं, लेकिन साये की छाया त्रिज्या का उपयोग करें, 4.f.
डैनियल थॉर्प

47

मुझे पता है कि मुझे इस पार्टी में देर हो रही है, लेकिन इससे मुझे अपनी यात्रा के बारे में पता लगाने में मदद मिलेगी ...

क्रेडिट देने के लिए जहां क्रेडिट की देयता है, यह अनिवार्य रूप से एक बड़े क्षेत्र से एक छोटे क्षेत्र को घटाने के कॉस्टिक के समाधान पर डैनियल थोर्प के विस्तार का एक संशोधन है। यह संस्करण ओवरराइडिंग के बजाय लेयर कंपोजीशन का उपयोग करने वालों के लिए है-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:


3
someInnerPath? क्या आप समझा सकते हैं कि कृपया थोड़ा और।
मो

4
@ मैं यह किसी भी मनमाने ढंग से CGPath आप चाहते हैं हो सकता है। [[UIBezierPath pathWithRect:[shadowLayer bounds]] CGPath]सबसे आसान विकल्प है।
मैट विल्डिंग

उस मैट के लिए चियर्स :-)
मो

मुझे शैडोलेयर.पथ के लिए एक ब्लैक (बाहरी) आयत मिल रहा है जो आंतरिक छाया को सही ढंग से खींचता है। मैं इसे (काले बाहरी आयत) से कैसे छुटकारा पा सकता हूं? लगता है कि आप केवल एक संदर्भ के अंदर fillColor सेट कर सकते हैं और आप एक का उपयोग नहीं करते हैं।
ओलिवियर

11
यह बहुत अच्छा काम करता है! मैंने कुछ परिवर्धन के साथ जीथूब पर अपलोड किया। एक कोशिश :) है github.com/inamiy/YIInnerShadowView
inamiy

35

सीमाओं के बाहर एक बड़ा आयत पथ बनाकर, कोर ग्राफिक्स के साथ एक आंतरिक छाया खींचना संभव है, एक सीमा-आकार के आयत पथ को घटाकर और परिणामस्वरूप पथ को "सामान्य" छाया के साथ भरना।

हालांकि, चूंकि आपको इसे एक ढाल परत के साथ संयोजित करने की आवश्यकता है, मुझे लगता है कि एक आसान समाधान आंतरिक छाया की 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);

फिर इनरशेडो लेयर के फ्रेम को सेट करें और इसे छाया को ठीक से फैलाना चाहिए।


हाँ, मुझे लगता है कि तुम सही हो। बस परत के रूप में संभव के रूप में फ्लैट होना चाहता था। मैं फ़ोटोशॉप में आंतरिक छाया और ढाल देखो के साथ छवि बना सकता हूं, मेरे पास बस डिवाइस पर 100% मिलान वाले रंगों के साथ समस्या है जब एक छवि का उपयोग किया जाता है।
रनमाड

हां, यह सभी ग्रेडिएंट्स और शैडो के साथ एक समस्या है, मैं सिर्फ इन फ़ोटोशॉप इफेक्ट्स को 1: 1 को iOS पर नहीं दोहरा सकता, जैसा कि मैं कोशिश करता हूं।
कॉस्टयूम

29

स्विफ्ट में सिर्फ एक काइल का उपयोग करके एक सरलीकृत संस्करण:

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

ध्यान दें कि इनरशैडो परत में एक अपारदर्शी पृष्ठभूमि रंग नहीं होना चाहिए क्योंकि यह छाया के सामने प्रस्तुत किया जाएगा।


अंतिम पंक्ति में 'परत' होती है। यह कहां से आता है?
चार्ली सेलिगमैन

@CharlieSeligman यह मूल परत है, जो किसी भी परत की हो सकती है। आप एक कस्टम परत या दृश्य की परत का उपयोग कर सकते हैं (UIView के पास एक परत संपत्ति है)।
पैट्रिक पिजनपेल

होना चाहिए let innerShadow = CALayer(); innerShadow.frame = bounds। उचित सीमा के बिना यह उचित छाया नहीं खींचेगा। वैसे भी धन्यवाद
haik.ampardjian

@noir_eagle यह सच है, हालाँकि आप शायद layoutSubviews()इसे सिंक में रखने के लिए सेट करना चाहते हैं
पैट्रिक पिजनपेल

सही! या तो layoutSubviews()या मेंdraw(_ rect)
haik.ampardjian

24

एक दौर के बारे में थोड़ा सा, लेकिन यह छवियों का उपयोग करने से बचता है (पढ़ें: रंग बदलने के लिए आसान, छाया त्रिज्या, आदि) और यह कोड की केवल कुछ पंक्तियाँ हैं।

  1. UIView के पहले सबव्यू के रूप में UIImageView जोड़ें, जिस पर आप ड्रॉपशेडो चाहते हैं। मैं आईबी का उपयोग करता हूं, लेकिन आप एक ही प्रोग्राम कर सकते हैं।

  2. UIImageView का संदर्भ मानते हुए 'इनरशैडो' है

`

[[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] काम नहीं करता है। उदाहरण में मैं एक अलग रंग का उपयोग करता हूं, लेकिन आप इसे गड़बड़ कर सकते हैं ताकि छाया की शुरुआत के समान रंग हो। :)

UIColorFromRGBमैक्रो के बारे में नीचे ब्रब्रम की टिप्पणी देखें ।


मैंने इसे छोड़ दिया है, लेकिन मान लें कि आप इसे छवि दृश्य को जोड़ने के हिस्से के रूप में करेंगे - यह सुनिश्चित करें कि फ्रेम को मूल रेव्यू के समान ही सेट करें। यदि आप IB का उपयोग कर रहे हैं, तो स्ट्रट्स और स्प्रिंग्स को दृश्य के साथ छाया आकार के लिए सेट करें यदि आप मूल दृश्य के फ़्रेम को बदल रहे हैं। कोड में एक आकार बदलने वाला मुखौटा होना चाहिए जिसे आप कर सकते हैं या वही कर सकते हैं, AFAIK।
जिंगलेशुल्ला

यह अब सबसे आसान तरीका है, लेकिन इस बात का ध्यान रखें कि कैलेयर शैडो विधियां केवल आईओएस 3.2 और बाद में ही उपलब्ध हैं। मैं 3.1 का समर्थन करता हूं, इसलिए मैं इन विशेषताओं को सेट करता हूं अगर ([लेयर रिस्पॉन्ड्सटैक्टर: @selector (setShadowColor :)]) {}
डग डबल

यह मेरे लिए काम नहीं लगता है। कम से कम एक्सकोड 4.2 और आईओएस सिम्युलेटर 4.3 पर। छाया को प्रकट करने के लिए मुझे एक पृष्ठभूमि रंग जोड़ना होगा ... जिस बिंदु पर ड्रॉपशैडो केवल बाहर दिखाई देता है।
एंड्रिया

@ और - मेरे द्वारा बताए गए चेतावनी को ध्यान में रखें। मुझे लगता है कि बैकग्राउंड कलर या बॉर्डर पर 'छाया को जोड़ने के लिए कुछ देने' का समान प्रभाव हो सकता है। जैसा कि बाहर दिखाई दे रहा है, अगर UIImageView उस का एक सबव्यू नहीं है जो आप चाहते हैं कि उस पर आंतरिक छाया हो - तो मुझे देखने के लिए आपके कोड को देखना होगा।
२२:०४ पर जिंगलेशूला

बस अपने पिछले बयान को सुधारने के लिए ... कोड वास्तव में काम करता है ... मुझे कुछ याद आ रहा था लेकिन दुर्भाग्य से मैं इसे अभी याद नहीं कर सकता। :) तो ... इस कोड को साझा करने के लिए धन्यवाद स्निपेट।
एंड्रिया

17

देर आए दुरुस्त आए...

यहाँ एक और दृष्टिकोण है, शायद पहले से पोस्ट किए गए लोगों की तुलना में कोई बेहतर नहीं है, लेकिन यह अच्छा और सरल है -

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

@ सोमामैन केवल विशिष्ट पक्ष के साथ छाया निर्धारित करना संभव है? जैसे केवल ऊपर या नीचे / नीचे या ऊपर / दाएं आदि में ..
मितेश दोबरीया

8

इसके बजाय आंतरिक छाया को आरेखित करके या दृश्य में UIView को जोड़ने के बजाय। आप सीधे सीमा पर कैलेयर को जोड़ सकते हैं, उदाहरण के लिए: यदि मैं यूआईईवीवाई वी के तल पर आंतरिक छाया प्रभाव चाहता हूं।

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 के लिए एक निचले आंतरिक छाया को जोड़ता है


6

यहां प्रत्येक तरफ स्विफ्ट, परिवर्तन 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)

मेरे लिए काम किया !! धन्यवाद।

5

यह आपका समाधान है, जिसे मैंने पेंटकोड से निर्यात किया है :

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

मुझे पार्टी में बहुत देर हो चुकी है, लेकिन मैं समुदाय को वापस देना चाहूंगा .. यह एक तरीका है जिसे मैंने UITextField background Image को हटाने के लिए लिखा है क्योंकि मैं एक स्टेटिक लाइब्रेरी और NO रिसोर्स की आपूर्ति कर रहा था ... मैंने इसके लिए इसका उपयोग किया था। चार UITextField इंस्टेंस की एक पिन एंट्री स्क्रीन, जो ViewController में वन कैरेक्टर रॉ (BOOL) [self isUsingBullets] या (BOOL) [self usingAsterisks] प्रदर्शित कर सकती है। ऐप iPhone / iPhone रेटिना / iPad / iPad रेटिना के लिए है, इसलिए मुझे चार छवियों की आपूर्ति करने की आवश्यकता नहीं है ...

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

मुझे उम्मीद है कि यह किसी की मदद करता है क्योंकि इस मंच ने मेरी मदद की है।


3

महान के लेख की जाँच करें क्वार्ट्ज में इनर छाया द्वारा क्रिस एमरी जो बताते हैं कि कैसे भीतरी छाया द्वारा तैयार कर रहे हैं 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);
}

3

यहां स्विफ्ट 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)
      }
    } 
  }
}

क्या होगा यदि मैं इसे एक अलग वर्ग के रूप में उपयोग नहीं कर रहा हूं, लेकिन मेरे कोड में उपयोग करने की तरह, संदर्भ (ctx) शून्य है जब मुझे यह मिलता है:let ctx = UIGraphicsGetCurrentContext
मोहसिन खुबैब अहमद

@MohsinKhubaibAhmed आप विधि द्वारा वर्तमान संदर्भ प्राप्त कर सकते हैं UIGraphicsGetCurrentContext जब कुछ विचार स्टैक पर उनके संदर्भ को धक्का देते हैं।
आर्को

@ जब मैंने डिवाइस को घुमाया तो मुझे कुछ परेशानी हुई। मैंने 'ओवरराइड सुविधा init (परत: कोई भी) {self.init ()}' जोड़ा। अब कोई त्रुटि प्रदर्शित नहीं हुई!
युमा टेक्निकल इंक।

दुर्घटना को ठीक करने के लिए init (परत: कोई भी) जोड़ा गया।
निक कोव

2

स्विफ्ट में CALayer का उपयोग करके स्केलेबल समाधान

वर्णित के साथ 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
    }
}

1

इस कोड ने मेरे लिए काम किया

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

यहाँ कुछ कोड है जो आपके लिए यह कर सकता है। यदि आप + (Class)layerClassJTAInnerShadowLayer में परत को अपने दृश्य में (ओवरराइड करके ) बदलते हैं, तो आप अपने इनिट विधि में इंडेंट परत पर आंतरिक छाया सेट कर सकते हैं और यह आपके लिए काम करेगा। यदि आप मूल सामग्री को भी खींचना चाहते हैं, तो सुनिश्चित करें कि आप setDrawOriginalImage:yesइंडेंट लेयर पर कॉल करें । यहाँ एक ब्लॉग पोस्ट है कि यह कैसे काम करता है


@MiteshDobareeya बस दोनों लिंक का परीक्षण किया और वे ठीक काम करने के लिए दिखाई देते हैं (एक निजी टैब सहित)। कौन सा लिंक आपको समस्याएं पैदा कर रहा था?
जेम्स स्नुक

क्या आप कृपया आंतरिक छाया कोड के इस कार्यान्वयन को देख सकते हैं। यह केवल ViewDidAppear विधि में काम कर रहा है। और कुछ चंचलता दिखाता है। drive.google.com/open?id=1VtCt7UFYteq4UteT0RoFRjMfFnbibD0E
मितेश Dobareeya

0

ढाल परत का उपयोग:

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

एक सरल उपाय है - बस सामान्य छाया खींचें और घुमाएं, इस तरह

@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),
            ])
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.