【问题标题】:How to keep a round imageView round using auto layout?如何使用自动布局保持圆形 imageView 圆形?
【发布时间】:2015-11-28 12:56:11
【问题描述】:

如何在不设置宽度和高度限制的情况下将矩形图像视图转换为可以在自动布局中保持形状的圆形图像视图?从而允许 imageView 定义它的大小,以及相对于它周围的对象的大小,并具有前导、尾随、顶部和底部约束。

前几天我问了一个类似的问题,但我认为这可能会以更简洁的方式提出。非常感谢!

编辑

好的,我重新开始,让这一切尽可能简单。我在单元格中有一个名为“Cell”的视图和一个名为“dog”的 UIImageView,就是这样。我在控制台中不再有“无法同时满足约束”,只有两个使用自动布局的简单视图。我仍在尝试使用此代码来舍入 UIImageView:

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

这是单元格约束设置:

这是配置文件图片约束设置:

这是没有代码的结果,没有四舍五入,但又漂亮又方形:

这是带有要四舍五入的代码的结果:

这对我来说毫无意义,因为没有四舍五入代码图像是方形的,而代码是菱形的。如果它是方形的,它不应该是一个没有问题的圆形吗?

编辑 2

当我移除底部约束并将 0.637 的乘数添加到超级视图的相等高度时,会发生以下情况。

【问题讨论】:

  • 您目前如何使图像视图变圆?
  • @GavinHope 我将它调用到 tableviewcontroller 中的一个单元格中并使用: cell.ProfileImageView.layer.cornerRadius = cell.ProfileImageView.frame.size.width / 2 cell.ProfileImageView.clipsToBounds = true
  • 如果您的图像视图有 Width != Height 那么您可以在这里找到解决方案:stackoverflow.com/questions/29685055/…

标签: ios swift xcode autolayout ios-autolayout


【解决方案1】:

不幸的是,你不能使用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 实例的宽度限制为与高度相同(以保持圆形)。您可能还希望将Foobar 实例的contentMode 设置为UIViewContentMode.ScaleAspectFill,以便它知道如何绘制图像(这意味着图像可能会被裁剪)。

【讨论】:

  • 嗨@Rupert 感谢您提供信息!我只是被 Swift 弄湿了。这听起来有点超出我的想象。这看起来如何......或者你是否有一个链接到具有类似解决方案的东西,我可以直观地查看?
  • 非常感谢!当您说“将 Foobar 实例的宽度限制为与高度相同”时,设置 1:1 的纵横比是否有效,或者我必须给它一个绝对宽度和高度?
  • 在你的子类中,例如,你可以添加一个约束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)
  • 一年多来我一直在寻找答案!非常,非常,非常感谢!
  • 谢谢。并为情节提要上的图像选择 Clip To Bounds。
【解决方案2】:

在 viewWillLayoutSubviews 中设置半径即可解决问题

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

【讨论】:

  • 这应该是公认的答案。简单实用符合 Apple 的理念,完美运行。
  • 奥马尔·阿尔贝克谢谢你:)
  • 应该是viewDidLayoutSubviews
  • viewWillLayoutSubviews() 或 viewDidLayoutSubviews() 都会在 viewController 中的每个 UI 更改时触发,例如,在键盘上键入时,这两种方法都会在每次点击时触发。在这种情况下我们应该使用这些方法吗?那安全吗?我想在自动布局中具有纵横比的按钮的角半径
【解决方案3】:

在您的 .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% 工作...享受

