मेरे कैलेयर के एंकरपॉइंट को बदलने से दृश्य बदल जाता है


131

मैं इसमें परिवर्तन करना चाहता हूं anchorPoint, लेकिन दृश्य को उसी स्थान पर रखता हूं । मैं कोशिश की है NSLog-ing self.layer.positionऔर self.centerऔर वे दोनों ठहरने anchorPoint में परिवर्तन की परवाह किए बिना ही। फिर भी मेरा विचार चलता है!

यह कैसे करना है पर कोई सुझाव?

self.layer.anchorPoint = CGPointMake(0.5, 0.5);
NSLog(@"center point: %f %f", self.layer.position.x, self.layer.position.y);
self.layer.anchorPoint = CGPointMake(1, 1);
NSLog(@"center point: %f %f", self.layer.position.x, self.layer.position.y);

आउटपुट है:

2009-12-27 20:43:24.161 Type[11289:207] center point: 272.500000 242.500000
2009-12-27 20:43:24.162 Type[11289:207] center point: 272.500000 242.500000

जवाबों:


139

लेयर ज्यामिति और Transforms कोर एनिमेशन प्रोग्रामिंग गाइड की धारा एक CALayer की स्थिति और anchorPoint प्रॉपर्टीज़ के संबंधों को बताते हैं। मूल रूप से, एक परत की स्थिति परत के लंगर के स्थान के संदर्भ में निर्दिष्ट है। डिफ़ॉल्ट रूप से, एक परत का एंकरपॉइंट (0.5, 0.5) है, जो परत के केंद्र में स्थित है। जब आप परत की स्थिति निर्धारित करते हैं, तो आप परत के केंद्र के स्थान को उसके सुपरलेयर के समन्वय प्रणाली में सेट कर रहे हैं।

क्योंकि स्थिति परत के एंकरपॉइंट के सापेक्ष है, उसी स्थिति को बनाए रखते हुए उस एंकर को बदलना परत को स्थानांतरित करता है। इस आंदोलन को रोकने के लिए, आपको नए एंकरप्वाइंट के लिए परत की स्थिति को समायोजित करने की आवश्यकता होगी। एक तरीका मैंने यह किया है कि परत की सीमा को हथियाने के लिए, पुराने और नए एंकरपॉइंट के सामान्यीकृत मानों द्वारा सीमा की चौड़ाई और ऊंचाई को गुणा करें, दो एंकर पॉइंट्स का अंतर लें, और उस अंतर को लेयर की स्थिति में लागू करें।

तुम भी CGPointApplyAffineTransform()अपने UIView के CGAffineTransform का उपयोग करके इस तरह से रोटेशन के लिए खाते में सक्षम हो सकते हैं ।


4
उद्देश्य से-सी में ब्रैड के उत्तर को कैसे लागू किया जा सकता है, यह देखने के लिए मैग्नस से उदाहरण कार्य देखें।
जिम जेफर्स

धन्यवाद, लेकिन मुझे समझ में नहीं आता कि कैसे anchorPointऔर positionएक साथ संबंधित हैं?
dumbfingers

6
@ ss1271 - जैसा कि मैंने ऊपर वर्णन किया है, एक परत का एंकरपॉइंट इसके समन्वय प्रणाली का आधार है। यदि यह (0.5, 0.5) पर सेट है, तो जब आप एक परत के लिए स्थिति निर्धारित करते हैं, तो आप सेट कर रहे हैं कि इसका केंद्र अपनी सुपरलेयर की समन्वय प्रणाली में कहां होगा। यदि आप एन्करपॉइंट को (0.0, 0.5) पर सेट करते हैं, तो पोजिशन सेट करने से सेट हो जाएगा जहां इसके बाएं किनारे का केंद्र होगा। फिर से, Apple के ऊपर-जुड़े प्रलेखन में कुछ अच्छी छवियां हैं (जो मैंने संदर्भ को अद्यतन किया है)।
ब्रैड लार्सन

मैंने मैग्नस द्वारा प्रदान की गई विधि का उपयोग किया। जब मैं NSLog स्थिति और फ्रेम वे सही दिखते हैं, लेकिन मेरा विचार अभी भी स्थानांतरित है। कोई विचार क्यों? यह ऐसा है जैसे नई स्थिति को ध्यान में नहीं रखा गया है।
एलेक्सिस

