जब एक ऑटो लेआउट का उपयोग किया जा रहा है, तो मैं एक CALayer के लंगर बिंदु को कैसे समायोजित करूं?


161

नोट : यह प्रश्न पूछे जाने के बाद से चीजें आगे बढ़ी हैं; एक अच्छे हालिया अवलोकन के लिए यहां देखें ।


ऑटो लेआउट से पहले, आप फ्रेम को स्टोर करके, एंकर पॉइंट सेट करके, और फ्रेम को पुनर्स्थापित करके दृश्य को स्थानांतरित किए बिना किसी दृश्य की परत के एंकर पॉइंट को बदल सकते हैं।

एक ऑटो लेआउट की दुनिया में, हम किसी भी अधिक फ्रेम सेट नहीं करते हैं, लेकिन बाधाओं को देखने के स्थान को वापस समायोजित करने के कार्य के लिए प्रतीत नहीं होता है जहां हम इसे चाहते हैं। आप अपने दृष्टिकोण को बदलने के लिए बाधाओं को हैक कर सकते हैं, लेकिन रोटेशन या अन्य आकार बदलने की घटनाओं पर, ये फिर से अमान्य हो जाते हैं।

निम्नलिखित उज्ज्वल विचार काम नहीं करता है क्योंकि यह "लेआउट विशेषताओं (बाएं और चौड़ाई) की अमान्य जोड़ी" बनाता है:

layerView.layer.anchorPoint = CGPointMake(1.0, 0.5);
// Some other size-related constraints here which all work fine...
[self.view addConstraint:
    [NSLayoutConstraint constraintWithItem:layerView
                                 attribute:NSLayoutAttributeLeft
                                 relatedBy:NSLayoutRelationEqual 
                                    toItem:layerView 
                                 attribute:NSLayoutAttributeWidth 
                                multiplier:0.5 
                                  constant:20.0]];

यहां मेरा इरादा बाएं किनारे को सेट करने का था layerView, एडजस्टेड एंकर पॉइंट के साथ, इसकी चौड़ाई के आधे हिस्से के साथ प्लस 20 (सुपरवेव के बाएं किनारे से इनसेट मैं चाहता हूं)।

क्या ऑटो लेआउट के साथ रखी गई दृश्य में, दृश्य के स्थान को बदलने के बिना, एंकर बिंदु को बदलना संभव है? क्या मुझे हार्डककोड मानों का उपयोग करने और हर घुमाव पर बाधा को संपादित करने की आवश्यकता है? मैं आशा नहीं करता।

मुझे एंकर बिंदु को बदलने की आवश्यकता है ताकि जब मैं दृश्य में परिवर्तन लागू करूं, मुझे सही दृश्य प्रभाव मिले।


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

इसमें कवर किया गया है //Size-related constraints that work fine- लेयर व्यू की चौड़ाई और ऊंचाई सुपरवाइज से ली गई है।
जर्थर्टन

जवाबों:


361

[संपादित करें: चेतावनी: संपूर्ण आगामी चर्चा संभवतः आईओएस 8 के द्वारा कम या कम से कम भारी रूप से कम कर दी जाएगी, जो उस समय लेआउट को ट्रिगर करने की गलती नहीं कर सकती है कि एक दृश्य परिवर्तन लागू किया जाता है।]

Autolayout बनाम रूपांतरण देखें

ऑटोलैयूट दृश्य रूपांतरों के साथ अच्छी तरह से नहीं खेलता है। कारण, जहाँ तक मैं समझ सकता हूँ, यह है कि आप एक ऐसे दृश्य के फ्रेम के साथ गड़बड़ नहीं कर रहे हैं जिसमें एक परिवर्तन है (डिफ़ॉल्ट पहचान परिवर्तन के अलावा) - लेकिन यह वही है जो ऑटोलेयूट करता है। जिस तरह से ऑटोलैयट काम करता है वह यह है कि layoutSubviewsरनटाइम में सभी बाधाओं के माध्यम से डैशिंग आता है और सभी विचारों के फ्रेम को तदनुसार सेट करता है।

दूसरे शब्दों में, बाधाएं जादू नहीं हैं; वे सिर्फ एक करने के लिए सूची है। layoutSubviewsवह जगह है जहां टू-डू सूची हो जाती है। और यह फ्रेम सेट करके करता है।

