2012-01-14 6 views
37

[UIBezierPath bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:] उपयोग करके, मैं इस जैसे, एक गोल दृश्य बनाने के लिए कर रहा हूँ:UIBezierPath घटाना पथ

rounded view

मैं इस एक (या किसी और तरीके से) से एक और रास्ता कैसे घटाना सकता है, बनाने के लिए इस तरह एक पथ:

subtracted view

वहाँ किसी भी तरह से मैं कुछ इस तरह कर सकते हैं? स्यूडोकोड:

UIBezierPath *bigMaskPath = [UIBezierPath bezierPathWithRoundedRect:bigView.bounds 
           byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight) 
             cornerRadii:CGSizeMake(18, 18)]; 
UIBezierPath *smallMaskPath = [UIBezierPath bezierPathWithRoundedRect:smalLView.bounds 
            byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight) 
              cornerRadii:CGSizeMake(18, 18)]; 

UIBezierPath *finalPath = [UIBezierPath pathBySubtractingPath:smallMaskPath fromPath:bigMaskPath]; 

उत्तर

52

आप स्ट्रोक के घटाया पथ चाहते हैं, आप अपने दम पर कर रहे हैं। ऐप्पल एक एपीआई प्रदान नहीं करता है जो दूसरे से एक पथ के घटाव (या केवल स्ट्रोक) देता है।

यदि आप केवल घटाए गए पथ को भरना चाहते हैं (जैसा कि आपकी उदाहरण छवि में है), तो आप इसे क्लिपिंग पथ का उपयोग करके कर सकते हैं। हालांकि, आपको एक चाल का उपयोग करना होगा। जब आप क्लिपिंग पथ पर पथ जोड़ते हैं, तो नया क्लिपिंग पथ पुराने क्लिपिंग पथ और अतिरिक्त पथ के चौराहे है। तो यदि आप क्लिपिंग पथ पर smallMaskPath जोड़ते हैं, तो आप केवल smallMaskPath के अंदर क्षेत्र भरने को समाप्त कर देंगे, जो आप चाहते हैं के विपरीत है।

आपको क्या करना है smallMaskPath के साथ मौजूदा क्लिपिंग पथ को अलग करना है। सौभाग्य से, आप उस अजीब घुमावदार नियम का उपयोग करके आसानी से ऐसा कर सकते हैं। आप Quartz 2D Programming Guide में भी अजीब नियम के बारे में पढ़ सकते हैं।

मूल विचार यह है कि हम दो उपपथों के साथ एक कंपाउंड पथ बनाते हैं: आपके smallMaskPath और एक विशाल आयताकार जो आपके smallMaskPath और हर दूसरे पिक्सेल को पूरी तरह से संलग्न करता है जिसे आप भरना चाहते हैं। अजीब नियम के कारण, smallMaskPath के अंदर प्रत्येक पिक्सेल को यौगिक पथ के बाहर के रूप में माना जाएगा, और smallMaskPath के बाहर प्रत्येक पिक्सेल को यौगिक पथ के अंदर माना जाएगा।

तो चलिए इस यौगिक पथ को बनाते हैं। हम विशाल आयत से शुरू करेंगे।

[clipPath appendPath:smallMaskPath]; 

अगला हम even- उपयोग करने के लिए पथ सेट: यह करने के लिए smallMaskPath जोड़कर

UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectInfinite]; 

अब हम यह एक यौगिक पथ में बनाना: और अनंत आयत से कोई आयत अधिक विशाल है अजीब नियम:

clipPath.usesEvenOddFillRule = YES; 

इससे पहले कि हम इस पथ पर क्लिप, हम ग्राफिक्स राज्य ताकि हम कतरन पथ के लिए परिवर्तन पूर्ववत कर सकते हैं बचाने के लिए करना चाहिए जब यह पूरा हो:

