Swift:更改 UIView 框架但显示不更新

Posted

技术标签:

【中文标题】Swift:更改 UIView 框架但显示不更新【英文标题】:Swift: Changing UIView frame but display doesn't update 【发布时间】:2016-06-07 14:29:51 【问题描述】:

我正在尝试动态调整 UIView 的框架大小。我可以更新代码中的框架并查看值已更改,但实际显示从未更改,即使我运行 setNeedsDisplay()。

我正在关注this post,在这里我通过CGRectMake 定义了一个新框架,然后将UIView 的框架设置为这个新框架。即,我正在这样做......

let newFrame = CGRectMake(minX,minY, width, height)
VUBarCover.frame = newFrame

如果我打印出 UIView 框架的值,我会看到它正在随我所愿地改变。但显示(在模拟器中)从不反映这些变化。

知道如何解决这个问题吗?

更新: 更详细地说:我正在尝试以编程方式用另一个 UIView 覆盖一个 UIView。它是一个水平的“VU Meter”,由一个显示颜色渐变的基本 UIImageView 组成,它被黑色背景的 UIView 部分动态地“覆盖”。 UIImageView "VUBarImageView" 在 StoryBoard 中定义并受 AutoLayout 约束。黑色的 UIView "VUBarCover" 是纯粹以编程方式定义的,没有 AutoLayout 约束。

为了完整起见,这是代码的相关部分...我遇到的麻烦在于 updateVUMeter()...

@IBOutlet weak var VUBarImageView: UIImageView!
var VUBarCover : UIView!

// this gets called after AutoLayout is finished
    override func viewDidLayoutSubviews() 
        // cover up VU Meter completely
        let center = VUBarImageView.center
        let width = VUBarImageView.frame.width
        let height = VUBarImageView.frame.height
        let frame = VUBarImageView.frame
        VUBarCover = UIView(frame: frame)
        MainView.addSubview(VUBarCover)
        VUBarCover.backgroundColor = UIColor.blueColor() // use black eventually. blue lets us see it for testing
    

 // partially cover up VUBarImage with black VUBarCover coming from the "right"
func updateVUMeter(inputValue:Float)    //inputValue is in dB

    //let val = pow(10.0,inputValue/10)  // convert from dB
    let val = Float( Double(arc4random())/Double(UInt32.max) ) //for Simulator testing, just use random numbers between 0 and 1
    print("val = ",val)

    let fullWidth = VUBarImageView.frame.width
    let maxX = VUBarImageView.frame.maxX
    let width = fullWidth * CGFloat(1.0-val)
    let minX = maxX - width
    let minY = VUBarImageView.frame.minY
    let height = VUBarImageView.frame.height

    let newFrame = CGRectMake(minX,minY, width, height)
    VUBarCover.frame = newFrame
    VUBarCover.setNeedsDisplay()

    print("fullWidth = ",fullWidth,"width = ",width)
    print(" newFrame = ",newFrame,"VUBarCover.frame = ",VUBarCover.frame)

样本结果为:

val =  0.9177
fullWidth =  285.0 width =  23.4556210041046
newFrame =  (278.544378995895, 93.5, 23.4556210041046, 10.0) VUBarCover.frame =  (278.544378995895, 93.5, 23.4556210041046, 10.0)
val =  0.878985
fullWidth =  285.0 width =  34.4891595840454
newFrame =  (267.510840415955, 93.5, 34.4891595840454, 10.0) VUBarCover.frame =  (267.510840415955, 93.5, 34.4891595840454, 10.0)
val =  0.955011
fullWidth =  285.0 width =  12.8218790888786
newFrame =  (289.178120911121, 93.5, 12.8218790888786, 10.0) VUBarCover.frame =  (289.178120911121, 93.5, 12.8218790888786, 10.0)

...即,我们看到 VUBarCover.frame 正在“内部”更改,但在屏幕上它从未更改。相反,我们看到的是全尺寸封面,完全覆盖了下面的图像(大约 300 像素宽),尽管它的宽度应该只有大约 12 像素)。

即使我在 VUBarCover 的超级视图上执行 setNeedsDisplay(),也没有任何变化。

帮助?谢谢。

【问题讨论】:

【参考方案1】:

首先,您有几个值得注意的错误:

实例变量(如VUBarCoverMainView 应始终以小写字母开头(如mainView)。 您应该在覆盖实施开始时调用super.viewDidLayoutSubviews()

其次,viewDidLayoutSubviews() 可以被调用任意次数,所以你对这个方法的实现应该是幂等的,这意味着无论调用多少次它都具有相同的效果。但是当你打电话时:

VUBarCover = UIView(frame: frame)
MainView.addSubview(VUBarCover)

您正在创建一个 UIView 并将其与您过去可能添加的其他内容一起添加到视图层次结构中。所以它看起来好像框架没有在屏幕上更新。事实上,您只是无法判断,因为 旧的 视图位于您正在更新的视图之后,而 它们 并没有改变。

相反,您可能想创建一次:

var vuBarCover = UIView()

将其添加为viewDidLoad()中的子视图:

mainView.addSubview(vuBarCover)

并在viewDidLayoutSubviews()修改其框架:

vuBarCover.frame = vuBarImageView.frame

您无需致电setNeedsDisplay()

【讨论】:

亚伦,感谢您的详细回答。忽略我对全局变量首字母大写的风格选择,我已经实现了您所描述的内容,不同之处在于现在,VUBarCover 在显示屏上显示为宽度为零,即它根本不覆盖图像,否则同样的问题适用:“内部”我可以通过打印语句看到框架发生变化,但在显示屏上没有任何反应。 也许它被另一个视图裁剪了?您是否尝试过使用新的Debug View Hierarchy 功能?我唯一能想到的另一件事是,也许你是从后台线程而不是主线程调用updateVUMeter 后台线程是我的假设。 @sh37211,什么代码在调用updateVUMeter?【参考方案2】:

在我的例子中,图层上留下了动画,删除它们后,帧更改会更新:

myView.layer.removeAllAnimations()

ios 在屏幕上显示UIView 中的presentationLayer,动画在该层上播放(frame 在动画期间更新到那里)。

【讨论】:

【参考方案3】:

我现在正在制作一个像你一样的自定义 UIView,也许我可以帮忙。

首先,viewDidLayoutSubviews() 喜欢您的评论:在 AutoLayout 完成后调用它。在每个布局都设置好之后,你开始为你的 UIView 和 UIImage 设置新的框架,也许这个 bug 就在这里。请尝试将 viewDidLayoutSubviews() 中的代码移动到 layoutSubviews()。并且不要忘记在 override 函数中添加 super.layoutSubviews(),它可以帮助您远离数百个错误:]。

其次,只有当你重写 drawRect: 方法时,你才应该调用 setNeedsDisplay(),它告诉系统我需要重绘我的视图,系统将适当地调用 drawRect: 方法。

希望你能弄清楚。

【讨论】:

以上是关于Swift:更改 UIView 框架但显示不更新的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 Swift 中使用惰性 var 初始化时添加框架不显示 UIView

在 Swift 运行时更改自动布局约束的 UIView 的框架

Swift 无法更改 xib uiview 中的 imageview 框架

Swift - UIView 完成更新其框架时?

在滚动视图中更改 UIView 而 / 更新滚动视图高度 - Swift 3 / Xcode 8

ViewController 操作后,Swift UIView 自定义视图不会更新