Otomatik düzen kısıtlamalarını kullanırken bir görünümün mevcut genişliğini ve yüksekliğini nasıl alabilirim?


108

Frame özelliğinden bahsetmiyorum, çünkü ondan yalnızca xib'de görünümün boyutunu elde edebilirsiniz. Görünümün kısıtlamaları nedeniyle (belki bir rotasyondan sonra veya bir olaya yanıt olarak) yeniden boyutlandırılmasından bahsediyorum. Mevcut genişliğini ve yüksekliğini almanın bir yolu var mı?

Kısıtlamaları boyunca genişlik ve yükseklik kısıtlamalarını aramayı denedim, ancak bu çok temiz değil ve içsel kısıtlamalar olduğunda başarısız oluyor (çünkü ikisi arasında ayrım yapamıyorum). Ayrıca, bu yalnızca gerçekten genişlik ve yükseklik sınırlamalarına sahiplerse işe yarar, ancak yeniden boyutlandırmak için başka kısıtlamalara güveniyorlarsa işe yaramaz.

Bu neden benim için bu kadar zor? ARG!


Görüntünün belirli bir zamandaki sınırlarının mevcut genişliğini ve yüksekliğini koruduğunu düşünürdüm. Yerleşim sürecinde ayarlanan şey budur.
Phillip Mills

Hmm sınırları kontrol etmeyi hiç düşünmemiştim. Bunu şimdi yapacağım.
yuf

Yanıtlar:


246

Cevap şudur [view layoutIfNeeded].

İşte nedeni:

Görünümün geçerli genişliğini ve yüksekliğini inceleyerek view.bounds.size.widthve view.bounds.size.height(veya ile oynamadığınız sürece eşdeğer olan çerçeve) almaya devam edersiniz view.transform.

İstediğiniz şey, mevcut kısıtlamalarınızın ima ettiği genişlik ve yükseklikse, yanıt, kısıtlamaları manuel olarak incelemek değildir, çünkü bu, bunları yorumlamak için otomatik düzen sisteminin tüm kısıtlama çözme mantığını yeniden uygulamanızı gerektirir. kısıtlamaları. Bunun yerine yapmanız gereken, auto layout'tan bu düzeni güncellemesini istemektir , böylece kısıtlamaları çözer ve view.bounds'un değerini doğru çözümle günceller ve ardından view.bounds'u inceleyin.

Otomatik düzenden düzeni güncellemesini nasıl istersiniz? [view setNeedsLayout]Otomatik düzenin çalışma döngüsünün bir sonraki dönüşünde düzeni güncellemesini istiyorsanız arayın .

Ancak, bunu hemen yeni sınırları değer erişebilmesi için, hemen düzenini güncellemek istiyorsanız geçerli fonksiyon içinde üzeri veya çalışma döngüsü başından önce başka bir noktada, o zaman çağırmanız gerekir [view setNeedsLayout]ve [view layoutIfNeeded].

İkinci bir soru sordunuz: "Doğrudan bir referansım yoksa bir yükseklik / genişlik sınırlamasını nasıl değiştirebilirim?".

Eğer kısıtlamayı IB'de yaratırsanız, en iyi çözüm görüş kontrolörünüzde veya sizin görüşünüzde bir IBOutlet oluşturmaktır, böylece ona doğrudan bir referansınız olur. Kısıtlamayı kodda oluşturduysanız, onu yarattığınız sırada dahili bir zayıf özellikteki bir referansı tutmalısınız. Kısıtlamayı başka biri oluşturduysa, görünümdeki view.constraints özelliğini ve muhtemelen tüm görünüm hiyerarşisini inceleyerek ve önemli NSLayoutConstraint'i bulan mantığı uygulayarak bulmanız gerekir. Bu muhtemelen yanlış bir yoldur, çünkü aynı zamanda, bu soruya basit bir cevap olması garanti edilmediğinde, sınır boyutunu hangi belirli kısıtlamanın belirlediğini belirlemenizi de etkili bir şekilde gerektirir. Nihai sınır değeri, çoklu kısıtlamalardan oluşan çok karmaşık bir sisteme çözüm olabilir,


16
Bir mükemmel cevap ve iyi de açıkladı. Saçımı çekip bir iki saat sonra beni kurtardım.
Ben Kreeger

2
layoutIfNeededharika ve çerçeveyi hemen kullanılabilir hale getirecek. Bununla birlikte, çağrıldığı görünümün alt ağaçlarındaki tüm görünümler üzerindeki kısıtlamaların oluşturulmasını da zorlayacaktır. Örneğin, programlı olarak görünümler ekliyorsanız ve layoutIfNeededbunların tümünü yinelemeli rutininizde çağırıyorsanız , görünüm hiyerarşinizin çok yavaş işlediğini fark edebilirsiniz. (Bunu zor yoldan öğrendim) Mükemmel yanıtta belirtildiği gibi, 'setNeedsLayout' daha etkilidir ve çerçeveyi, ideal olarak saniyenin yaklaşık 1 / 60'ında gerçekleşen bir sonraki düzen geçişinde kullanılabilir hale getirecektir.
shmim

1
Bunun eski olduğunu biliyorum ama bu yöntemi nerede adlandırıyorsunuz? İçinde initWithCoder?
MayNotBe

4
Neden düzeni sadece sınırları aşmak için zorluyorsunuz? Bu verimsiz görünüyor ve karmaşık bir arayüzle bu potansiyel olarak pahalı olacak. Bunun yerine, viewDidLayoutSubviews'lerden veya hatta viewWillLayoutSubviews'lerden en son sınırlara erişin ve kakaonun kendi düzen zamanlamasını yönetmesine izin verin. Bir sorunsa, birden çok çağrıyı önlemek için değere veya bayrağa erişmeniz gerekiyorsa bir özellik ayarlayın.
smileBot