CGContextSaveGState(UIGraphicsGetCurrentContext()); { 

अब हम कतरन पथ को संशोधित कर सकते हैं:

[clipPath addClip]; 

और हम bigMaskPath भर सकते हैं:

[[UIColor orangeColor] setFill]; 
    [bigMaskPath fill]; 

अंत में हम ग्राफिक्स राज्य बहाल, कतरन पथ के लिए परिवर्तन नाश:

} CGContextRestoreGState(UIGraphicsGetCurrentContext()); 

यहां सह है डी सब एक साथ आप प्रतिलिपि बनाना चाहते मामले में/चिपकाएं:

UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectInfinite]; 
[clipPath appendPath:smallMaskPath]; 
clipPath.usesEvenOddFillRule = YES; 

CGContextSaveGState(UIGraphicsGetCurrentContext()); { 
    [clipPath addClip]; 
    [[UIColor orangeColor] setFill]; 
    [bigMaskPath fill]; 
} CGContextRestoreGState(UIGraphicsGetCurrentContext()); 
+6

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

+0

आपका जवाब सरल है जब छोटे मास्कपैथ को बड़े पैथमास्क द्वारा पूरी तरह से निहित किया गया है, जैसा कि जेडन 10 के उदाहरण में है। सामान्य मामले में जहां छोटे मास्कपैथ में बड़े मास्कपैथ के बाहर पिक्सल भी शामिल होते हैं, आपका सरल दृष्टिकोण काम नहीं करेगा। –

+5

मुझे वह स्टाइल पसंद है जिसका उपयोग आप ब्रेस्ट के बीच जीस्टेट डालने के लिए करते हैं !! – To1ne

31

यह करना चाहिए, आकार को समायोजित के रूप में आप की तरह:

CGRect outerRect = {0, 0, 200, 200}; 
CGRect innerRect = CGRectInset(outerRect, 30, 30); 

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:outerRect cornerRadius:10]; 

[path appendPath:[UIBezierPath bezierPathWithRoundedRect:innerRect cornerRadius:5]]; 
path.usesEvenOddFillRule = YES; 

[[UIColor orangeColor] set]; 
[path fill]; 

एक और वास्तव में आसान तरीका प्रभाव आप के बाद कर रहे हैं पाने के लिए सिर्फ बाहरी चौराहे को खींचना, रंग बदलना, और उस पर भीतरी को खींचना है।

+0

यह वास्तव में आशाजनक लगता है कि मैं क्या करने की कोशिश कर रहा हूं, हालांकि, UIBezierPath और NSColor मिश्रण नहीं करते क्योंकि एक आईओएस है और दूसरा ओएसएक्स है। क्या आप, मौके से, एक और पूरा उदाहरण है जो आईओएस पर काम करता है? –

+0

एक 'CAShapeLayer' बनाएं और इसे अपने दृश्य के एक उपन्यास के रूप में जोड़ें, फिर नई परत की' पथ' और 'फ्रेम' गुण सेट करें। आप 'UIBezierPath' के' CGPath' संस्करण में 'पथ' प्रॉपर्टी सेट कर सकते हैं, उदा। '[uiBezierPathInstance CGPath]'। – jdc

0

घटाने के बजाय, मैं CGPathCGPathAddLineToPoint और CGPathAddArcToPoint का उपयोग करके हल करने में सक्षम था। यह समान कोड के साथ UIBiezerPath के साथ भी किया जा सकता है।

4

मैं कई ओवरलैपिंग CGPaths के साथ ऐसा करने का तरीका जानने के लिए दीवार के खिलाफ अपना सिर मार रहा हूं। यदि आप एक से अधिक बार ओवरलैप करते हैं, तो यह ऊपर दिए गए समाधानों के साथ फिर से भर जाता है। यह वास्तव में संदर्भ के मिश्रण मोड को साफ़ करने के लिए एकाधिक ओवरलैपिंग पथों के साथ 'घटाना' प्रभाव प्राप्त करने का तरीका दिखाता है।