मैं बग के रूप में इस बारे में मदद नहीं कर सकता। अगर मैं इस परिवर्तन को एक दृश्य में लागू करता हूं:

v.transform = CGAffineTransformMakeScale(0.5,0.5);

मुझे उम्मीद है कि पहले और आधे आकार में इसके केंद्र के साथ दृश्य दिखाई देगा। लेकिन इसकी बाधाओं के आधार पर, यह वह नहीं हो सकता है जो मैं देख रहा हूं।

[वास्तव में, यहां एक दूसरा आश्चर्य है: एक दृश्य में परिवर्तन को लागू करना लेआउट को तुरंत ट्रिगर करता है। यह मुझे एक और बग लगता है। या शायद यह पहली बग का दिल है। मुझे क्या उम्मीद होगी कि लेआउट समय तक कम से कम एक परिवर्तन के साथ दूर होने में सक्षम हो, जैसे कि डिवाइस को घुमाया जाता है - जैसे मैं लेआउट समय तक फ्रेम एनीमेशन के साथ दूर हो सकता हूं। लेकिन वास्तव में लेआउट का समय तत्काल है, जो गलत लगता है।]

समाधान 1: कोई बाधा नहीं

एक वर्तमान समाधान है, अगर मैं एक दृश्य के लिए एक अर्धचालक परिवर्तन लागू करने जा रहा हूं (और न केवल इसे किसी तरह अस्थायी रूप से बंद कर दूं), जिससे सभी बाधाओं को दूर किया जा सके। दुर्भाग्य से यह आम तौर पर दृश्य को स्क्रीन से गायब करने का कारण बनता है, क्योंकि ऑटोलॉयट अभी भी होता है, और अब हमें यह बताने के लिए कोई बाधा नहीं है कि दृश्य कहां रखा जाए। इसलिए बाधाओं को दूर करने के अलावा, मैंने व्यू translatesAutoresizingMaskIntoConstraintsको YES पर सेट किया । अब दृश्य पुराने तरीके से काम करता है, प्रभावी रूप से ऑटोलेयआउट द्वारा अप्रभावित है। (यह ऑटोलैयूट से प्रभावित है, जाहिर है, लेकिन निहित ऑटोरेस्पोन्ग मुखौटा बाधाओं के कारण इसका व्यवहार वैसा ही हो सकता है जैसा कि ऑटोलॉययट से पहले था।)

समाधान 2: केवल उचित बाधाओं का उपयोग करें

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

NSMutableArray* cons = [NSMutableArray array];
for (NSLayoutConstraint* con in self.view.constraints)
    if (con.firstItem == self.otherView || con.secondItem == self.otherView)
        [cons addObject:con];

[self.view removeConstraints:cons];
[self.otherView removeConstraints:self.otherView.constraints];
[self.view addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:self.otherView.center.x]];
[self.view addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:self.otherView.center.y]];
[self.otherView addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeWidth relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.width]];
[self.otherView addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.height]];

अपशॉट यह है कि यदि आपके पास कोई अवरोध नहीं है जो किसी दृश्य के फ्रेम को प्रभावित करता है, तो ऑटोलॉययट दृश्य के फ्रेम को स्पर्श नहीं करेगा - जो कि आपके द्वारा परिवर्तन के बाद बस यही है।

समाधान 3: एक सबव्यू का उपयोग करें

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

यहाँ एक उदाहरण है:

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

सफेद दृश्य मेजबान दृश्य है; आप यह दिखावा करने वाले हैं कि यह पारदर्शी है और इसलिए अदृश्य है। लाल दृश्य इसका उप-भाग है, जिसे इसके केंद्र को होस्ट दृश्य केंद्र पर पिन करके पोस्ट किया गया है। अब हम बिना किसी समस्या के इसके केंद्र के चारों ओर लाल दृश्य को माप सकते हैं और घुमा सकते हैं, और वास्तव में चित्रण से पता चलता है कि हमने ऐसा किया है:

self.otherView.transform = CGAffineTransformScale(self.otherView.transform, 0.5, 0.5);
self.otherView.transform = CGAffineTransformRotate(self.otherView.transform, M_PI/8.0);

और इस बीच मेजबान दृश्य पर बाधाएं इसे सही जगह पर रखती हैं क्योंकि हम डिवाइस को घुमाते हैं।

समाधान 4: इसके बजाय लेयर ट्रांसफ़ॉर्म का उपयोग करें

