Почему маска CALayer не центрирована и не смещена на несколько пикселей?

У меня есть следующий код:

public override void ViewWillAppear(bool animated)
{
    base.ViewWillAppear(animated);

    var gradient = new CAGradientLayer();
    gradient.Frame = view.Bounds;
    gradient.Colors = colors;

    var mask = new CALayer();
    mask.Frame = view.Bounds;
    mask.Contents = view.Image.CGImage;
    mask.ContentsGravity = CALayer.GravityCenter;

    gradient.Mask = mask;
    view.Layer.AddSublayer(gradient);
}

который дает следующий результат: изображение

view - это UIImageView, который идеально центрирован.

Число 1 находится ровно посередине. Здесь вы можете видеть, что маска не идеально отцентрирована.

Изображение здесь — это значок сердца из стандартных ресурсов, это не пользовательское изображение. (Если, например, используется в кнопке, то она идеально центрирована).

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

Кто-то еще сообщил о подобной проблеме здесь, но решения не было: Gradient Color над изображением шаблона в Swift 4

Это заставляет меня поверить, что есть ошибка с применением масок. Кто-нибудь может подтвердить?


person ison    schedule 20.03.2021    source источник
comment
«есть ошибка с применением масок» Нет. Поверь мне в этом.   -  person matt    schedule 20.03.2021
comment
@matt Почему такой простой код дает неверные результаты? Похоже, что маски не центрированы, даже если используется CALayer.GravityCenter. Или здесь есть какой-то крайний случай с тем, как следует устанавливать фреймы?   -  person ison    schedule 20.03.2021
comment
Вы не показали достаточно кода, чтобы воспроизвести проблему, и даже не объяснили, что вы пытаетесь сделать, поэтому дальнейшая помощь невозможна. Я просто говорю, что вы не поймете этого, если не начнете с предположения, что вы совершаете ошибку.   -  person matt    schedule 20.03.2021
comment
@matt Единственная другая вещь - это раскадровка с центрированным UIImageView и центрированной меткой. Я не уверен, что смогу загрузить его куда-нибудь сюда.   -  person ison    schedule 20.03.2021
comment
Например, возможно, вы делаете это в эквиваленте viewDidLoad. Но это слишком рано, никаких кадров не известно. Как я уже сказал, без достаточного количества кода и ясной цели вопрос бессмыслен.   -  person matt    schedule 20.03.2021
comment
@matt Это во ViewWillAppear. Я обновил код. Если вы думаете, что есть что-то еще, что было бы хорошо опубликовать, дайте мне знать.   -  person ison    schedule 20.03.2021
comment
Я до сих пор не знаю, что вы хотите сделать. Это секрет? Конечно, цель не странная красная капля.   -  person matt    schedule 20.03.2021
comment
@matt Я бы хотел, чтобы изображение было центрировано, как указано в заголовке: Почему маска CALayer не центрирована и смещена на несколько пикселей?   -  person ison    schedule 20.03.2021
comment
Но образ — это не маска. Какое изображение и какое отношение оно имеет к градиентам, центрированию и маскированию?   -  person matt    schedule 20.03.2021
comment
@matt Изображение можно использовать в качестве маски, как указано выше. Это градиентный слой с изображением, используемым в качестве маски, которая создает красную форму на экране, которая не находится по центру. Здесь не задействованы никакие другие виджеты, только те, что упомянуты в коде выше.   -  person ison    schedule 20.03.2021
comment
Так вот вопрос: разместить градиентный слой на весь экран, и замаскировать его ровно по центру с изображением?   -  person matt    schedule 20.03.2021
comment
@matt Не уверен, что вы имеете в виду, но да, тогда замаскированное изображение смещено от центра на несколько пикселей. Вы можете увидеть аналогичный результат здесь: swift 4">stackoverflow.com/questions/47122957/ (есть изображение). Я где-то нашел, что маски на самом деле используют другое координатное пространство, так что, возможно, это причина, но я не знаю, как перевести кадр в эти координаты маски.   -  person ison    schedule 20.03.2021


Ответы (1)


С маскировкой ошибок нет. Это очень важный материал, базовый для всего рисунка. Если бы была ошибка, весь интерфейс был бы сломан. Ошибка в вашем коде.

Это все в основном просто вопрос времени. Не говорите ни о каких фреймах или границах, пока не узнаете, что они собой представляют на самом деле.

Чтобы продемонстрировать, я буду работать в обратном порядке: сначала я покажу вам результат моего кода, затем я покажу вам код. Вот результат. Я начинаю с раскадровки с квадратом, центрированным автомакетом, так что вы знаете, что я не жульничаю:

введите здесь описание изображения

Теперь я запускаю приложение и накладываю на весь экран градиентный слой с маской изображения сердца по центру:

введите здесь описание изображения

Как видите, сердце находится точно в нужном месте. Он центрирован точно в том же смысле, что и желтый квадрат, и поэтому кажется центрированным в квадрате.

Теперь вот код. Обратите внимание, что работа по сборке выполняется только один раз, в viewDidLoad, но вся работа по измерению выполняется, когда у нас есть фактические измерения для работы, а именно в viewDidLayoutSubviews. Это Swift, но у вас не должно возникнуть проблем с переводом (С# или что-то еще):

class ViewController: UIViewController {
    
    let gradient : CAGradientLayer = {
        let g = CAGradientLayer()
        g.colors = [UIColor.red.cgColor, UIColor.green.cgColor]
        return g
    }()
    
    let mask = CALayer()
    
    let image : UIImage = {
        let im = UIImage(systemName: "heart.fill")!
        return im
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.layer.addSublayer(gradient)
        mask.contents = image.cgImage
        mask.contentsGravity = .center
        gradient.mask = mask
    }
    
    override func viewDidLayoutSubviews() {
        gradient.frame = view.bounds
        mask.frame = gradient.bounds
    }
}

Идите и поступайте так же.

person matt    schedule 20.03.2021
comment
Итак, я начал новый проект, и он действительно сработал! Я попытался выяснить, почему это дало другие результаты, чем в моем случае, и оказалось, что это потому, что я сделал это в ViewWillAppear, что, по-видимому, было слишком рано. Я переместил свой код в ViewDidAppear, и он сработал. Я не понимал, что ViewWillAppear может быть слишком рано. И изображение было выключено всего на несколько пикселей. Спасибо! - person ison; 22.03.2021
comment
На мой взгляд, viewDidAppear, вероятно, будет слишком поздним — пользователь может увидеть прыжок отрисовки. Вот почему я использую viewDidLayoutSubviews. - person matt; 22.03.2021