当设备方向更改 SWIFT 5 时重绘自定义 UIVIEW

Posted

技术标签:

【中文标题】当设备方向更改 SWIFT 5 时重绘自定义 UIVIEW【英文标题】:redraw a custom UIVIEW when device orientation changed SWIFT 5 【发布时间】:2020-07-11 23:29:32 【问题描述】:

我为新的 UIVIEW 编写了代码,但是在设备旋转后,UIVIEW 的绘制没有被清理以适应新的情况!并保持对用户可见!看图片。我们如何解决这个问题?

导入 UIKit

class ViewController: UIViewController

    
    override func viewDidLoad()
    
        super.viewDidLoad()
    
    

    override func viewDidLayoutSubviews()
    

        var drawView = UIView()

        let drawViewOffsetFromTop = max( 25 , Int( view.safeAreaInsets.top ) )
        let drawViewOffsetFromBottom = max( 25 , Int( view.safeAreaInsets.bottom ) )
        
        let drawViewWidth = Int( view.frame.size.width ) - 2*25
        let drawViewHeight = Int( view.frame.size.height) - drawViewOffsetFromTop - drawViewOffsetFromBottom

        drawView = UIView(frame: CGRect(x: 25 , y: drawViewOffsetFromTop, width: drawViewWidth  , height: drawViewHeight ))
        drawView.backgroundColor = UIColor.gray
        drawView.layer.cornerRadius = 15
        view.addSubview(drawView)
   
    
    
    


【问题讨论】:

【参考方案1】:

您可以使用 Autolayout 让事情变得简单。 完全移除覆盖viewDidLayoutSubviews

viewDidLoad中使用以下内容

override func viewDidLoad()

    super.viewDidLoad()
    var drawView = UIView()

    //Setup Constraints for `drawView`
    drawView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        drawView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 25),
        drawView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -25),
        drawView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 15),
        drawView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -15)
    ])


编辑:没有自动布局的解决方案 问题是您的 drawView 在视图层次结构中被多次添加!您可以只添加一次,然后只更改框架。

class ViewController: UIViewController 
        
    var drawView = UIView()

    override func viewDidLoad()
    
        super.viewDidLoad()
        view.addSubview(drawView)
        drawView.backgroundColor = UIColor.gray
        drawView.layer.cornerRadius = 15
    


    override func viewDidLayoutSubviews()
    


        let drawViewOffsetFromTop = max( 25 , Int( view.safeAreaInsets.top ) )
        let drawViewOffsetFromBottom = max( 25 , Int( view.safeAreaInsets.bottom ) )
        
        let drawViewWidth = Int( view.frame.size.width ) - 2*25
        let drawViewHeight = Int( view.frame.size.height) - drawViewOffsetFromTop - drawViewOffsetFromBottom
        
        drawView.frame = CGRect(x: 25 , y: drawViewOffsetFromTop, width: drawViewWidth  , height: drawViewHeight )

    


【讨论】:

谢谢!但是您的代码不是问题的答案,我可以先使用您的代码,然后再在这里提问!这个问题的重点不是使用约束!但要画一个新的观点!新方向的诅咒摆脱旧观点!但我提供你的时间和代码! 哦,好吧。问题是 viewDidLayoutSubviews 可以为视图控制器多次调用。每次调用它时,都会将一个新视图添加到您的视图层次结构中,但您添加的视图永远不会被删除。您可以在视图控制器中初始化drawView 而不是viewDidLayoutSubviews,将其作为子视图添加到viewDidLoad 中,这样一次只有1 个drawView,并且只在需要时设置框架! 尝试初始化 drawView 并将其添加为 viewDidLoad 中的子视图,并且仅在需要时更改帧 :) 您也可以将帧的更改放在动画块中,这样它就会显示出优雅的变化UI 中的框架。 因为 safeAreaInsets,我正在使用 viewDidLayoutSubviews!如果我使用 viewDidLoad,那么我的 safeAreaInsets 值可能为零!这就是我使用 viewDidLayoutSubviews 的原因!您的代码在横向模式下也会出现问题!因为缺口!你看过横向模式的屏幕吗?我可以通过在旧绘图之上添加新的全视图背景并绘制新视图来解决我的问题!但我有兴趣找到更好的方法 您仍然可以使用viewDidLayoutSubviews设置drawView的框架。我想说的是,每次调用viewDidLayoutSubviews 时(可以多次调用),您都在向视图层次结构中添加新视图。因此,如果您愿意,只需添加一次视图并在 viewDidLayoutSubviews 中单独更改框架。使用drawView.frame = //your frame【参考方案2】:

首先,您应该不要将创建 UIView 的代码放在viewDidLayoutSubviews 中,而是在viewDidLoad 中创建并添加它。您可以将视图框架更新代码放入viewDidLayoutSubviews