1
Evet, yalnızca çalışma döngüsünün dönüşünden önce otomatik düzen hesaplanan değere erişmeniz gerekiyorsa düzeni zorlamanız gerekir - örneğin, geçerli işlev çağrısı kapsamında.
algal

8

Benzer bir sorunla karşılaştım, buna bir üst ve alt kenarlık eklemem gereken yerde UITableView, içindeki kısıtlamalar kurulumuna göre yeniden boyutlandırılıyor UIStoryboard. İle güncellenen kısıtlamalara erişebildim - (void)viewDidLayoutSubviews. Bu, bir görünümü alt sınıflara ayırmanıza ve yerleşim yöntemini geçersiz kılmanıza gerek kalmaması için kullanışlıdır.

/*** SET TOP AND BOTTOM BORDERS ON TABLE VIEW ***/
- (void)addBorders
{
    CALayer *topBorder           = [CALayer layer];
    topBorder.frame              = CGRectMake(0.0f, self.tableView.frame.origin.y, 320.0f, 0.5f);
    topBorder.backgroundColor    = [UIColor redColor].CGColor;

    CALayer *bottomBorder        = [CALayer layer];
    bottomBorder.frame           = CGRectMake(0.0f, (self.tableView.frame.origin.y + self.tableView.frame.size.height), 320.0f, 0.5f);
    bottomBorder.backgroundColor = [UIColor redColor].CGColor;

    [self.view.layer addSublayer:topBorder];
    [self.view.layer addSublayer:bottomBorder];
}

/*** GET AUTORESIZED FRAME DIMENSIONS ***/
- (void)viewDidLayoutSubviews{
    [self addBorders];
}

Yöntemi yöntemden çağırmadan, viewDidLayoutSubviewalt sınır ekran dışında bir yerde olduğundan yalnızca üst sınır doğru şekilde çizilir.


3
viewDidLayoutSubviews birkaç kez çağrılıyor, tonlarca katman oluşturuyorsunuz ... Başka bir katman eklemeden önce bazı varoluş kontrolleri eklemeniz gerekiyor .
Kalzem

Birkaç yıl sonra yorum yapın ... herhangi bir şeyden önce geçersiz kılarken bu yöntemi süper sınıfta da çağırmalısınız:[super viewDidLayoutSubviews];
Alejandro Iván

5

Özellikle TableviewCell ile hala bu tür sorunlarla karşılaşabilecek olanlar için.

Sadece yöntemi geçersiz kıl:

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

UITableViewCell veya UICollectionViewCell olması durumunda hücrenin bir alt sınıfını oluşturun ve aynı yöntemi geçersiz kılın:

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

görüşümün gerçek son boyutunu elde etmenin tek yolu bu
etmemin

Bu, kısıtlı görünümlerimden herhangi biri için nihai boyutu elde edebilmemin tek yoluydu çünkü onları awakeFromNib'e almaya çalışmadan önce, alt görünümlere ilk önce yeniden boyutlandırma zamanı vermemiştim. Teşekkür ederim!
Azin Mehrnoosh

3

Kullan -(void)viewWillAppear:(BOOL)animatedve ara [self.view layoutIfNeeded];- Denediğim işe yarıyor.

çünkü kullanırsanız -(void)viewDidLayoutSubviewskesinlikle işe yarayacaktır ancak kullanıcı arayüzünüz her güncelleme / değişiklik talep ettiğinde bu yöntem çağrılır. Yönetmesi zorlaşacak. Yazılım anahtarı, bu tür çağrı döngüsünden kaçınmak için bir bool değişkeni kullanmanızdır. daha iyi kullanım viewWillAppear. Unutmayın viewWillAppear, görünüm yeniden yüklenirse (yeniden tahsis etmeden) çağrılacaktır.


2

Çerçeve hala geçerlidir. Sonuç olarak, görünüm kendisini düzenlemek için çerçeve özelliğini kullanır. Bu çerçeveyi tüm kısıtlamalara göre hesaplar. Kısıtlamalar yalnızca ilk yerleşim düzeni için kullanılır (ve herhangi bir zamanda layoutSubviews bir görünümde döndürmeden sonra olduğu gibi çağrılır). Bundan sonra, konum bilgisi çerçeve özelliğindedir. Yoksa aksini mi görüyorsunuz?


1
Ben aksini görüyorum. Çerçeve değerleri, genişlik / yükseklik kısıtlamalarını doğrudan değiştirdikten sonra bile değişmiyor (sabitlerini değiştirerek.
Xib'de

Sorunum benzersiz çünkü kısıtlamayı değiştiriyorum ve ardından çerçeveyi aynı işlevde kullanmam gerekiyor. SetNeedsLayout çağrıldığında hemen güncellenmez. Sanırım önce düzeni beklemem, ardından çerçeveyi kullanmam gerekiyor. İkinci sorum, doğrudan bir referansım yoksa bir yükseklik / genişlik sınırlamasını nasıl değiştirebilirim?
yuf

1
Daha sonra dispatch_async yapmalısınız ve bu, kodunuzu bir sonraki çerçeveye kadar geciktirmenize izin verecektir. İkinci kısma gelince ... Bilmiyorum ... tüm kısıtlamaları tekrarlamanız ve hangisinin uygulanabilir olduğuna bakmanız gerekir ... IBOutlet'ler çok daha kolaydır.
borrrden

IBOutlets için doğru, ancak bir UIView kategorisinde çalışacak IB'lere sahip olmayacağım bir işlev yazmak istiyorum.
yuf
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.