Как сохранить круглое изображение с помощью автоматического макета?

Как превратить прямоугольное изображение в круглое, которое может удерживать форму в автоматическом макете без установки ограничений по ширине и высоте? Тем самым позволяя объекту imageView определять его размер, а также увеличивать и уменьшать размер по отношению к объектам вокруг него с ограничениями в начале, в конце, сверху и снизу.

На днях я задал аналогичный вопрос, но я думаю, что его можно было бы сформулировать более кратко. Огромное спасибо!

ИЗМЕНИТЬ

Хорошо, я начал все сначала, чтобы сделать это как можно проще. У меня есть представление с именем «Cell» и UIImageView с именем «dog» внутри ячейки, и все. У меня больше нет «невозможно одновременно удовлетворить ограничения» в консоли, только два простых представления с использованием автоматической компоновки. Я все еще пытаюсь использовать этот код для обхода UIImageView:

profileImageView.layer.cornerRadius = profileImageView.frame.size.width / 2
profileImageView.clipsToBounds = true

Вот настройка ограничения ячейки:

скриншот 1

Вот настройка ограничения изображения профиля:

скриншот 2

Вот результат без кода, без округления, но красивый и квадратный:

скриншот 3

Вот результат с кодом для округления:

скриншот 4

Для меня это не имеет смысла, потому что без кода округления изображение будет квадратным, а с кодом - ромбовидным. Если он квадратный, разве это не должен быть круг без проблем?

ИЗМЕНИТЬ 2

Вот что происходит, когда я удаляю нижнее ограничение и добавляю множитель 0,637 для равной высоты супервизора.

скриншот 5


person CHM    schedule 02.09.2015    source источник
comment
Как вы в настоящее время делаете обзор изображения кругленьким?   -  person Gavin Hope    schedule 03.09.2015
comment
@GavinHope Я вызываю его в ячейку в tableviewcontroller и использую: cell.ProfileImageView.layer.cornerRadius = cell.ProfileImageView.frame.size.width / 2 cell.ProfileImageView.clipsToBounds = true   -  person CHM    schedule 03.09.2015
comment
Если ваше изображение имеет ширину! = Высота, вы можете найти решение здесь: stackoverflow.com/questions/29685055/   -  person lee    schedule 18.08.2016


Ответы (15)


К сожалению, вы не можете сделать это с помощью cornerRadius и автоматического раскладки - CGLayer не зависит от автоматического раскладки, поэтому любое изменение размера представления не повлияет на радиус, который был однажды установлен, что, как вы заметили, приведет к потере формы круга. .

Вы можете создать собственный подкласс UIImageView и переопределить layoutSubviews, чтобы устанавливать cornerRadius каждый раз при изменении границ просмотра изображения.

ИЗМЕНИТЬ

Пример может выглядеть примерно так:

class Foo: UIImageView {
    override func layoutSubviews() {
        super.layoutSubviews()

        let radius: CGFloat = self.bounds.size.width / 2.0

        self.layer.cornerRadius = radius
    }
}

И, очевидно, вам придется ограничить ширину экземпляра Foobar такой же, как и высота (чтобы сохранить круг). Вы, вероятно, также захотите установить contentMode экземпляра Foobar на UIViewContentMode.ScaleAspectFill, чтобы он знал, как рисовать изображение (это означает, что изображение, вероятно, будет обрезано).

person Rupert    schedule 03.09.2015
comment
Привет, @Rupert, спасибо за информацию! Я просто промочил ноги со Свифтом. Это звучит немного выше моей головы. Как бы это выглядело ... или у вас, может быть, есть ссылка на что-то с аналогичным решением, которое я мог бы визуально проверить? - person CHM; 03.09.2015
comment
Спасибо! Когда вы говорите, что ширина экземпляра Foobar должна быть такой же, как высота, для этого будет работать установка соотношения сторон 1: 1, или мне придется указать абсолютную ширину и высоту? - person CHM; 03.09.2015
comment
Например, в своем подклассе вы можете добавить ограничение let constraint: NSLayoutConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0.0); self.addConstraint(constraint) - person Rupert; 03.09.2015
comment
Я ищу ответ БОЛЬШЕ ГОДА! Большое вам спасибо! - person Noah G.; 22.12.2016
comment
Спасибо. И выберите «Обрезать границы» для изображения в раскадровке. - person Photon Point; 20.03.2017
comment
это сработало для меня для iOS 11, но когда я тестирую то же самое в iOS 10, он явно показывает рывки ... плавная передача не происходит в iOS 10 - person Mehul Thakkar; 22.11.2017
comment
@MehulThakkar Трудно узнать без дополнительных деталей, но я подозреваю, что это не имеет ничего общего с этим решением. Использование cornerRadius, очевидно, связано с компоновкой представления, что снижает производительность. Обычно это незаметно, но, возможно, вы используете сложные представления в виде таблицы или чего-то подобного. - person Rupert; 23.11.2017
comment
Большое вам спасибо .. Вы сэкономили мне много времени. - person Amit Kumar; 29.11.2017

