【问题标题】:UIView frame not updating after orientation changeUIView 框架在方向更改后不更新
【发布时间】:2019-09-26 16:29:42
【问题描述】:

如何使用 Swift 3 检测方向变化?

我需要在更改后检测它以计算帧大小。

编辑

我问这个问题是因为我需要像这个问题一样重绘关于方向变化的视图:Can't get background gradient to fill entire screen upon rotation

我不知道如何实现标记为正确答案的答案。

我尝试了另一个答案

self.backgroundImageView.layer.sublayers?.first?.frame = self.view.bounds

但它不起作用。

viewDidLoad()我有

let color1 =  UIColor(red: 225.0/255.0, green: 210.0/255.0, blue: 0.0/255.0, alpha: 1.0).cgColor
let color2 = UIColor(red: 255.0/255.0, green: 125.0/255.0, blue: 77.0/255.0, alpha: 1.0).cgColor

let gradientLayer = CAGradientLayer()
gradientLayer.colors = [color1, color2]
gradientLayer.locations = [ 0.0, 1.0]
gradientLayer.frame = self.view.bounds

self.view.layer.insertSublayer(gradientLayer, at: 0)

【问题讨论】:

  • A. 你可以确保创建正确的视图层次结构并尽量不要破坏iOS自动支持的通知链;或B。您可以订阅UIDeviceOrientationDidChangeNotification 并听取方向变化并在必要时更新您的自定义视图。

标签: ios swift


【解决方案1】:

上面接受的答案在转换之前返回帧大小。所以您的视图没有更新..您需要在转换完成后获取帧大小。

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {

        coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in

            let orient = UIApplication.shared.statusBarOrientation

            switch orient {

            case .portrait:

                print("Portrait")

            case .landscapeLeft,.landscapeRight :

                print("Landscape")

            default:

                print("Anything But Portrait")
            }

            }, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
                //refresh view once rotation is completed not in will transition as it returns incorrect frame size.Refresh here           

        })
        super.viewWillTransition(to: size, with: coordinator)

    }

【讨论】:

  • 我尝试了很多方法,但找不到具有更新视图框架的解决方案。在视图框架之后执行完成处理程序这一事实解决了问题,您的回答真的很有帮助。
  • 如果您需要新的(旋转后)框架尺寸,这是一个很棒的解决方案!太感谢了。我唯一注意到的是这个委托方法有时会被调用两次。因此,为了防止不必要地刷新视图两次,我存储旧方向,并且只有当新方向不同时,我才会刷新视图。谢谢!
【解决方案2】:

要获取方向更改回调,您需要添加此通知

NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

你需要实现这个方法

func rotated() {
    if(UIDeviceOrientationIsLandscape(UIDevice.current.orientation))
    {
        print("landscape")
    }

    if(UIDeviceOrientationIsPortrait(UIDevice.current.orientation))
    {
        print("Portrait")
    }
}

【讨论】:

  • 太棒了!比viewWillTransition好多了,就像viewDidTransition(不可用)
【解决方案3】:

添加此功能,您将检测到方向变化:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    } else if UIDevice.current.orientation.isPortrait {
        print("Portrait")
    }
}

【讨论】:

  • 谢谢!它有效,但我有另一个问题,因为我首先问了这个问题。我更新了我的问题。可以看看吗?
  • 尝试在viewDidLoad 末尾添加self.view.setNeedsLayout()
【解决方案4】:

这是我的解决方案,基于这些要求:

(1) 我的布局需要根据更大的尺寸、高度或宽度进行更改。 (基本上是人像或风景,但将来……?)

(2) 我的应用程序是通用的,所以我无法考虑尺寸等级或与之相关的任何覆盖。

(3) 我的应用也有照片编辑扩展,所以 UIApplication 不可用。 (此外,landscapeLeft 和 LandscapeRight 等方向不适用于此类扩展。)

注意:为了完整起见,我正在为 AutoLayout 添加我的结构。

UIViewController:

var p = [NSLayoutConstraint]()
var l = [NSLayoutConstraint]()
var initialOrientation = true
var isInPortrait = false

override func viewDidLoad() {
    super.viewDidLoad()
    // set up all constraints, including common, portrait and landscape
    setUpConstraints()  
}

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    if initialOrientation {
        initialOrientation = false
        if view.frame.width > view.frame.height {
            isInPortrait = false
        } else {
            isInPortrait = true
        }
        orientationChanged()
    } else {
        if view.orientationHasChanged(&isInPortrait) {
            orientationChanged()
        }
    }
}

func orientationChanged() {
    // this was split out because of other app-specific logic
    view.setOrientation(p, l)
}

UIView:

extension UIView {

    public func orientationHasChanged(_ isInPortrait:inout Bool) -> Bool {
        // always check against isInPortrait to reduce unnecessary AutoLayout changes!
        if self.frame.width > self.frame.height {
            if isInPortrait {
                isInPortrait = false
                return true
            }
        } else {
            if !isInPortrait {
                isInPortrait = true
                return true
            }
        }
        return false
    }
    public func setOrientation(_ p:[NSLayoutConstraint], _ l:[NSLayoutConstraint]) {
        NSLayoutConstraint.deactivate(l)
        NSLayoutConstraint.deactivate(p)
        if self.bounds.width > self.bounds.height {
            NSLayoutConstraint.activate(l)
        } else {
            NSLayoutConstraint.activate(p)
        }
    }
}

【讨论】:

    【解决方案5】:

    试试这个

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) {
    
            print("Landscape mode called")
    
            if UI_USER_INTERFACE_IDIOM() == .Pad {
                print("Landscape mode in ipad");
    
            }
            else
            {
                print("Landscape mode in Iphone ")
    
            }
        } else {
            print("Portrait mode called")
            if UI_USER_INTERFACE_IDIOM() == .Pad {
    
                print("Portrait mode called in ipad")
            }
            else
            {
                print("Portrait mode called in iphone")
            }
        }
    }
    override func willRotateToInterfaceOrientation(toInterfaceOrientation:UIInterfaceOrientation, duration: NSTimeInterval) {
        print("willRotateToInterfaceOrientation method called");
        //Here you can invalidateLayout()
    }
    

    【讨论】:

    • 这不正确...改用 viewWillTransition。
    • @Anish웃 它对我有用..你可以试试这个代码
    • 可以,但是viewWillTransition只有在设备旋转时才会调用,而viewWillLayoutSubviews可能会调用多次
    • @Anish웃 你是对的,但你也可以使布局无效并感谢纠正我。
    【解决方案6】:
    override func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
    
        self.sliding.setNeedsDisplay()
    
    }
    

    【讨论】:

      【解决方案7】:

      如果您使用它来获取 traitCollection 的更新值,请改用它

      override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
          print("Trait collection changes")
      
      }
      

      【讨论】:

        猜你喜欢
        • 2017-09-17
        • 1970-01-01
        • 1970-01-01
        • 2014-04-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多