web-dev-qa-db-fra.com

Comment garder un rond imageView en utilisant la mise en page automatique?

Comment transformer une vue d'image rectangulaire en vue d'image circulaire pouvant conserver la forme dans la présentation automatique sans définir de restrictions de largeur et de hauteur? Laissant ainsi l'imageView définir sa taille, ainsi que des tailles plus grandes et plus petites par rapport aux objets qui l'entourent avec des contraintes de début, de fin, de haut et de bas.

J'ai posé une question similaire l'autre jour, mais je pense que cela pourrait être posé de manière plus concise. Merci beaucoup!

MODIFIER

Ok, j'ai recommencé pour rendre cela aussi simple que possible. J'ai une vue nommée "Cell" et un UIImageView nommé "chien" dans la cellule, et c'est tout. Je ne suis plus «incapable de satisfaire simultanément les contraintes» dans la console, mais deux vues simples utilisant la mise en page automatique. J'essaie toujours d'utiliser ce code pour contourner le UIImageView:

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

Voici la configuration de la contrainte de cellule:

 screenshot 1

Voici la configuration de la contrainte d'image de profil:

 screenshot 2

Voici le résultat sans le code, pas d'arrondi, mais joli et carré:

 screenshot 3

Voici le résultat avec le code à arrondir:

 screenshot 4

Cela n’a aucun sens pour moi, car sans le code d’arrondi, l’image est carrée et avec le code, elle a la forme d’un losange. Si c'est carré, ne devrait-il pas s'agir d'un cercle sans problème?

EDIT 2

Voici ce qui se passe lorsque je supprime la contrainte du bas et ajoute un multiplicateur de 0,637 pour une hauteur égale à superview.

 screenshot 5

38
CHM

Malheureusement, vous ne pouvez pas utiliser cornerRadius et autolayout. La CGLayer n'est pas affectée par autolayout. Toute modification de la taille de la vue ne modifie donc pas le rayon défini une fois, ce qui entraîne la perte de forme du cercle. .

Vous pouvez créer une sous-classe personnalisée de UIImageView et remplacer layoutSubviews afin de définir cornerRadius chaque fois que les limites de la vue image sont modifiées.

MODIFIER

Un exemple pourrait ressembler à quelque chose comme ceci:

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

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

        self.layer.cornerRadius = radius
    }
}

Et bien évidemment, vous devriez contraindre la largeur de l'instance Foobar à la même hauteur que la hauteur (pour conserver un cercle). Vous voudrez probablement aussi définir Foobar de l'occurrence contentMode pour UIViewContentMode.ScaleAspectFill afin qu'il sache dessiner l'image (cela signifie que l'image est susceptible d'être recadrée).

65
Rupert

La définition du rayon dans viewWillLayoutSubviews résoudra le problème.

override func viewWillLayoutSubviews() {
  super.viewWillLayoutSubviews()
  profileImageView.layer.cornerRadius = profileImageView.frame.height / 2.0
}
30
Omar Albeik

créer une nouvelle interface dans votre fichier .h comme

@interface CornerClip : UIImageView

@end

et la mise en œuvre dans un fichier .m comme

@implementation cornerClip


-(void)layoutSubviews
{
    [super layoutSubviews];

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

    self.layer.cornerRadius = radius;

}

@end

donnez simplement la classe en tant que "CornerClip" à votre imageview . 100% de travail ... Enjoy

10
Mr.Javed Multani

Il semble que lorsque vous ajoutez une vue en tant que sous-vue d'une autre vue, cette vue ne présente pas nécessairement la même hauteur que son aperçu. C'est ce que le problème semble être. La solution consiste à ne pas ajouter votre image View en tant que sous-vue, mais à la placer au-dessus de votre backgroundView. Dans l'image ci-dessous, j'utilise un UILabel comme backgroundView.

 screenshot

Également dans votre cas, lorsque vous définissez le cornerRadius, utilisez ceci: let radius: CGFloat = self.bounds.size.height / 2.0.

4
Randel G. Smith

Tout d’abord, je dois mentionner que vous pouvez obtenir une forme de cercle pour votre UIView/UIImageView uniquement si la largeur et la hauteur seront égales. C'est important de comprendre. Dans tous les autres cas (lorsque width! = Height), vous n'obtiendrez pas une forme de cercle car la forme initiale de votre instance d'interface utilisateur était un rectangle.

OK, UIKit SDK fournit aux développeurs un mécanisme permettant de manipuler l’instance layer d’UIview afin de modifier les paramètres de la couche, notamment la configuration d’un mask pour remplacer la forme initiale de l’élément UIView par un. Ces instruments sont IBDesignable/IBInspectable. L'objectif est de prévisualiser nos vues personnalisées directement via Interface Builder.