Установка радиуса в viewWillLayoutSubviews решит проблему

override func viewWillLayoutSubviews() {
  super.viewWillLayoutSubviews()
  profileImageView.layer.cornerRadius = profileImageView.frame.height / 2.0
}
person Omar Albeik    schedule 17.04.2016
comment
Это должен быть принятый ответ. Простой, практичный, соответствует идеалам Apple и отлично работает. - person dannysood; 18.10.2017
comment
Омар Альбейк Спасибо :) - person Azharhussain Shaikh; 08.11.2017
comment
Вероятно, должно быть viewDidLayoutSubviews? - person JWhitey; 08.03.2019
comment
viewWillLayoutSubviews () или viewDidLayoutSubviews () срабатывают при каждом изменении пользовательского интерфейса в viewController, например, при вводе на клавиатуре оба метода срабатывают при каждом щелчке. Должны ли мы в этой ситуации использовать эти методы? это безопасно? Я хочу угловой радиус кнопки, у которой есть соотношение сторон в автоматическом раскладке - person Sohaib Siddique; 06.08.2019

создайте новый интерфейс в вашем .h файле, например

@interface CornerClip : UIImageView

@end

и реализация в файле .m, например

@implementation cornerClip


-(void)layoutSubviews
{
    [super layoutSubviews];

    CGFloat radius = self.bounds.size.width / 2.0;

    self.layer.cornerRadius = radius;

}

@end

теперь просто дайте класс "CornerClip" вашему изображению. 100% работает ... Наслаждайтесь

person Mr.Javed Multani    schedule 16.05.2017

Прежде всего, я должен упомянуть, что вы можете получить форму круга для своего UIView / UIImageView только, если ширина и высота будут равны. Это важно понять. Во всех остальных случаях (когда ширина! = Высота) вы не получите форму круга, потому что исходной формой вашего экземпляра пользовательского интерфейса был прямоугольник.

Итак, UIKit SDK предоставляет разработчикам механизм для управления экземпляром слоя UIview, чтобы каким-либо образом изменить любой из параметров слоя, включая настройку mask для замены исходной формы элемента UIView на пользовательскую. . Это IBDesignable / IBInspectable. Цель состоит в том, чтобы предварительно просмотреть наши пользовательские представления непосредственно через Interface Builder.

Таким образом, используя эти ключевые слова, мы можем написать наш собственный класс, который будет иметь дело только с одним условием, нужно ли нам скруглять углы для нашего элемента пользовательского интерфейса или нет.

Например, давайте создадим класс, расширенный из UIImageView.

@IBDesignable
class UIRoundedImageView: UIImageView {

    @IBInspectable var isRoundedCorners: Bool = false {
        didSet { setNeedsLayout() }
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        if isRoundedCorners {
            let shapeLayer = CAShapeLayer()
            shapeLayer.path = UIBezierPath(ovalIn:
                CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.width, height: bounds.height
            )).cgPath
            layer.mask = shapeLayer
        }
        else {
            layer.mask = nil
        }

    }

}

После установки имени класса для вашего элемента UIImageView (где находится изображение собаки) в раскадровке вы получите новую опцию, появившуюся в меню Attributes Inspector (подробности на скриншоте).

Окончательный результат должен быть таким.

person hamsternik    schedule 14.10.2018

Кажется, что когда вы добавляете одно представление в качестве подпредставления другого, это сетевое представление не обязательно будет иметь ту же высоту, что и его супервизор. Вот в чем проблема. Решение состоит в том, чтобы не добавлять ваш imageView в качестве подпредставления, а размещать его поверх вашего backgroundView. На изображении ниже я использую UILabel в качестве backgroundView.

скриншот

Также в вашем случае, когда вы устанавливаете cornerRadius, используйте это: let radius: CGFloat = self.bounds.size.height / 2.0.

person Randel S    schedule 03.09.2015
comment
аааа ... Я не могла заставить его работать. Я пробовал это из контейнера с помощью этого кода, основываясь на том, что вы сказали: let radius: CGFloat = profileImageView.bounds.size.height / 2.0 profileImageView.clipsToBounds = true - person CHM; 04.09.2015
comment
Вот проект xcode, показывающий, что я сделал. github.com/RGSSoftware/ImageViewConstraints - person Randel S; 04.09.2015
comment
Рэндел, это полностью работает. Моя единственная проблема заключается в том, что я запускаю эти изображения в пользовательских ячейках tableview, которые имеют свои собственные файлы классов с элементами, установленными как IBOutlets, поэтому я не могу понять, как использовать func viewDidLayoutSubviews, потому что я вызываю их динамически в контроллер tableView с помощью cellForRowAtIndexPath и использование dequeueReusableCellWithIdentifier - person CHM; 04.09.2015