दृश्य परिवर्तनों के बजाय, लेयर ट्रांसफ़ॉर्म का उपयोग करें, जो लेआउट को ट्रिगर नहीं करता है और इस प्रकार बाधाओं से तत्काल संघर्ष का कारण नहीं बनता है।

उदाहरण के लिए, यह सरल "थ्रोब" दृश्य एनीमेशन ऑटोलेयूट के तहत अच्छी तरह से टूट सकता है:

[UIView animateWithDuration:0.3 delay:0
                    options:UIViewAnimationOptionAutoreverse
                 animations:^{
    v.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
    v.transform = CGAffineTransformIdentity;
}];

भले ही अंत में दृश्य के आकार में कोई परिवर्तन नहीं हुआ था, केवल इसके transformहोने के लिए लेआउट का कारण बनता है, और बाधाएं दृश्य को चारों ओर उछाल सकती हैं। (क्या यह एक बग या क्या महसूस करता है?) लेकिन अगर हम कोर एनीमेशन के साथ एक ही काम करते हैं (एक CABasicAnimation का उपयोग करना और एनीमेशन को दृश्य की परत पर लागू करना), लेआउट नहीं होता है, और यह ठीक काम करता है:

CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.autoreverses = YES;
ba.duration = 0.3;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1, 1.1, 1)];
[v.layer addAnimation:ba forKey:nil];

3
यह परिवर्तन मेरे लिए समस्या का कारण नहीं है, बस नए एंकर बिंदु को सेट करना है। मैं बाधाओं को दूर करने और autoresizing मुखौटा की कोशिश कर रहा पर एक जाना होगा, हालांकि।
जटार्टन

1
मुझे यहाँ कुछ समझ में नहीं आ रहा है। Solution4: जब एक ट्रांसफ़ॉर्मिंग को बैकिंग लेयर पर लागू किया जाता है जो ट्रांसफ़ॉर्म ऑटोलॉययट को भी प्रभावित करता है, क्योंकि व्यू की ट्रांसफ़ॉर्मिंग प्रॉपर्टी बैकिंग लेयर के ट्रांसफ़ॉर्म की बात कर रही है
लुका बार्टोलेटी

1
@ इसके विपरीत, आईओएस 8 पर समस्या पूरी तरह से दूर हो गई है और मेरा पूरा जवाब अनावश्यक है
मैट

1
@ सनकस मैंने आपके अंक में उत्तर दिया।
बेन सिनक्लेयर

2
IOS 8 पर, लेयर ट्रांसफॉर्मेशन भी ऑटो लेआउट को
गति प्रदान करता है

41

मेरे पास एक समान Isuue था और बस Apple में ऑटोलैयूट टीम से वापस सुना। वे कंटेनर व्यू एप्रोच मैट का उपयोग करने का सुझाव देते हैं, लेकिन वे लेआउट को ओवरराइट करने के लिए UIView का एक उपवर्ग बनाते हैं और वहां कस्टम लेआउट कोड लागू करते हैं - यह एक आकर्षण की तरह काम करता है

हेडर फ़ाइल इस तरह दिखती है ताकि आप इंटरफ़ेस बिल्डर से सीधे अपनी पसंद के लिंक को लिंक कर सकें

#import <UIKit/UIKit.h>

@interface BugFixContainerView : UIView
@property(nonatomic,strong) IBOutlet UIImageView *knobImageView;
@end

और एम फाइल उस तरह विशेष कोड लागू करता है:

#import "BugFixContainerView.h"

@implementation BugFixContainerView
- (void)layoutSubviews
{
    static CGPoint fixCenter = {0};
    [super layoutSubviews];
    if (CGPointEqualToPoint(fixCenter, CGPointZero)) {
        fixCenter = [self.knobImageView center];
    } else {
        self.knobImageView.center = fixCenter;
    }
}
@end

जैसा कि आप देख सकते हैं, यह पहली बार देखने के केंद्र बिंदु को पकड़ लेता है और उस स्थिति में आगे की कॉल में पुन: उपयोग करता है ताकि तदनुसार व्यू को जगह मिल सके। यह उस अर्थ में ऑटोलॉययट कोड को अधिलेखित करता है, कि यह [सुपर लेआउटसुब्यू] के बाद होता है; जिसमें ऑटोलॉययट कोड होता है।