path.append(cutout.reversing()) 

यह काम करता है क्योंकि डिफ़ॉल्ट भरने नियम non-zero winding rule है:

CGContextSetBlendMode(ctx, kCGBlendModeClear);

39

असल में वहाँ ज्यादातर मामलों के लिए एक बहुत ही सरल तरीका, स्विफ्ट में उदाहरण है।

+2

वाह! सुपर सुरुचिपूर्ण और अद्भुत! –

+0

यह वही है जो मुझे चाहिए था। स्वीकार किए गए उत्तर से आसान तरीका :) – Orgmir

+0

एनएसआरस्पॉन्डर का जवाब अच्छा था, लेकिन यह बहुत अच्छा है! धन्यवाद! – timgcarlson

0

पैट्रिक ने non-zero winding rule का उपयोग करके उपयोगकर्ता NSResponder के उत्तर में सुधार/विकल्प प्रदान किया। यहां स्विफ्ट में एक पूर्ण कार्यान्वयन है जो विस्तारित उत्तर की तलाश में है।

UIGraphicsBeginImageContextWithOptions(CGSize(width: 200, height: 200), false, 0.0) 
let context = UIGraphicsGetCurrentContext() 

let rectPath = UIBezierPath(roundedRect: CGRectMake(0, 0, 200, 200), cornerRadius: 10) 
var cutoutPath = UIBezierPath(roundedRect: CGRectMake(30, 30, 140, 140), cornerRadius: 10) 

rectPath.appendPath(cutoutPath.bezierPathByReversingPath()) 

UIColor.orangeColor().set() 
outerForegroundPath.fill() 

let image = UIGraphicsGetImageFromCurrentImageContext() 
UIGraphicsEndImageContext() 

Here's a gist of this.

आप देख सकते हैं और उत्पादन के साथ चारों ओर खेलने के लिए एक स्टोरीबोर्ड में डाल सकते हैं।

The result

1

@Patrick Pijnappel जवाब का उपयोग करके आप त्वरित परीक्षण

import UIKit 
import PlaygroundSupport 

let view = UIView(frame: CGRect(x: 0, y: 0, width: 375, height: 647)) 
view.backgroundColor = UIColor.green 

let someView = UIView(frame: CGRect(x:50, y: 50, width:250, height:250)) 
someView.backgroundColor = UIColor.red 
view.addSubview(someView) 

let shapeLayer = CAShapeLayer() 
shapeLayer.frame = someView.bounds 
shapeLayer.path = UIBezierPath(roundedRect: someView.bounds, 
           byRoundingCorners: [UIRectCorner.bottomLeft,UIRectCorner.bottomRight] , 
           cornerRadii: CGSize(width: 5.0, height: 5.0)).cgPath 

someView.layer.mask = shapeLayer 
someView.layer.masksToBounds = true 

let rect = CGRect(x:0, y:0, width:200, height:100) 
let cornerRadius:CGFloat = 5 
let subPathSideSize:CGFloat = 25 

let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius) 
let leftSubPath = UIBezierPath(arcCenter: CGPoint(x:0, y:rect.height/2), 
           radius: subPathSideSize/2, startAngle: CGFloat(M_PI_2), endAngle: CGFloat(M_PI + M_PI_2), clockwise: false) 
leftSubPath.close() 

let rightSubPath = UIBezierPath(arcCenter: CGPoint(x:rect.width, y:rect.height/2), 
           radius: subPathSideSize/2, startAngle: CGFloat(M_PI_2), endAngle: CGFloat(M_PI + M_PI_2), clockwise: true) 
rightSubPath.close() 

path.append(leftSubPath) 
path.append(rightSubPath.reversing()) 
path.append(path) 

let mask = CAShapeLayer() 
mask.frame = shapeLayer.bounds 
mask.path = path.cgPath 

shapeLayer.mask = mask 

view 
PlaygroundPage.current.liveView = view 

enter image description here