С моим хакерским решением вы получите плавную анимацию радиуса угла при изменении размера кадра.

Допустим, у вас есть ViewSubclass: UIView. Он должен содержать следующий код:

class ViewSubclass: UIView {
    var animationDuration : TimeInterval?
    let imageView = UIImageView()
    //some imageView setup code

    override func layoutSubviews() {
        super.layoutSubviews()

        if let duration = animationDuration {
            let anim = CABasicAnimation(keyPath: "cornerRadius")
            anim.fromValue = self.imageView.cornerRadius
            let radius = self.imageView.frame.size.width / 2
            anim.toValue = radius
            anim.duration = duration
            self.imageView.layer.cornerRadius = radius
            self.imageView.layer.add(anim, forKey: "cornerRadius")
        } else {
            imageView.cornerRadius = imageView.frame.size.width / 2
        }

        animationDuration = nil
    }
}

Затем вам нужно будет сделать это:

let duration = 0.4 //For example
instanceOfViewSubclass.animationDuration = duration
UIView.animate(withDuration: duration, animations: { 
    //Your animation
    instanceOfViewSubclass.layoutIfNeeded()
})

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

person Oleksii Nezhyborets    schedule 28.09.2016

Swift 4+ чистое решение, основанное на ответе omaralbeik

import UIKit

extension UIImageView {

    func setRounded(borderWidth: CGFloat = 0.0, borderColor: UIColor = UIColor.clear) {
        layer.cornerRadius = frame.width / 2
        layer.masksToBounds = true
        layer.borderWidth = borderWidth
        layer.borderColor = borderColor.cgColor
    }
}

Пример использования в UIViewController

1. просто закругленный UIImageView

override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        imageView.setRounded()
    }

2. закругленный UIImageView с шириной и цветом границы

override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        imageView.setRounded(borderWidth: 1.0, borderColor: UIColor.red)
    }
person Maverick    schedule 02.08.2018

напишите этот код

override viewDidLayoutSubViews() {
profileImageView.layer.cornerRadius = profileImageView.frame.size.width / 2
profileImageView.clipsToBounds = true

}

в этом случае он будет вызываться после вычисления вычислений автоматического раскладки в первом коде, который вы назвали кодом углового радиуса перед вычислением фактического размера представления, потому что он динамически вычисляется с использованием соотношения сторон, фактический радиус угла вычисляется до приравнивания ширины и высоты вид

person MoTaher    schedule 28.10.2019

У меня такая же проблема, и теперь я понимаю, что здесь произошло, надеюсь, что некоторые идеи могут вам помочь: между буксировками есть что-то другое:

  1. ваш профильImageView в раскадровке
  2. ваш profileImageView в viewDidLoad

размер границы и рамки в viewDidLoad и в раскадровке различаются просто потому, что размер представления изменяется для разных размеров устройства. Вы можете попробовать print(profileImageView.bounds.size) в viewDidLoad и viewDidAppear вы обнаружите, что размер в viewDidLoad, который вы установили cornerRadius, не является реальным "бегущим" размером.

совет для вас: вы можете использовать подкласс ImageView, чтобы избежать этого, или не использовать его в раскадровке,

person livinspring    schedule 13.12.2015

Если вы создали подкласс UIImageView. Тогда просто добавьте в него этот кусок магического кода.

Написано на: Swift 3

override func layoutSubviews() {
    super.layoutSubviews()
    if self.isCircular! {
        self.layer.cornerRadius = self.bounds.size.width * 0.50
    }
}
person iOSer    schedule 31.01.2018

Я новичок в разработке приложений для iOS, но у меня была та же проблема, и я нашел решение.

это была цель

Итак, у зеленого фона есть следующие ограничения:

backgroundView.translatesAutoresizingMaskIntoConstraints = false
backgroundView.topAnchor.constraint(equalTo: superview!.safeAreaLayoutGuide.topAnchor).isActive = true
backgroundView.leftAnchor.constraint(equalTo: superview!.leftAnchor).isActive = true
backgroundView.widthAnchor.constraint(equalTo: superview!.widthAnchor).isActive = true
backgroundView.heightAnchor.constraint(equalTo: superview!.heightAnchor, multiplier: 0.2).isActive = true

Ограничения изображения:

avatar.translatesAutoresizingMaskIntoConstraints = false
avatar.heightAnchor.constraint(equalTo: backgroundView.heightAnchor, multiplier: 0.8).isActive = true
avatar.widthAnchor.constraint(equalTo: backgroundView.heightAnchor, multiplier: 0.8).isActive = true
avatar.centerYAnchor.constraint(equalTo: backgroundView.centerYAnchor, constant: 0).isActive = true
avatar.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor, constant: 20).isActive = true