जैसे कि अब ऑटोलॉययट से बचने की कोई आवश्यकता नहीं है, लेकिन डिफ़ॉल्ट बिहेवियर अब उपयुक्त नहीं हैं, तो आप अपना स्वयं का ऑटोलॉयआउट बना सकते हैं। बेशक आप उस उदाहरण में क्या है की तुलना में अधिक जटिल सामान लागू कर सकते हैं, लेकिन यह सब मुझे आवश्यक था क्योंकि मेरे ऐप केवल पोर्ट्रेट मोड का उपयोग कर सकते हैं।


5
इसे पोस्ट करने के लिए आपका धन्यवाद। जब से मैंने अपना उत्तर लिखा है, मैं निश्चित रूप से ओवरराइडिंग के मूल्य को समझ गया हूं layoutSubviews। मैं सिर्फ इतना जोड़ूंगा , कि Apple यहाँ केवल आपको वही कर रहा है जो मुझे लगता है कि उन्हें (Autolayout के उनके कार्यान्वयन में) करना चाहिए था।
मैट

आप सही हैं लेकिन मुझे लगता है कि यह वास्तव में इस मुद्दे से ही संबंधित नहीं है। चूँकि उन्होंने उपरोक्त कोड प्रदान किया था जिससे मैं खुश हूँ!
सेंसेक्स

4
यह दुखद और बेतुका है कि ऑटोलैयट टीम स्वयं एक कस्टम कंटेनर उपवर्ग बनाने की सलाह देती है कि यह देखने के लिए कि सिस्टम में ऑटोलॉययट दीर्घावधि और सहज व्यवहार को कैसे तोड़ता है।
algal

8

मुझे एक आसान तरीका लगता है। और यह iOS 8 और iOS 9 पर काम करता है।

जब आप फ्रेम-आधारित लेआउट का उपयोग करते हैं, तो एंकरपॉइंट को समायोजित करें:

let oldFrame = layerView.frame
layerView.layer.anchorPoint = newAnchorPoint
layerView.frame = oldFrame

जब आप ऑटो के लेआउट के साथ दृश्य के एंकर को समायोजित करते हैं, तो आप एक ही काम करते हैं लेकिन बाधाओं में। जब एंकरपॉइंट (0.5, 0.5) से (1, 0.5) में बदल जाता है, तो लेयर व्यू, चौड़ाई की आधी लंबाई से कुछ दूरी पर छोड़ दिया जाएगा, इसलिए आपको इसकी भरपाई करने की आवश्यकता है।

मैं प्रश्न में बाधा को नहीं समझता हूं। इसलिए, मान लें कि आप सुपर व्यू सेंटर के सापेक्ष एक सेंटरएक्स बाधा को एक स्थिर के साथ जोड़ते हैं: layerView.centerX = superView.centerX + निरंतर

layerView.layer.anchorPoint = CGPoint(1, 0.5)
let centerXConstraint = .....
centerXConstraint.constant = centerXConstraint.constant + layerView.bounds.size.width/2

मेरे दोस्त आपके लिए बेहतरीन बीयर का गिलास। यह इतना अच्छा समाधान है: D
Błajej

3

यदि आप ऑटो लेआउट का उपयोग कर रहे हैं, तो मैं यह नहीं देखता कि मैन्युअल रूप से सेटिंग की स्थिति लंबे समय में कैसे काम करेगी क्योंकि अंततः ऑटो लेआउट आपके द्वारा सेट किए गए स्थिति मूल्य को क्लोब कर देगा जब यह अपने स्वयं के लेआउट की गणना करता है।

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

/**
  Set the anchorPoint of view without changing is perceived position.

 @param view view whose anchorPoint we will mutate
 @param anchorPoint new anchorPoint of the view in unit coords (e.g., {0.5,1.0})
 @param xConstraint an NSLayoutConstraint whose constant property adjust's view x.center
 @param yConstraint an NSLayoutConstraint whose constant property adjust's view y.center

  As multiple constraints can contribute to determining a view's center, the user of this
 function must specify which constraint they want modified in order to compensate for the
 modification in anchorPoint
 */