मेरे मामले में, मैं AVVideoCompositionCoreAnimationTool क्लास की वीडियोकंपिशनCoreAnimationToolWithPostProcessingAsVideoLayer विधि का उपयोग करके परत पर एक एनीमेशन जोड़ना चाहता हूं। क्या होता है, जब मैं इसे y- अक्ष के चारों ओर घुमा रहा हूं, तो मेरी आधी परत छिन्न-भिन्न हो रही है।
nr5

205

मुझे भी यही समस्या थी। जब दृश्य घुमाया जाता है तब भी ब्रैड लार्सन के समाधान ने बहुत अच्छा काम किया। यहाँ उसका समाधान कोड में अनुवादित है।

-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
    CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, 
                                   view.bounds.size.height * anchorPoint.y);
    CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, 
                                   view.bounds.size.height * view.layer.anchorPoint.y);

    newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
    oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);

    CGPoint position = view.layer.position;

    position.x -= oldPoint.x;
    position.x += newPoint.x;

    position.y -= oldPoint.y;
    position.y += newPoint.y;

    view.layer.position = position;
    view.layer.anchorPoint = anchorPoint;
}

और स्विफ्ट समकक्ष:

func setAnchorPoint(anchorPoint: CGPoint, forView view: UIView) {
    var newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y)
    var oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y)

    newPoint = CGPointApplyAffineTransform(newPoint, view.transform)
    oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform)

    var position = view.layer.position
    position.x -= oldPoint.x
    position.x += newPoint.x

    position.y -= oldPoint.y
    position.y += newPoint.y

    view.layer.position = position
    view.layer.anchorPoint = anchorPoint
}

स्विफ्ट 4.x

func setAnchorPoint(anchorPoint: CGPoint, forView view: UIView) {
    var newPoint = CGPoint(x: view.bounds.size.width * anchorPoint.x,
                           y: view.bounds.size.height * anchorPoint.y)


    var oldPoint = CGPoint(x: view.bounds.size.width * view.layer.anchorPoint.x,
                           y: view.bounds.size.height * view.layer.anchorPoint.y)

    newPoint = newPoint.applying(view.transform)
    oldPoint = oldPoint.applying(view.transform)

    var position = view.layer.position
    position.x -= oldPoint.x
    position.x += newPoint.x

    position.y -= oldPoint.y
    position.y += newPoint.y

    view.layer.position = position
    view.layer.anchorPoint = anchorPoint
}

2
एक बार जब मैं आपके उदाहरण को यहाँ देख पा रहा था, तो बिल्कुल सही और पूर्ण समझ में आता है। इसके लिए धन्यवाद!
जिम जेफ़र्स

2
मैं अपने awakeFromNib / initWithFrame विधियों में इस कोड का उपयोग करता हूं, लेकिन यह अभी भी काम नहीं करता है, क्या मुझे डिस्प्ले अपडेट करने की आवश्यकता है? संपादित करें: setNeedsDisplay काम नहीं करता है
एडम कार्टर

2
आप view.bounds का उपयोग क्यों कर रहे हैं? क्या आपको लेयर.बाउंड्स का उपयोग नहीं करना चाहिए?
zakdances

1
मेरे मामले में मान सेट करने के लिए: view.layer.position कुछ भी नहीं बदलता है
एंड्रयूज

5
एफडब्ल्यूआईडब्ल्यू, मुझे काम करने के लिए एक डिस्पैच_सुंक ब्लॉक में सेटअनचोर विधि को लपेटना पड़ा। मुझे यकीन नहीं है कि जैसा कि मैं इसे viewDidLoad के अंदर कह रहा हूं। क्या ViewDidLoad अब मुख्य थ्रेड कतार पर नहीं है?
जॉन फाउलर

44

इसे हल करने की कुंजी फ्रेम संपत्ति का उपयोग करना था, जो अजीब तरह से केवल एक चीज है जो बदल जाती है।

स्विफ्ट 2

let oldFrame = self.frame
self.layer.anchorPoint = CGPointMake(1, 1)
self.frame = oldFrame

स्विफ्ट 3

let oldFrame = self.frame
self.layer.anchorPoint = CGPoint(x: 1, y: 1)
self.frame = oldFrame

