如何将UIView作为带有Xcode 8的圆角半径的圆

Mat*_*lak 10 objective-c cornerradius uiview ios

我在视图上设置角半径时遇到问题,因为自从Xcode更新为8以来,视图的帧在大多数情况下设置为1000x1000而不是适当的大小.

这里有一个类似的问题,但我想补充一点信息,希望有人找到答案或解决方法.

在其中一个实例中,我有一个表格视图单元格,其中包含在故事板中创建的图像视图.它的宽度和高度设置为约束常数(至95).角半径设置在layoutSubviews现在工作的范围内:

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.myImageView.layer.cornerRadius = self.myImageView.frame.size.width/2.0f;
}
Run Code Online (Sandbox Code Playgroud)

所以我在试验和错误中玩了一下,如果我尝试通过调用以下方法设置映像(从远程服务器检索图像)后尝试触发布局,情况似乎会好一点.

self.myImageView.image = image;
[self.myImageView setNeedsLayout];
[self setNeedsLayout];
[self layoutIfNeeded];
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,这仍然不起作用.现在有趣的部分:

我在标签栏上有一个这个视图控制器的情况,当我按下目标视图控制器的标签时,将立即调用布局子视图(从UIApplicationMain)图像视图的大小为1000x1000但是一旦设置了图像然后我调用布局方法然后再次调用布局子视图,正确的图像视图大小为95x95,得到正确的结果.

第二种情况是推动相同类型的视图控制器,现在首先不调用布局,在图像设置和强制布局之后进行唯一的调用.结果是再次具有大小为1000x1000的图像视图,将角半径设置为500并且图像不可见.

那么有一个很好的解决方案来修复这些奇怪的帧值吗?我在多个视图控制器,表视图单元格上遇到同样的问题.这一切都在更新之前一直有效.

谁有任何想法甚至是这个问题的根源是什么?是故事板还是某种设置迁移,还是这个版本的软件引入了一些布局错误?

我现在还从笔尖添加了清醒:

- (void)awakeFromNib
{
    [super awakeFromNib];

    [self.myImageView setNeedsLayout];
    [self setNeedsLayout];
    [self layoutIfNeeded];
}
Run Code Online (Sandbox Code Playgroud)

现在强制布局在我的情况下发生至少2次,第二次框架是正确的.仍然这远远不是答案,这是非常糟糕的,我想要它,因为我需要在每个图像视图,按钮的所有单元格上使用它.

Pus*_*raj 10

你能把你的方法更新到吗?

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.myImageView.layer.cornerRadius = self.myImageView.frame.size.width/2.0f;
    self.myImageView.clipsToBounds = TRUE;
}
Run Code Online (Sandbox Code Playgroud)

并尝试一下.

因为在我的一个案例中,我错过了设置clipsToBounds = TRUE;并且没有让它成为圈子.


rob*_*off 6

我猜layoutSubviews问题中显示的覆盖是在UITableViewCell.

这里的问题可能myImageView不是单元格的直接子视图。

了解单元格的调用[super layoutSubviews]仅设置单元格的直接子视图的框架很重要。在返回之前,它不会一直沿着视图层次结构向下走。单元格通常只有一个直接子视图:它的contentView. 所以调用[super layoutSubviews]只设置内容视图的框架,然后返回。设置的尝试self.myImageView.layer.cornerRadius发生得太快了,因为self.myImageView.frame尚未更新。

稍后,在cell的layoutSubviews方法返回后,UIKit会向layoutSubviews内容视图发送消息,此时内容视图将设置自己的直接子视图的框架。那是什么时候myImageView的框架将被设置。

最简单的解决方法就是创建一个UIImageView子类并覆盖它layoutSubviews以设置自己的角半径。

@interface CircleImageView: UIImageView
@end

@implementation CircleImageView

- (void)layoutSubviews {
    [super layoutSubviews];
    self.layer.cornerRadius = self.bounds.size.width / 2;
}

@end
Run Code Online (Sandbox Code Playgroud)

更新

Matic Oblak 问道(在评论中),“这是你在这里写的可测试的吗”?

是的,它是可测试的。这是测试它的一种方法:创建一个具有根视图、根视图内的容器视图和容器视图内的叶视图的视图层次结构:

视图层次结构

覆盖layoutSubviews根视图和容器视图以打印视图的框架:

// RootView.m

@implementation RootView {
    IBOutlet UIView *_containerView;
    IBOutlet UIView *_leafView;
}

- (void)layoutSubviews {
    NSLog(@"%s before super: self.frame=%@ _containerView.frame=%@ _leafView.frame=%@", __FUNCTION__, NSStringFromCGRect(self.frame), NSStringFromCGRect(_containerView.frame), NSStringFromCGRect(_leafView.frame));
    [super layoutSubviews];
    NSLog(@"%s after super: self.frame=%@ _containerView.frame=%@ _leafView.frame=%@", __FUNCTION__, NSStringFromCGRect(self.frame), NSStringFromCGRect(_containerView.frame), NSStringFromCGRect(_leafView.frame));
}

@end

// ContainerView.m

@implementation ContainerView {
    IBOutlet UIView *_leafView;
}