void SetViewAnchorPointMotionlesslyUpdatingConstraints(UIView * view,CGPoint anchorPoint,
                                                       NSLayoutConstraint * xConstraint,
                                                       NSLayoutConstraint * yConstraint)
{
  // assert: old and new anchorPoint are in view's unit coords
  CGPoint const oldAnchorPoint = view.layer.anchorPoint;
  CGPoint const newAnchorPoint = anchorPoint;

  // Calculate anchorPoints in view's absolute coords
  CGPoint const oldPoint = CGPointMake(view.bounds.size.width * oldAnchorPoint.x,
                                 view.bounds.size.height * oldAnchorPoint.y);
  CGPoint const newPoint = CGPointMake(view.bounds.size.width * newAnchorPoint.x,
                                 view.bounds.size.height * newAnchorPoint.y);

  // Calculate the delta between the anchorPoints
  CGPoint const delta = CGPointMake(newPoint.x-oldPoint.x, newPoint.y-oldPoint.y);

  // get the x & y constraints constants which were contributing to the current
  // view's position, and whose constant properties we will tweak to adjust its position
  CGFloat const oldXConstraintConstant = xConstraint.constant;
  CGFloat const oldYConstraintConstant = yConstraint.constant;

  // calculate new values for the x & y constraints, from the delta in anchorPoint
  // when autolayout recalculates the layout from the modified constraints,
  // it will set a new view.center that compensates for the affect of the anchorPoint
  CGFloat const newXConstraintConstant = oldXConstraintConstant + delta.x;
  CGFloat const newYConstraintConstant = oldYConstraintConstant + delta.y;

  view.layer.anchorPoint = newAnchorPoint;
  xConstraint.constant = newXConstraintConstant;
  yConstraint.constant = newYConstraintConstant;
  [view setNeedsLayout];
}

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

यदि आप केवल एक अस्थायी एनीमेशन के लिए परिवर्तन का उपयोग कर रहे हैं, तो उपरोक्त क्या हो सकता है क्योंकि मुझे विश्वास नहीं है कि ऑटो लेआउट इन-फ्लाइट एनीमेशन को उन छवियों को प्रस्तुत करने से रोक देगा जो बाधाओं के विशुद्ध रूप से क्षणिक उल्लंघन का प्रतिनिधित्व करते हैं।


मैं दुर्भाग्य से परिवर्तनों का उपयोग कर रहा हूं। पोजिशन सेट करना ठीक है क्योंकि विधि को हर बार व्यू आउट कहा जाता है, इसलिए यह क्लोबबर्ड नहीं होता है। मैं सोच रहा हूं कि लेआउट को ओवरराइड करना एक बेहतर समाधान हो सकता है (आपके पहले के उत्तर में उल्लेखित है) लेकिन मैंने अभी तक इसकी कोशिश नहीं की है।
jrturton

वैसे, यहाँ एक परीक्षण रिग का उपयोग किया गया है। यह बहुत अच्छा होगा अगर किसी को यह पता चल जाए: github.com/algal/AutolayoutAnchorPointTestRig
algal

यह स्वीकृत उत्तर होना चाहिए। @ मैट का जवाब एंकर पॉइंट के बारे में भी नहीं है।
इयूलियन ओनोफ्रेई

3

tl: dr: आप बाधाओं में से एक के लिए एक आउटलेट बना सकते हैं ताकि इसे हटाया जा सके और फिर से वापस जोड़ा जा सके।


मैंने एक नई परियोजना बनाई और केंद्र में एक निश्चित आकार के साथ एक दृश्य जोड़ा। नीचे दी गई छवि में बाधाओं को दिखाया गया है।

सबसे छोटे उदाहरण के लिए बाधाओं मैं सोच सकता था।

इसके बाद मैंने उस दृश्य के लिए एक आउटलेट जोड़ा जो घूमने जा रहा है और केंद्र x संरेखण बाधा के लिए।

@property (weak, nonatomic) IBOutlet UIView *rotatingView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *xAlignmentContstraint;

बाद में viewDidAppearमैं नए एंकर बिंदु की गणना करता हूं

UIView *view = self.rotatingView;

CGPoint rotationPoint = // The point I'm rotating around... (only X differs)
CGPoint anchorPoint = CGPointMake((rotationPoint.x-CGRectGetMinX(view.frame))/CGRectGetWidth(view.frame),
                                  (rotationPoint.y-CGRectGetMinY(view.frame))/CGRectGetHeight(view.bounds));

CGFloat xCenterDifference = rotationPoint.x-CGRectGetMidX(view.frame);

view.layer.anchorPoint = anchorPoint;