в viewWillLayoutSubviews() методе я установил радиус угла равным

avatar.layer.cornerRadius = (self.frame.height * 0.2 * 0.8) / 2

По сути, я просто вычисляю высоту изображения, а затем делю ее на 2. 0,2 - множитель ограничения высоты backgroungView, а 0,8 - множитель ограничения ширины / высоты изображения.

person Vasil Kanev    schedule 14.10.2018

Решение: Обрежьте изображение.

[imageView setImage:[[imageView image] imageWithRoundedCorners:imageView.image.size.width/2]];
imageView.contentMode = UIViewContentModeScaleAspectFill;

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

Опубликуйте это, даже если размер вашего просмотра изменится, изображение останется круглым и хорошо выглядит.

person Amol Dumrewal    schedule 26.03.2020

Добавьте к imageView ограничение соотношения сторон 1: 1, чтобы оно оставалось круглым, несмотря на любые изменения высоты или ширины.

person Community    schedule 02.09.2015
comment
Привет, @petahchristian, я фиксирую соотношение 1: 1, а также использую верхние, ведущие, замыкающие и нижние ограничения, но когда я увеличиваю или уменьшаю размер, я теряю идеальный круг. Когда я становлюсь больше, я получаю продолговатый овал, а меньше - больше ромбовидной формы. - person CHM; 03.09.2015
comment
Вы не можете одновременно удовлетворить ограничения в консоли? Если у вас есть верхнее, ведущее, замыкающее и нижнее ограничения, то эти ограничения могут вытягивать или подталкивать ваш imageView, что приводит к срыву ограничения соотношения сторон. - person Randel S; 03.09.2015
comment
@CHM Да. Как отметил Рэндел, вы чрезмерно ограничили imageView. Вы не можете иметь размер в 4 направлениях и при этом оставаться круглым. - person ; 03.09.2015
comment
@ RandelG.Smith: да, я понял это в консоли. Завтра я проведу несколько тестов ограничений и буду добавлять их по частям, чтобы посмотреть, что их нарушает. Спасибо за помощь ребята. Я обновлюсь, когда разберусь. - person CHM; 03.09.2015
comment
@ RandelG.Smith Я добавил упрощенные диаграммы к исходному вопросу. Посмотрите, это дает более наглядное представление о том, что происходит. - person CHM; 03.09.2015
comment
@PetahChristian Я добавил упрощенные диаграммы к исходному вопросу. Посмотрите, это дает более наглядное представление о том, что происходит. - person CHM; 03.09.2015
comment
@CHM Попробуйте снять нижнее ограничение на изображении собаки. - person Randel S; 04.09.2015
comment
@ RandelG.Smith Что я попытался удалить его и добавить 0,637 в качестве множителя, я опубликую результат выше в редактировании - person CHM; 04.09.2015
comment
@ RandelG.Smith Куда пропал твой ответ? - person CHM; 04.09.2015
comment
Я собираюсь добавить другой ответ. Тот, который я удалил, был неправильным. - person Randel S; 04.09.2015
comment
@ RandelG.Smith Я заставил множитель работать. Думаю, цифра была другой, потому что у меня была наценка. Итак, я запустил его в симуляторе, и изображение все еще ромбовидное на 5 с, идеальный круг на 6 и почти круг на 6+. - person CHM; 04.09.2015

Я добавил собственный IBInspectable cornerRadiusPercent, так что вы можете сделать это без кода.

class RoundButton: UIButton {

    override var bounds: CGRect {
        didSet {
            updateCornerRadius()
        }
    }


    //private var cornerRadiusWatcher : CornerRadiusPercent?
    @IBInspectable var cornerRadiusPercent: CGFloat = 0 {

        didSet {
            updateCornerRadius()
        }
    }

    func updateCornerRadius()
    {
        layer.cornerRadius = bounds.height * cornerRadiusPercent
    }

}
person Chen Jiling    schedule 01.11.2018

Это легко сделать, создав IBOutlet для ограничения, которое необходимо изменить во время выполнения. Ниже приведен код:

  1. Создайте IBOutlet для ограничения, которое необходимо изменить во время выполнения.

    @IBOutlet var widthConstraint: NSLayoutConstraint!
    
  2. Добавьте ниже код в viewDidLoad():

    self.widthConstraint.constant = imageWidthConstraintConstant()
    

Ниже функция определяет для типов устройств изменение ограничения ширины соответственно.

func imageWidthConstraintConstant() -> CGFloat {

    switch(self.screenWidth()) {

    case 375:
        return 100

    case 414:
        return 120

    case 320:
        return 77

    default:
        return 77
    }
}
person Badrinath    schedule 17.10.2016
comment
это так плохо XD - person J. Doe; 15.02.2018