问题是您多次添加drawView 而没有删除现有的。此外,您不必要地在不同的行中多次初始化 drawView

要解决此问题,请在您的控制器中创建drawView,将其添加到viewDidLoad 中的superview 并编辑您的viewDidLayoutSubviews,如下所示:

class ViewController: UIViewController 
    private var drawView : UIView = UIView()

    override func viewDidLoad() 
        super.viewDidLoad()
        view.addSubview(drawView)
        
    
    override func viewDidLayoutSubviews() 
        let drawViewOffsetFromTop = max(25 , Int(view.safeAreaInsets.top))
        let drawViewOffsetFromBottom = max(25 , Int(view.safeAreaInsets.bottom))
        
        let drawViewWidth = Int(view.frame.size.width ) - 2*25
        let drawViewHeight = Int(view.frame.size.height) - drawViewOffsetFromTop - drawViewOffsetFromBottom

        drawView.frame = CGRect(x: 25, y: drawViewOffsetFromTop, width: drawViewWidth, height: drawViewHeight)
        drawView.backgroundColor = UIColor.gray
        drawView.layer.cornerRadius = 15
    

【讨论】:

我已经向@Rishabh 解释了为什么我不能使用 viewDidLoad()!你说把我的代码放在 viewDidLoad() 中,然后从 viewDidLayoutSubviews() 触发 viewDidLoad()。所以我做了我没有帮助!试过你的想法了吗?是的!每次我在 viewDidLayoutSubviews() 中绘制一个新视图。我是如何通过画全屏并重新绘制来解决问题的! 是的,我尝试了我编写的解决方案,发现问题已解决。你是否?您应该添加一次子视图,然后在 viewDidLayoutSubviews 中编辑布局。否则,即使您没有看到它,也会有不必要的多个视图。您可以使用 Debug View Hierarchy 检查您添加的视图 不!我觉得不对!正如@Rishabh 所说! viewDidLayoutSubviews 仅用于绘制而不使其可编辑!但是使用 viewDidLoad 我们可以编辑它! viewDidLoad 的问题在于缺口!我无法从 viewDidLoad 访问 safeAreaInsets。正如我所说,没有办法删除或取消添加到子视图的视图!唯一的办法就是盖住它!绘制渐变时会非常方便!当你改变你的方向时,渐变也会出现同样的问题!最好的方法是覆盖它并重新绘制!【参考方案3】:
//▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
var drawView = UIView()
//▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼


//█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
override func viewDidLoad()

    super.viewDidLoad()
    view.backgroundColor = UIColor.gray

//█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █



//█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
override func viewDidLayoutSubviews()


    //▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
    let backView = UIView(frame: CGRect(x: 0 , y: 0, width: view.frame.size.width  , height: view.frame.size.height ))
    backView.backgroundColor = UIColor.gray
    view.addSubview(backView)
    //▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼


    //▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
    let drawViewOffsetFromTop = max( 25 , Int( view.safeAreaInsets.top ) )
    let drawViewOffsetFromBottom = max( 25 , Int( view.safeAreaInsets.bottom ) )
    let drawViewOffsetFromRight = max( 25 , Int( view.safeAreaInsets.right ) )
    let drawViewOffsetFromLeft = max( 25 , Int( view.safeAreaInsets.left ) )
    //▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
    

    //▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
    let drawViewWidth = Int( view.frame.size.width ) - drawViewOffsetFromRight - drawViewOffsetFromLeft
    let drawViewHeight =  Int( view.frame.size.height) - drawViewOffsetFromTop - drawViewOffsetFromBottom
    //▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
    
    //▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
    drawView = UIView(frame: CGRect(x: drawViewOffsetFromRight , y: drawViewOffsetFromTop, width: drawViewWidth  , height: drawViewHeight ))
    //▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼

    
    //▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
    drawView.backgroundColor = UIColor.systemTeal
    drawView.layer.cornerRadius = 12
    drawView.layer.borderWidth = 5
    drawView.layer.borderColor = UIColor.black.cgColor
    view.addSubview(drawView)
    //▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼

    

    


//█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █

【讨论】:

以上是关于当设备方向更改 SWIFT 5 时重绘自定义 UIVIEW的主要内容,如果未能解决你的问题,请参考以下文章

在自定义属性更改时重绘自定义 CALayer 子类

将 CALayer 添加到 UIView 并能够在方向更改时重绘

当设备方向改变时导致子视图重绘的正确方法是啥?

SwiftUI:在设备方向上重绘会使计时器无效

量角器:从标准角度GRID中获取数据,在向左或向右滚动时重绘

防止在 UIScrollView 的最大/最小缩放时重绘屏幕