फिर मैं उस बाधा को दूर करता हूं जिसके लिए मेरे पास एक आउटलेट है, एक नया बनाएं जो ऑफसेट है और इसे फिर से जोड़ें। उसके बाद मैं बदले हुए अवरोध के साथ दृश्य को बताता हूं कि इसे बाधाओं को अद्यतन करने की आवश्यकता है।

[self.view removeConstraint:self.xAlignmentContstraint];
self.xAlignmentContstraint = [NSLayoutConstraint constraintWithItem:self.rotatingView
                                                          attribute:NSLayoutAttributeCenterX
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:self.view
                                                          attribute:NSLayoutAttributeCenterX
                                                         multiplier:1.0
                                                           constant:xDiff];
[self.view addConstraint:self.xAlignmentContstraint];
[self.view needsUpdateConstraints];

अंत में मैं सिर्फ घूर्णन दृश्य में रोटेशन एनीमेशन जोड़ता हूं।

CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotate.toValue = @(-M_PI_2);
rotate.autoreverses = YES;
rotate.repeatCount = INFINITY;
rotate.duration = 1.0;
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 

[view.layer addAnimation:rotate forKey:@"myRotationAnimation"];

घूमने वाली परत ऐसी लगती है कि यह डिवाइस को घुमाते समय भी स्थिर रहती है (जो इसे चाहिए) या फिर इससे अवरोधों को अद्यतन करने के लिए। नया अवरोध और परिवर्तित एंकर बिंदु नेत्रहीन एक दूसरे को रद्द करते हैं।


1
यह मेरी विशिष्ट स्थिति के लिए काम करेगा, ऐसा नहीं लगता कि एक अच्छा सामान्य समाधान संभव है।
जर्तूरटन

1

मेरा वर्तमान समाधान मैन्युअल रूप से परत की स्थिति को समायोजित करना है viewDidLayoutSubviews। यह कोड layoutSubviewsएक दृश्य उपवर्ग के लिए भी उपयोग किया जा सकता है , लेकिन मेरे मामले में मेरा दृश्य एक दृश्य नियंत्रक के अंदर एक शीर्ष-स्तरीय दृश्य है, इसलिए इसका मतलब है कि मुझे UIView उपवर्ग बनाने की आवश्यकता नहीं है।

यह बहुत अधिक प्रयास की तरह लगता है इसलिए अन्य उत्तर सबसे स्वागत योग्य हैं।

-(void)viewDidLayoutSubviews
{
    for (UIView *view in self.view.subviews)
    {
        CGPoint anchorPoint = view.layer.anchorPoint;
        // We're only interested in views with a non-standard anchor point
        if (!CGPointEqualToPoint(CGPointMake(0.5, 0.5),anchorPoint))
        {
            CGFloat xDifference = anchorPoint.x - 0.5;
            CGFloat yDifference = anchorPoint.y - 0.5;
            CGPoint currentPosition = view.layer.position;

            // Use transforms if we can, otherwise manually calculate the frame change
            // Assuming a transform is in use since we are changing the anchor point. 
            if (CATransform3DIsAffine(view.layer.transform))
            {
                CGAffineTransform current = CATransform3DGetAffineTransform(view.layer.transform);
                CGAffineTransform invert = CGAffineTransformInvert(current);
                currentPosition = CGPointApplyAffineTransform(currentPosition, invert);
                currentPosition.x += (view.bounds.size.width * xDifference);
                currentPosition.y += (view.bounds.size.height * yDifference);
                currentPosition = CGPointApplyAffineTransform(currentPosition, current);
            }
            else
            {
                CGFloat transformXRatio = view.bounds.size.width / view.frame.size.width;

                if (xDifference < 0)
                    transformXRatio = 1.0/transformXRatio;

                CGFloat transformYRatio = view.bounds.size.height / view.frame.size.height;
                if (yDifference < 0)
                    transformYRatio = 1.0/transformYRatio;

                currentPosition.x += (view.bounds.size.width * xDifference) * transformXRatio;
                currentPosition.y += (view.bounds.size.height * yDifference) * transformYRatio;
            }
            view.layer.position = currentPosition;
        }

    }
}

1

मेरे मैट के जवाब से प्रेरित होकर, मैंने एक अलग दृष्टिकोण की कोशिश करने का फैसला किया। एक कंटेनर दृश्य, उचित रूप से लागू बाधाओं के साथ, इस्तेमाल किया जा सकता है। संशोधित लंगर बिंदु के साथ दृश्य को कंटेनर दृश्य के भीतर रखा जा सकता है, खराब पुराने दिनों की तरह ऑटोराइजिंग मास्क और स्पष्ट फ्रेम सेटिंग का उपयोग करके।