- (void)layoutSubviews {
    NSLog(@"%s before super: self.frame=%@ _leafView.frame=%@", __FUNCTION__, NSStringFromCGRect(self.frame), NSStringFromCGRect(_leafView.frame));
    [super layoutSubviews];
    NSLog(@"%s after super: self.frame=%@ _leafView.frame=%@", __FUNCTION__, NSStringFromCGRect(self.frame), NSStringFromCGRect(_leafView.frame));
}

@end
Run Code Online (Sandbox Code Playgroud)

为了更好地衡量,让我们也看看视图控制器的viewWillLayoutSubviews和何时viewDidLayoutSubviews运行:

// ViewController.m

@implementation ViewController

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    NSLog(@"%s", __FUNCTION__);
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    NSLog(@"%s", __FUNCTION__);
}

@end
Run Code Online (Sandbox Code Playgroud)

最后,在与故事板不同大小的设备或模拟上运行它,并查看调试控制台:

-[ViewController viewWillLayoutSubviews]
-[RootView layoutSubviews] before super: self.frame={{0, 0}, {375, 667}} _containerView.frame={{20, 40}, {280, 420}} _leafView.frame={{20, 20}, {240, 380}}
-[RootView layoutSubviews] after super: self.frame={{0, 0}, {375, 667}} _containerView.frame={{20, 40}, {335, 607}} _leafView.frame={{20, 20}, {240, 380}}
-[ViewController viewDidLayoutSubviews]
-[ContainerView layoutSubviews] before super: self.frame={{20, 40}, {335, 607}} _leafView.frame={{20, 20}, {240, 380}}
-[ContainerView layoutSubviews] after super: self.frame={{20, 40}, {335, 607}} _leafView.frame={{20, 20}, {295, 567}}
Run Code Online (Sandbox Code Playgroud)

注意,根视图的[super layoutSubviews]改变容器视图的帧,但并没有改变它的叶视图的帧。然后,容器视图[super layoutSubviews]更改叶视图的大小。

因此,当layoutSubviews在一个观点上运行,你可以假设该视图的直接子视图的框架已更新,但你必须假设的任何更深层嵌套的看法帧都不会被更新。

另请注意,viewDidLayoutSubviews在视图控制器的视图完成之后运行layoutSubviews,但嵌套更深的后代运行之前layoutSubviews。所以viewDidLayoutSubviews,你必须再次假设的更深层嵌套的看法帧都不会被更新。

有一种方法可以强制立即更新更深层嵌套的子视图的框架:发送layoutIfNeeded到其超视图。例如,我可以修改-[RootView layoutSubviews]为发送layoutIfNeeded_leafView.superview

- (void)layoutSubviews {
    NSLog(@"%s before super: self.frame=%@ _containerView.frame=%@ _leafView.frame=%@", __FUNCTION__, NSStringFromCGRect(self.frame), NSStringFromCGRect(_containerView.frame), NSStringFromCGRect(_leafView.frame));
    [super layoutSubviews];
    NSLog(@"%s after super: self.frame=%@ _containerView.frame=%@ _leafView.frame=%@", __FUNCTION__, NSStringFromCGRect(self.frame), NSStringFromCGRect(_containerView.frame), NSStringFromCGRect(_leafView.frame));
    [_leafView.superview layoutIfNeeded];
    NSLog(@"%s after _leafView.superview: self.frame=%@ _containerView.frame=%@ _leafView.frame=%@", __FUNCTION__, NSStringFromCGRect(self.frame), NSStringFromCGRect(_containerView.frame), NSStringFromCGRect(_leafView.frame));
}
Run Code Online (Sandbox Code Playgroud)

(请注意,在此示例项目中,_leafView.superviewis _containerView,但通常可能不是。)结果如下:

-[ViewController viewWillLayoutSubviews]
-[RootView layoutSubviews] before super: self.frame={{0, 0}, {375, 667}} _containerView.frame={{20, 40}, {280, 420}} _leafView.frame={{20, 20}, {240, 380}}
-[RootView layoutSubviews] after super: self.frame={{0, 0}, {375, 667}} _containerView.frame={{20, 40}, {335, 607}} _leafView.frame={{20, 20}, {240, 380}}
-[ContainerView layoutSubviews] before super: self.frame={{20, 40}, {335, 607}} _leafView.frame={{20, 20}, {240, 380}}
-[ContainerView layoutSubviews] after super: self.frame={{20, 40}, {335, 607}} _leafView.frame={{20, 20}, {295, 567}}
-[RootView layoutSubviews] after _leafView.superview: self.frame={{0, 0}, {375, 667}} _containerView.frame={{20, 40}, {335, 607}} _leafView.frame={{20, 20}, {295, 567}}
-[ViewController viewDidLayoutSubviews]
Run Code Online (Sandbox Code Playgroud)

所以现在我已经强制 UIKit 更新_leafView之前-[RootView layoutSubviews]返回的框架。

至于“我假设在标签上设置简单文本等许多操作都会调用 setNeedsLayout”:您可以使用我刚刚演示的相同技术来找出该问题的答案。