Donc, en utilisant ces mots-clés, nous pouvons écrire notre classe personnalisée, qui ne traitera que de la seule condition, que nous ayons besoin d'arrondir les coins ou non de notre élément d'interface utilisateur. 

Par exemple, créons la classe étendue à partir de 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
        }

    }

}

Après avoir défini le nom de classe de votre élément UIImageView (où se trouve la photo du chien), une nouvelle option apparaît dans votre storyboard. Elle apparaît dans le menu Inspecteur des attributs (détails sur la capture d'écran).

 

Le résultat final devrait être comme celui-ci.

 

3
Hamsternik

Avec ma solution de hacky, vous obtiendrez une animation en douceur du rayon des coins lors du changement de taille d'image.

Disons que vous avez ViewSubclass: UIView. Il devrait contenir le code suivant:

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
    }
}

Ensuite, vous devrez faire ceci:

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

Ce n'est pas beau et peut ne pas fonctionner pour des animations multiples complexes, mais répond à la question.

1
Oleksii Nezhyborets

J'ai le même problème, et maintenant je comprends ce qui est arrivé ici, j'espère que quelques idées pourront vous aider: Il y a quelque chose de différent entre les deux remorques:

  1. votre profileImageView dans le storyboard
  2. votre profileImageView in viewDidLoad

la taille de la reliure et du cadre est différente lorsque viewDidLoad et dans le storyboard, simplement parce que view est redimensionné pour une taille de périphérique différente . Vous pouvez l'essayer print(profileImageView.bounds.size) dans viewDidLoad et viewDidAppear vous trouverez la taille dans viewDidLoad que vous avez définie cornerRadius n'est pas la réalité "en cours" taille. 

quelques conseils pour vous: vous pouvez utiliser une sous-classe d’ImageView pour l’éviter, ou ne pas l’utiliser dans le storyboard,

0
livinspring

J'ai ajouté IBInspectable cornerRadiusPercent personnalisé pour pouvoir le faire sans code.

 enter image description here

  1. extension

    extension UIView {
    
    @IBInspectable var cornerRadiusPercent: CGFloat {
        get {
           return 0
        }
        set {
            if newValue == 0
            {
                 self.setObjectTag(nil)
                return
            }
    
            let watcher = CornerRadiusPercent(view: self, percent: newValue)
    
            self.setObjectTag(watcher)
        }
    }
    }
    
  2. CornerRadiusPercent

    class CornerRadiusPercent : NSObject{
    
    weak var view:UIView?
    let percent:CGFloat
    
    init(view:UIView, percent:CGFloat) {
        self.view = view
        self.percent = percent
    
        super.init()
    
        view.layer.cornerRadius = view.bounds.width * percent
        view.addObserver(self, forKeyPath: "bounds", options: [], context: nil)
    }
    
    deinit {
        view?.removeObserver(self, forKeyPath: "bounds")
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    
        if keyPath == "bounds", let view = self.view
        {
            view.layer.cornerRadius = view.bounds.width * percent            
            view.setObjectTag(nil)
        }
    
    }
    
    }
    
  3. ObjectTag

    extension NSObject {
    
    fileprivate struct ObjectTagKeys {
        static var ObjectTag = "ObjectTag"
    }
    
    func setObjectTag(_ tag:Any!) {
        objc_setAssociatedObject(self, &ObjectTagKeys.ObjectTag, tag, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
    
    func getObjectTag() -> Any
    {
        return objc_getAssociatedObject(self, &ObjectTagKeys.ObjectTag)
    }
    
    }
    
0
Chen Jiling

Si vous avez sous-classé UIImageView. Ensuite, ajoutez simplement ce morceau de code magique. 

Written in: Swift 3  

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

Swift 4+ solution propre basée sur la réponse d'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
    }
}

Exemple d'utilisation dans UIViewController

1. UIImageView simplement arrondi

override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        imageView.setRounded()
    }

2. UIImageView arrondi avec largeur et couleur de bordure

override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        imageView.setRounded(borderWidth: 1.0, borderColor: UIColor.red)
    }
0
Maverick

Je suis assez nouveau pour le développement natif iOS, mais j'ai eu le même problème et j'ai trouvé une solution.

 this was the goal

Donc, le fond vert a ces contraintes:

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

Les contraintes de l'image:

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

sur la méthode viewWillLayoutSubviews() je règle le rayon de cointo

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

Fondamentalement, je calcule simplement la hauteur de l'image, puis je la divise par 2. 0,2 est le multiplicateur de contrainte de hauteur de backgroungView et 0,8 le multiplicateur de contrainte de largeur/hauteur de l'image.

0
Vasil Kanev