फिर मैं अपना आकार परिवर्तन करता हूं, जहां यह एंकरप्ले से तराजू करता है। फिर मुझे पुराने एंकरशिप को पुनर्स्थापित करना होगा;

स्विफ्ट 2

let oldFrame = self.frame
self.layer.anchorPoint = CGPointMake(0.5,0.5)
self.frame = oldFrame

स्विफ्ट 3

let oldFrame = self.frame
self.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.frame = oldFrame

संपादित करें: यदि दृश्य को घुमाया जाता है, तो यह बाहर निकलता है, क्योंकि यदि CGAffineTransform लागू किया गया है, तो फ्रेम संपत्ति अपरिभाषित है।


9
बस यह जोड़ना चाहता था कि यह क्यों काम करता है और सबसे आसान तरीका लगता है: डॉक्स के अनुसार, फ्रेम प्रॉपर्टी एक "कंपोज़्ड" प्रॉपर्टी है: "फ्रेम का मूल्य सीमा, एंकरपॉइंट और पोज़िशन प्रॉपर्टीज़ से लिया गया है।" यही कारण है कि "फ्रेम" संपत्ति एनिमेटेबल नहीं है।
डार्कडस्ट नोव

1
यह ईमानदारी से मेरा पसंदीदा तरीका है। यदि आप स्वतंत्र रूप से सीमा या स्थिति में हेरफेर करने की कोशिश नहीं कर रहे हैं या उनमें से किसी को भी चेतन करते हैं, तो एंकर बिंदु को बदलने से पहले फ्रेम को कैप्चर करना आमतौर पर पर्याप्त होता है। कोई गणित आवश्यक नहीं।
CIFilter

28

मेरे लिए मेरी समझ positionऔर anchorPointसबसे आसान तब थी जब मैंने UIView में अपनी समझ की तुलना फ्रेमवर्क से करना शुरू किया। फ्रेम के साथ एक UIView.origin = (20,30) का अर्थ है कि UIView बाएं से 20 अंक और उसके मूल दृश्य के शीर्ष से 30 अंक है। इस दूरी की गणना यूआईवीवाईई के किस बिंदु से की जाती है? इसकी गणना UIView के ऊपरी-बाएँ कोने से की जाती है।

परत anchorPointमें बिंदु (सामान्यीकृत रूप में यानी 0 से 1 तक) को चिन्हित किया जाता है, जहाँ से इस दूरी की गणना की जाती है, इसलिए परत। पोजिशन = (20, 30) का अर्थ है कि परत anchorPointबाईं ओर से 20 अंक और अपनी मूल परत के ऊपर से 30 अंक है। डिफ़ॉल्ट रूप से एक परत एंकरपॉइंट (0.5, 0.5) है, इसलिए परत के केंद्र में दूरी गणना बिंदु सही है। निम्नलिखित आंकड़ा मेरी बात को स्पष्ट करने में मदद करेगा:

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

anchorPoint यह उस बिंदु के आसपास भी होता है जिसके चारों ओर घुमाव होता है यदि आप परत में परिवर्तन लागू करते हैं।


1
कृपया आप यह बता सकते हैं कि मैं इस एंकर पॉइंट जंपिंग को कैसे हल कर सकता हूं, जो UIView ऑटो लेआउट का उपयोग करके सेट किया गया है, इसका मतलब है कि ऑटो लेआउट के साथ हम फ़्रेम या सीमा गुणों को प्राप्त करने या सेट करने के लिए मान नहीं रहे हैं।
एसजे

17

इतना सरल उपाय है। यह केनी के जवाब पर आधारित है। लेकिन पुराने फ्रेम को लागू करने के बजाय, इसका उपयोग करें और अनुवाद की गणना करने के लिए नया है, फिर उस अनुवाद को केंद्र में लागू करें। यह घुमाए गए दृश्य के साथ भी काम करता है! यहाँ कोड, अन्य समाधानों की तुलना में बहुत सरल है:

func setAnchorPoint(anchorPoint: CGPoint, view: UIView) {
   let oldOrigin = view.frame.origin
   view.layer.anchorPoint = anchorPoint
   let newOrigin = view.frame.origin

   let translation = CGPoint(x: newOrigin.x - oldOrigin.x, y: newOrigin.y - oldOrigin.y)

   view.center = CGPoint(x: view.center.x - translation.x, y: view.center.y - translation.y)
}