यह वैसे भी मेरी स्थिति के लिए, एक इलाज का काम करता है। व्यू व्यूडलड में यहाँ स्थापित किए गए हैं:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    UIView *redView = [UIView new];
    redView.translatesAutoresizingMaskIntoConstraints = NO;
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[redView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(redView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[redView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(redView)]];
    self.redView = redView;

    UIView *greenView = [UIView new];
    greenView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    greenView.layer.anchorPoint = CGPointMake(1.0, 0.5);
    greenView.frame = redView.bounds;
    greenView.backgroundColor = [UIColor greenColor];
    [redView addSubview:greenView];
    self.greenView = greenView;

    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = 0.005;
    self.redView.layer.sublayerTransform = perspective;
}

इससे कोई फर्क नहीं पड़ता कि इस दृश्य पर लाल दृश्य के लिए फ्रेम शून्य हैं, क्योंकि हरे रंग के दृश्य पर ऑटोरेस्ज़िंग मास्क है।

मैंने एक एक्शन मेथड पर एक रोटेशन परिवर्तन जोड़ा, और यह परिणाम था:

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

यह डिवाइस के घूमने के दौरान खुद को खोता हुआ प्रतीत होता है, इसलिए मैंने इसे व्यूडायलेटआउट साक्षात्कार विधि में जोड़ा:

-(void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    CATransform3D transform = self.greenView.layer.transform;
    self.greenView.layer.transform = CATransform3DIdentity;
    self.greenView.frame = self.redView.bounds;
    self.greenView.layer.transform = transform;
    [CATransaction commit];

}

मुझे आश्चर्य है कि क्या आपके मामले में यह आसान नहीं होगा कि आप view.layerअकेले छोड़ दें और अपने सभी कामों को एक उपलेयर में करें view.layer। दूसरे शब्दों में, दृश्य बस एक मेजबान होगा, और सभी ड्राइंग और सबलेयर रूपांतरित हो जाते हैं और इसी तरह एक स्तर नीचे होगा, बाधाओं से अप्रभावित।
मैट

1
ठीक है, आपके सबव्यू सॉल्यूशन से प्रेरित होकर, मैंने इसे अपने निबंध में जोड़ा है! मुझे अपने सैंडबॉक्स में खेलने के लिए धन्यवाद ...
मैट

0

मुझे लगता है कि आप उस पद्धति से ऑटोलाययूट के उद्देश्य को हरा रहे हैं। आपने उल्लेख किया है कि चौड़ाई और दायाँ किनारा पर्यवेक्षण पर निर्भर करता है, तो क्यों न केवल उस विचारधारा के साथ बाधाओं को जोड़ा जाए?

एंकरपॉइंट / ट्रांसफॉर्म प्रतिमान खो दें और कोशिश करें:

[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
                             attribute:NSLayoutAttributeRight
                             relatedBy:NSLayoutRelationEqual 
                                toItem:self.view 
                             attribute:NSLayoutAttributeWidth 
                            multiplier:1.0f
                              constant:-somePadding]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
                             attribute:NSLayoutAttributeWidth
                             relatedBy:NSLayoutRelationEqual 
                                toItem:someViewWeDependTheWidthOn
                             attribute:NSLayoutAttributeWidth 
                            multiplier:0.5f // because you want it to be half of someViewWeDependTheWidthOn
                              constant:-20.0f]]; // your 20pt offset from the left

NSLayoutAttributeRightबाधा साधन वास्तव में की तरह anchorPoint = CGPointMake(1.0, 0.5)है, और NSLayoutAttributeWidthबाधा मोटे तौर पर अपने पिछले कोड के के बराबर है NSLayoutAttributeLeft


आपके उत्तर के लिए धन्यवाद, लेकिन मैं "एंकर पॉइंट / ट्रांसफॉर्म प्रतिमान नहीं खो सकता"। मुझे एक परिवर्तन लागू करने की आवश्यकता है, और सही परिवर्तन करने के लिए लंगर बिंदु को समायोजित करना है।
जर्थुरटन