【讨论】:

    【解决方案4】:

    首先,我应该提到你可以为你的 UIView/UIImageView 获得一个圆形只有如果宽度和高度相等。了解这一点很重要。在所有其他情况下(当 width != height 时),您不会得到圆形,因为您的 UI 实例的初始形状是矩形。

    好的,因此 UIKit SDK 为开发人员提供了一种机制来操作 UIview 的 layer 实例以以某种方式更改任何 layer 的参数,包括设置 mask 来替换 UIView 的初始形状元素与自定义元素。这些工具是IBDesignable/IBInspectable。目标是直接通过 Interface Builder 预览我们的自定义视图。

    因此,使用这些关键字,我们可以编写自定义类,它只处理单个条件,无论我们是否需要为 UI 元素圆角。

    例如,让我们创建从 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 菜单中(详细信息在屏幕截图中) .

    最终的结果应该是这样的。

    【讨论】:

      【解决方案5】:

      似乎当您将一个视图添加为另一个视图的子视图时,网状视图不一定具有与其父视图相同的高度。这就是问题的样子。解决方案是不要将您的 imageView 添加为子视图,而是将其放在您的 backgroundView 之上。在下图中,我使用 UILabel 作为背景视图。

      在您的情况下,当您设置cornerRadius 时,请使用:let radius: CGFloat = self.bounds.size.height / 2.0

      【讨论】:

      • 啊……我无法让它工作。我根据您所说的使用此代码从容器中尝试了它: let radius: CGFloat = profileImageView.bounds.size.height / 2.0 profileImageView.clipsToBounds = true
      • 这是显示我所做的 xcode 项目。 github.com/RGSSoftware/ImageViewConstraints
      • Randel,这样做完全可以。我唯一的问题是我在自定义 tableview 单元格中运行这些图像,这些单元格有自己的类文件,元素设置为 IBOutlets,所以我无法弄清楚如何使用 func viewDidLayoutSubviews,因为我将它们动态调用到 tableView 控制器中cellForRowAtIndexPath 和使用 dequeueReusableCellWithIdentifier
      【解决方案6】:

      使用我的 hacky 解决方案,您将获得平滑的圆角半径动画以及帧大小的变化。

      假设你有 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()
      })
      

      它不漂亮,可能不适用于复杂的多动画,但确实回答了这个问题。

      【讨论】:

        【解决方案7】:

        Swift 4+ 基于omaralbeik's answer的干净解决方案

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

        【讨论】:

          【解决方案8】:

          写这段代码

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

          在这种情况下,它会在计算第​​一个代码中的自动布局计算之后调用,在计算视图的实际大小之前调用cornerradius代码,因为它是使用纵横比动态计算的,实际角半径是在等于宽度之前计算的,并且视图的高度

          【讨论】:

            【解决方案9】:

            我有同样的问题,现在我知道这里发生了什么,希望一些想法可以帮助你: 拖车之间有一些不同:

            1. 情节提要中您的 profileImageView
            2. viewDidLoad 中的您的 profileImageView

            viewDidLoad 和 storyboard 中的 bound 和 frame 的大小是不同的,只是因为 view 针对不同的设备大小进行了调整。 您可以在viewDidLoadviewDidAppear 中尝试print(profileImageView.bounds.size),您会发现在viewDidLoad 中设置的大小cornerRadius 并不是真正的“运行”大小。

            给您的提示: 您可以使用 ImageView 的子类来避免它,或者不要在情节提要中使用它,

            【讨论】:

              【解决方案10】:

              如果您已对 UIImageView 进行了子类化。然后只需在其中添加这段神奇的代码。

              编写于:Swift 3

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

              【讨论】:

                【解决方案11】:

                我是 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 是背景视图高度约束乘数,0.8 是图像宽度/高度约束乘数。

                【讨论】:

                  【解决方案12】:

                  解决方案裁剪图像

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

                  我一直在为个人资料图片寻找相同的解决方案。经过一番尝试并尝试了可用功能后,我发现了一些可行的方法,并且是确保其安全性的好方法。您可以使用以下功能从原始图像中裁剪出圆形图像,然后您不必担心圆角半径。

                  即使您的视图大小发生变化,也发布此图像,图像仍然是圆形的并且看起来不错。

                  【讨论】:

                    【解决方案13】:

                    imageView 添加 1:1 纵横比约束,使其保持圆形,尽管有任何高度或宽度变化。

                    【讨论】:

                    • 嗨@petahchristian 我锁定了 1:1 的比例,并且还使用了顶部、前导、尾随和底部约束,但是当我缩小或放大时,我失去了完美的圆圈。当我变大时,我会得到一个长椭圆形,而更小,则更像是一个菱形
                    • 您是否在控制台中收到“无法同时满足约束”?如果您有顶部、前导、尾随和底部约束,那么这些约束可能会拉动或推动您的 imageView 导致纵横比约束停止。
                    • @CHM 是的。正如 Randel 指出的那样,您过度限制了 imageView。你不能让它在 4 个方向上的大小也保持圆形。
                    • @RandelG.Smith 是的,我在控制台中得到了它。明天我将运行一些约束测试,并逐个添加以查看是什么破坏了它。谢谢你们的帮助。等我弄明白了再更新。
                    • @RandelG.Smith 我在原始问题中添加了简化图。看一看,它更直观地描述了正在发生的事情。
                    【解决方案14】:

                    我添加了自定义 IBInspectablecornerRadiusPercent,因此您无需任何代码即可完成。

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

                    【讨论】:

                      【解决方案15】:

                      可以通过为需要在运行时更改的约束创建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
                          }
                      }
                      

                      【讨论】:

                      • 这太糟糕了XD
                      猜你喜欢
                      • 2015-08-01
                      • 2019-12-07
                      • 2020-05-25
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2010-10-28
                      • 1970-01-01
                      相关资源
                      最近更新 更多