2
अच्छी छोटी विधि ... एक जोड़ी मामूली सुधार के साथ ठीक काम करती है: लाइन 3: एंकरपॉइंट में पूंजी पी। पंक्ति 8: TRANS.Y = NEW - OLD
बॉब

2
फिर भी मैं असमंजस में हूँ कि मैं इसका उपयोग कैसे कर सकता हूँ। मुझे उइगेस्ट्रोरोटेशन के साथ अपने दृष्टिकोण को घुमाने के लिए अभी एक ही समस्या का सामना करना पड़ रहा है। मैं अजीब हूं कि मुझे यह तरीका कहां से कहना चाहिए। अगर मैं इशारे से पहचानने वाले हैंडलर को बुला रहा हूं। यह दृश्य को गायब कर देता है।
जेक

3
यह जवाब बहुत प्यारा है मुझे अब मधुमेह है।
जिंककोड

14

जिन लोगों को इसकी आवश्यकता है, उनके लिए यहां स्विफ्ट में मैग्नस का समाधान है:

func setAnchorPoint(anchorPoint: CGPoint, view: UIView) {
    var newPoint: CGPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y)
    var oldPoint: CGPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y)

    newPoint = CGPointApplyAffineTransform(newPoint, view.transform)
    oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform)

    var position: CGPoint = view.layer.position

    position.x -= oldPoint.x
    position.x += newPoint.x

    position.y -= oldPoint.y
    position.y += newPoint.y

    view.setTranslatesAutoresizingMaskIntoConstraints(true)     // Added to deal with auto layout constraints
    view.layer.anchorPoint = anchorPoint
    view.layer.position = position
}

1
के लिए वोट दें view.setTranslatesAutoresizingMaskIntoConstraints(true)। धन्यवाद आदमी
ऑस्कर युंदिनाता

1
view.setTranslatesAutoresizingMaskIntoConstraints(true)ऑटो लेआउट बाधाओं का उपयोग करते समय आवश्यक है।
समिरचेन

1
इस के लिए बहुत बहुत धन्यवाद! translatesAutoresizingMaskIntoConstraintsबस मैं क्या याद आ रही थी, अब तक सीखा है सकता है
Ziga

5

यहाँ OS9 NS7 के लिए OS945711 का उत्तर समायोजित किया गया है। इसके अलावा NSView के पास कोई .centerसंपत्ति नहीं है, NSView का फ्रेम नहीं बदलता है (शायद इसलिए कि NSView डिफ़ॉल्ट रूप से एक कायर के साथ नहीं आते हैं) लेकिन एंकरॉप के बदलने पर कैलेयर फ्रेम की उत्पत्ति बदल जाती है।

func setAnchorPoint(anchorPoint: NSPoint, view: NSView) {
    guard let layer = view.layer else { return }

    let oldOrigin = layer.frame.origin
    layer.anchorPoint = anchorPoint
    let newOrigin = layer.frame.origin

    let transition = NSMakePoint(newOrigin.x - oldOrigin.x, newOrigin.y - oldOrigin.y)
    layer.frame.origin = NSMakePoint(layer.frame.origin.x - transition.x, layer.frame.origin.y - transition.y)
}

4

यदि आप एंकरपॉइंट बदलते हैं, तो इसकी स्थिति भी बदल जाएगी, UNLESS आप की उत्पत्ति शून्य बिंदु है CGPointZero

position.x == origin.x + anchorPoint.x;
position.y == origin.y + anchorPoint.y;

1
आप 0 से 1.0 के बीच स्थिति और एंकरपॉइंट, एंकरपॉइंट तराजू की तुलना नहीं कर सकते
एडवर्ड एंथोनी

4

स्टोरीबोर्ड पर UIView के एंकर पॉइंट राइट को संपादित करें और देखें (स्विफ्ट 3)

यह एक वैकल्पिक समाधान है जो आपको एट्रिब्यूट्स इंस्पेक्टर के माध्यम से एंकर बिंदु को बदलने की अनुमति देता है और पुष्टि के लिए एंकर बिंदु को देखने के लिए एक और संपत्ति है।

अपने प्रोजेक्ट में शामिल करने के लिए नई फ़ाइल बनाएँ

import UIKit