आगे की व्याख्या करने के लिए, इस विशिष्ट मामले में रूपांतरित दृश्य को कभी-कभी वाई अक्ष के साथ घूमते हुए स्क्रीन पर मोड़ना होगा। इसलिए, लंगर बिंदु को स्थानांतरित करना होगा। मैं भी एक सामान्य समाधान की तलाश में हूं।
जर्थर्टन

बेशक आप अभी भी रख सकते हैं anchorPoint, मेरी बात यह है कि आप इसे माप के लिए उपयोग नहीं करना चाहिए। UIView ऑटोलैयूट सिस्टम को कैलेयर ट्रांसफॉर्म से स्वतंत्र होना चाहिए। तो यूआईवीयूवाई: लेआउट, कायर: उपस्थिति / एनिमेशन
जॉन एस्ट्रोपिया

1
यह होना चाहिए, लेकिन यह नहीं है। क्या आपने वास्तव में यह कोशिश की है? एंकर पॉइंट को बदलना ऑटो लेआउट द्वारा अपना काम करने के बाद परत की स्थिति को बंद कर देता है । आपका समाधान संशोधित एंकर बिंदु के साथ काम नहीं करता है।
jrturton

हां, मैं बाधाओं के साथ रखे गए मेरे विचारों के लिए विभिन्न एंकर पॉइंट्स का उपयोग कर सकता हूं। आपके उत्तर viewDidLayoutSubviewsको ठीक करना चाहिए; positionहमेशा साथ जाता है anchorPoint। मेरा जवाब केवल दिखाता है कि पहचान परिवर्तन के लिए बाधा को कैसे परिभाषित किया जाए।
जॉन एस्ट्रोपिया

0

इस सवाल और जवाब ने मुझे अपनी स्वयं की समस्याओं को हल करने के लिए प्रेरित किया ऑटोलॉयउट और स्केलिंग के साथ, लेकिन स्क्रॉल साक्षात्कार के साथ। मैंने गितुब पर अपने समाधान का एक उदाहरण बनाया:

https://github.com/hansdesmedt/AutoLayout-scrollview-scale

यह UIScrollView का एक उदाहरण है जो AutoLayout में पूरी तरह से बनाए गए कस्टम पेजिंग के साथ है और लंबे प्रेस और ज़ूम करने के लिए टैप करने के साथ स्केलेबल (CATransform3DMakeScale) है। iOS 6 और 7 संगत।


0

यह एक बड़ा विषय है और मैंने सभी टिप्पणियों को नहीं पढ़ा है लेकिन एक ही मुद्दे का सामना कर रहा था।

मुझे XIB से ऑटोलेयूट के साथ एक दृश्य मिला। और मैं इसकी परिवर्तनशील संपत्ति को अद्यतन करना चाहता था। कंटेनर दृश्य में एम्बेड करना मेरी समस्या का समाधान नहीं करता है क्योंकि कंटेनर दृश्य में ऑटोलेयूट अजीब तरह से काम कर रहा था। इसीलिए मैंने कंटेनर के दृश्य को शामिल करने के लिए सिर्फ दूसरा कंटेनर दृश्य जोड़ा, जिसमें मेरा दृश्य शामिल है और उस पर परिवर्तन लागू कर रहा था।


0

tl; dr मान लें कि आपने एंकर पॉइंट को (0, 0) में बदल दिया है। लंगर बिंदु अब शीर्ष पर है। जब भी आप ऑटो लेआउट में शब्द केंद्र देखते हैं , आपको शीर्ष-बाएँ सोचना चाहिए ।

जब आप अपने एंकरपॉइंट को समायोजित करते हैं, तो आप बस AutoLayout के शब्दार्थ को बदलते हैं। ऑटो लेआउट आपके एंकरपॉइंट के साथ हस्तक्षेप नहीं करेगा और न ही इसके विपरीत। यदि आपको यह समझ में नहीं आता है, तो आपके पास एक बुरा समय है


उदाहरण:

चित्रा ए। कोई लंगर बिंदु संशोधनों

#Before changing anchor point to top-left
view.size == superview.size
view.center == superview.center

चित्रा बी। लंगर बिंदु ऊपर बाईं ओर बदल गया

view.layer.anchorPoint = CGPointMake(0, 0)
view.size == superview.size
view.center == superview.topLeft                <----- L0-0K, center is now top-left

चित्रा ए और चित्रा बी बिल्कुल एक जैसे दिखते हैं। कुछ नहीं बदला। बस किस केंद्र की परिभाषा बदलनी है।


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