CALayers не змінили розмір при зміні меж його UIView. Чому?


101

У мене UIViewє близько 8 різних CALayerпідшарів, доданих до його шару. Якщо я змінюю межі перегляду (анімовані), то сам вигляд зменшується (я перевірив це за допомогою а backgroundColor), але розмір підшару залишається незмінним .

Як це вирішити?

Відповіді:


149

Я використовував той самий підхід, що і Солін, але в цьому коді є помилка друку. Метод повинен бути:

- (void)layoutSubviews {
  [super layoutSubviews];
  // resize your layers based on the view's new bounds
  mylayer.frame = self.bounds;
}

Для своїх цілей я завжди хотів, щоб підшар був повним розміром батьківського подання. Помістіть цей метод у свій клас перегляду.


8
@fishinear Ви впевнені? це звучить так, ніби ви описуєте кадр. Теоретично межі повинні завжди мати 0,0 як своє походження.
griotspeak

3
@griotspeak - це насправді не так. Опис властивості меж дивіться тут: developer.apple.com/library/ios/#documentation/uikit/reference/… - "За замовчуванням походження прямокутника меж встановлено на (0, 0), але ви можете змінити це значення для відображення різних частин перегляду. "
jdc

Велике спасибі, я з того часу дізнався про декілька випадків (прокрутка для одного), де це неправда, але я забув про цей коментар.
griotspeak

31
Не забудьте зателефонувати [супер layoutSubviews], оскільки це може спричинити несподівану поведінку в різних класах UIKit.
Kpmurphy91

2
Для мене це не дуже добре спрацювало з розміром із UITextViewрозмірами (scrollEnabled = NO) та автоматичним макетом. У мене був перегляд тексту, прикріплений до його огляду, який, у свою чергу, мав CALayerу нижній частині себе (межі), і я хотів, щоб воно оновило позицію кордону, коли висота перегляду тексту змінилася. Чомусь layoutSubiewsназивались занадто пізно (і положення шару було оживлено).
iosdude

26

Оскільки CALayer на iPhone не підтримує менеджерів макетів, я думаю, що вам доведеться зробити основний шар вашого перегляду спеціальним підкласом CALayer, в якому ви переосмислюєте, layoutSublayersщоб встановити кадри всіх підшарів. Ви також повинні перекрити +layerClassметод перегляду, щоб повернути клас нового підкласу CALayer.


Override + layerClass, як у випадку перекриття шару OpenGL? Гадаю так. Встановити кадри підшарів? До чого? Я повинен обчислювати всі кадри / позиції підшару індивідуально залежно від розміру кадру супершару? Чи не існує жодного рішення, схожого на обмеження, чи «зв’язок до супершару»? О, боже, ще один день поза часом. Програми CGLayers отримали розмір, але вони були занадто млявими, CALayers досить швидкі, але не змінювали їх розмір. Стільки сюрпризів. Дякую за відповідь, все одно.
Гері Борбас

3
CALayer на Mac має для цього менеджери макетів, але вони недоступні на iPhone. Так що так, ви повинні самі розрахувати розміри кадру. Іншим варіантом може бути використання підперегляду замість підшарів. Тоді ви можете встановити відповідно автоматичні маски підперегляду.
Оле Бегеман

2
Так, UIView є надто дорогими класами, тому я використовую CALayers.
Гері Борбас

Я спробував так, як ти запропонував. Це працює, і це не так. Я змінюю розмір анімованого перегляду, але розмір підшарів змінюється в іншій анімації. Кривавий чорт, що зараз робити? Який спосіб перевизначення в підкласі CALayer, що викликає ВСЕ (!) Кадр анімації перегляду? Чи є?
Гері Борбас

Просто наткнувся і на це. Як видається Оле, найкращим варіантом є підклас CALayer, але це видається зайвим громіздким.
Димитрій

15

Я використовував це в UIView.

-(void)layoutSublayersOfLayer:(CALayer *)layer
{
    if (layer == self.layer)
    {
        _anySubLayer.frame = layer.bounds;
    }

super.layoutSublayersOfLayer(layer)
}

Працює для мене.


Так, це працювало для мене в iOS 8. Імовірно, він працював би і в попередніх версіях. У мене був фоновий шар градієнта і потрібен для зміни розміру шару при поверненні пристрою.
EPage_Ed

Працював для мене, але йому потрібен був дзвінок до супер
ентропії

Це найкраща відповідь! Так, я спробував layoutSubviews, але він лише порушує макет мого UITextField, як зникнення leftView та rightView, і більше тексту в UITextField зникає. Можливо, я використовував розширення замість спадкування UIView, але це було дуже баггі. ЦІ РОБОТИ ВЕЛИКІ! THX
Michał Ziobro

Для шару зі спеціальним малюнком ( CALayerDelegateими draw(_:in:)) мені довелося також зателефонувати _anySubLayer.setNeedsDisplay(). Тоді це працювало для мене.
jedwidz

9

У мене була така ж проблема. У спеціальний шар перегляду я додав ще два підшари. Щоб змінити розмір підшарів (щоразу, коли змінюються межі спеціального перегляду), я застосував метод layoutSubviewsмого спеціального перегляду; всередині цього методу я просто оновлюю кожен кадр підшару, щоб він відповідав поточним межам шару мого підрису.

Щось на зразок цього:

-(void)layoutSubviews{
   //keep the same origin, just update the width and height
   if(sublayer1!=nil){
      sublayer1.frame = self.layer.bounds;
   }
}

16
FYI, вам не потрібен if (sublayer1! = Nil), оскільки ObjC визначає повідомлення для нуля (у цьому випадку -setFrame :) як неоперативний повернення 0.
Andrew Pouliot

7

2017 рік

Буквальна відповідь на це питання:

"Користувачі CAL не отримали змін у зміні меж його UIView. Чому?"

це на краще чи гірше

needsDisplayOnBoundsChange

за замовчуванням у false в CALayer.

рішення,

class CircularGradientViewLayer: CALayer {
    
    override init() {
        
        super.init()
        needsDisplayOnBoundsChange = true
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override open func draw(in ctx: CGContext) {
        go crazy drawing in .bounds
    }
}

Дійсно, я направляю вас до цієї якості

https://stackoverflow.com/a/47760444/294884

що пояснює, що, до біса, критична contentsScaleустановка; зазвичай потрібно однаково встановити, що при встановленні потребDisplayOnBoundsChange.


5

Версія Swift 3

У клітинку "Користувальниця" додайте наступні рядки

Декларуйте спочатку

let gradientLayer: CAGradientLayer = CAGradientLayer()

Потім додайте наступні рядки

override func layoutSubviews() {
    gradientLayer.frame = self.YourCustomView.bounds
}

0

Як [Ole] писав, CALayer не підтримує автоматичну автоматизацію на iOS. Тому слід налаштувати макет вручну. Мій варіант полягав у налаштуванні рамки шару в межах (iOS 7 та новіших версій)

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 

або (станом на iOS 8)

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinato

0

У вашому користувальницькому представленні вам потрібно оголосити змінну для вашого спеціального шару, не оголосити змінну в області init. І просто запускайте його один раз, не намагайтеся встановити нульове значення та повторно вводити його

    клас CustomView: UIView {
        var customLayer: CALayer = CALayer ()

        замінити функцію компонуванняSubviews () {
            super.layoutSubviews ()
    // охороняйте, щоб _fillColor = self._fillColor else {return}
            InitializeLayout ()
        }
        приватний функціонал InitializeLayout () { 
            customLayer.removeFromSuperView ()
            customLayer.frame = layer.bounds
                   
            layer.insertSubview (за: 0)
        }
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.