@IBDesignable
class UIViewAnchorPoint: UIView {

    @IBInspectable var showAnchorPoint: Bool = false
    @IBInspectable var anchorPoint: CGPoint = CGPoint(x: 0.5, y: 0.5) {
        didSet {
            setAnchorPoint(anchorPoint: anchorPoint)
        }
    }

    override func draw(_ rect: CGRect) {
        if showAnchorPoint {
            let anchorPointlayer = CALayer()
            anchorPointlayer.backgroundColor = UIColor.red.cgColor
            anchorPointlayer.bounds = CGRect(x: 0, y: 0, width: 6, height: 6)
            anchorPointlayer.cornerRadius = 3

            let anchor = layer.anchorPoint
            let size = layer.bounds.size

            anchorPointlayer.position = CGPoint(x: anchor.x * size.width, y: anchor.y * size.height)
            layer.addSublayer(anchorPointlayer)
        }
    }

    func setAnchorPoint(anchorPoint: CGPoint) {
        var newPoint = CGPoint(x: bounds.size.width * anchorPoint.x, y: bounds.size.height * anchorPoint.y)
        var oldPoint = CGPoint(x: bounds.size.width * layer.anchorPoint.x, y: bounds.size.height * layer.anchorPoint.y)

        newPoint = newPoint.applying(transform)
        oldPoint = oldPoint.applying(transform)

        var position = layer.position
        position.x -= oldPoint.x
        position.x += newPoint.x

        position.y -= oldPoint.y
        position.y += newPoint.y

        layer.position = position
        layer.anchorPoint = anchorPoint
    }
}

स्टोरीबोर्ड में दृश्य जोड़ें और कस्टम क्लास सेट करें

कस्टम वर्ग

अब UIView के लिए न्यू एंकर पॉइंट सेट करें

प्रदर्शन

शो एंकर पॉइंट को चालू करने से लाल बिंदु दिखाई देगा ताकि आप बेहतर तरीके से देख सकें कि एंकर पॉइंट नेत्रहीन कहाँ होगा। आप इसे हमेशा बाद में बंद कर सकते हैं।

यह वास्तव में मेरी मदद करता था जब नियोजन यूविवि पर बदल जाता था।


UIView पर कोई भी डॉट नहीं है, मैंने स्विफ्ट के साथ Objective-C का उपयोग किया, अपने कोड से UIView कस्टम क्लास अटैच किया और Show anch पर स्विच किया ... लेकिन कोई भी डॉट
Genevios

1

स्विफ्ट 3 के लिए:

func setAnchorPoint(_ anchorPoint: CGPoint, forView view: UIView) {
    var newPoint = CGPoint(x: view.bounds.size.width * anchorPoint.x, y: view.bounds.size.height * anchorPoint.y)
    var oldPoint = CGPoint(x: view.bounds.size.width * view.layer.anchorPoint.x, y: view.bounds.size.height * view.layer.anchorPoint.y)

    newPoint = newPoint.applying(view.transform)
    oldPoint = oldPoint.applying(view.transform)

    var position = view.layer.position
    position.x -= oldPoint.x
    position.x += newPoint.x

    position.y -= oldPoint.y
    position.y += newPoint.y

    view.layer.position = position
    view.layer.anchorPoint = anchorPoint
}

1
इसके लिए धन्यवाद :) मैंने इसे अपनी परियोजना में एक स्थिर कवक के रूप में चिपकाया और एक आकर्षण की तरह काम करता है: D
Kjell

0

मैग्नस के महान और गहन उत्तर पर विस्तार करते हुए, मैंने एक संस्करण बनाया है जो उप परतों पर काम करता है:

-(void)setAnchorPoint:(CGPoint)anchorPoint forLayer:(CALayer *)layer
{
    CGPoint newPoint = CGPointMake(layer.bounds.size.width * anchorPoint.x, layer.bounds.size.height * anchorPoint.y);
    CGPoint oldPoint = CGPointMake(layer.bounds.size.width * layer.anchorPoint.x, layer.bounds.size.height * layer.anchorPoint.y);
    CGPoint position = layer.position;
    position.x -= oldPoint.x;
    position.x += newPoint.x;
    position.y -= oldPoint.y;
    position.y += newPoint.y;
    layer.position = position;
    layer.anchorPoint = anchorPoint;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.