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:
Voici la configuration de la contrainte d'image de profil:
Voici le résultat sans le code, pas d'arrondi, mais joli et carré:
Voici le résultat avec le code à arrondir:
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.
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).
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
}
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
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.
Également dans votre cas, lorsque vous définissez le cornerRadius, utilisez ceci: let radius: CGFloat = self.bounds.size.height / 2.0
.
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.
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.
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:
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,
J'ai ajouté IBInspectable cornerRadiusPercent personnalisé pour pouvoir le faire sans code.
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)
}
}
}
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)
}
}
}
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)
}
}
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
}
}
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)
}
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.
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.