更新自动布局约束以重新定位文本字段

Updating auto layout constraints to reposition Text Field

我目前正在努力完成 Udacity iOS 开发纳米学位,遇到了一个我真的很难解决的问题。我是开发和 Whosebug 的新手,所以这是我的第一个 post... 开始了;

我正在构建一个用于生成模因的应用程序。它通过允许用户选择图像并输入一些文本来做到这一点。

我有一个UIView。在那个 UIView 中,我有 2x UIToolbar、2x UITextField 和一个 UIImageView。工具栏位于视图的顶部和底部,文本字段分别位于它们下方和上方。 UIImageView space 视图的完整宽度和高度,位于工具栏和 textInputs 下方。

screenshots of Storyboard and app

请看这里

用户可以 select 相册中的图像,或用相机拍摄照片,以填充 UIImageView。然后将 UIImageView 设置为 Aspect Fit 以保持其正确的纵横比。

我在 Interface Builder 中设置了约束,以便 UITextFields 位于与顶部和底部工具栏的恒定距离处。

我遇到的问题是用户需要旋转设备来定位文本字段,以便它们位于图像的顶部和底部。我的意思是,如果用户 select 的是横向图像,而设备是纵向的,则用户需要旋转他们的设备以确保文本字段位于图像的顶部。

我想做的是在用户 select 编辑或拍摄图像并且图像已应用 Aspect Fit 后重新定位 textFields。

有人建议我可以使用 AVMakeRectWithAspectRatioInside 生成与 UIImage 具有相同尺寸的 CGRect 在应用了 Aspect Fit 之后,使用 CGRect 更新UIImageView的框架,然后更新约束。

我已经成功生成了新的 CGRect 并更新了 UIImageView 的框架。然后我更改了对 UITextFields 的约束,以便它们与 UIImageView 而不是工具栏对齐,但是 UITextFields 在框架更新时不会重新定位。

我在框架更新后调用以下命令;

view.setNeedUpdateConstraints()

如果有人知道解决此问题的最佳方法,那就太好了。

编辑:

这是我创建的项目 viewController 中的代码,用于隔离并尝试解决此问题..

import UIKit
import AVFoundation

class ViewController: UIViewController {

    @IBOutlet weak var imageViewPic: UIImageView!
    @IBOutlet weak var topTextToToolberContraint: NSLayoutConstraint!
    @IBOutlet weak var bottomTextToToolbarConstraint: NSLayoutConstraint!
    @IBOutlet weak var topText: UILabel!
    @IBOutlet weak var bottomText: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewWillAppear(animated: Bool) {
        let imageToDisplay = UIImage(named: "Captain-Picard-Facepalm")
        imageViewPic.image = imageToDisplay
    }

    func updateFrame() {        
        let resizedImageSize = imageViewPic.image!.size
        let imageViewBounds = imageViewPic.bounds
        let newRect = AVMakeRectWithAspectRatioInsideRect(resizedImageSize, imageViewBounds)
        imageViewPic.frame = newRect
    }

    override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
        updateFrame()
        view.setNeedsLayout()
    }
}

我发现这里的问题是 drawing/rendering 的浏览量。所以在实际布局发生之前绘制视图之类的东西,这意味着在视图未渲染之前框架不会改变。

您在这里可以做的是在实际绘图发生之前尽早强制布局。有一种方法可以在视图的框架更新时处理强制布局视图。

func setNeedsLayout()
func layoutIfNeeded()

以上两种方法都用于强制布局视图。

试试下面的代码,它可以负责更新布局。

func updateFrame() {        
    let resizedImageSize = imageViewPic.image!.size
    let imageViewBounds = imageViewPic.bounds
    let newRect = AVMakeRectWithAspectRatioInsideRect(resizedImageSize, imageViewBounds)
    imageViewPic.frame = newRect
    imageViewPic.layoutIfNeeded()
    self.view.layoutIfNeeded()
}

同时覆盖 ViewController 中的以下方法。 updateViewConstraints() 用于更新自定义约束的方法。

override func updateViewConstraints() {
    super.updateViewConstraints()
    self.view.layoutIfNeeded()
    self.imageViewPic.layoutIfNeeded()
}

我找到了一个非常相似的例子。这就是方法。

  1. 在故事板中,只对每个 UITextField 应用 3 个约束,保留 topTextField 的顶部约束和 bottomTextField 的底部约束。

  2. 为顶部和底部约束添加两个属性到 ViewController。

    var topConstraint : NSLayoutConstraint!
    var bottomConstraint : NSLayoutConstraint!
    
  3. 创建以下辅助方法;

    func layoutTextFields() {
    
    //Remove any existing constraints
    if topConstraint != nil {
        view.removeConstraint(topConstraint)
    }
    
    if bottomConstraint != nil {
        view.removeConstraint(bottomConstraint)
    }
    
    //Get the position of the image inside the imageView
    let size = memeImageView.image != nil ? memeImageView.image!.size : memeImageView.frame.size
    let frame = AVMakeRectWithAspectRatioInsideRect(size, memeImageView.bounds)
    
    //A margin for the new constrains; 10% of the frame's height
    let margin = frame.origin.y + frame.size.height * 0.10
    
    //Create and add the new constraints
    topConstraint = NSLayoutConstraint(
        item: topTextField,
        attribute: .Top,
        relatedBy: .Equal,
        toItem: memeImageView,
        attribute: .Top,
        multiplier: 1.0,
        constant: margin)
    view.addConstraint(topConstraint)
    
    bottomConstraint = NSLayoutConstraint(
        item: bottomTextField,
        attribute: .Bottom,
        relatedBy: .Equal,
        toItem: memeImageView,
        attribute: .Bottom,
        multiplier: 1.0,
        constant: -margin)
    view.addConstraint(bottomConstraint) 
    }
    
  4. viewDidLayoutSubviews()函数中调用辅助方法

    override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    layoutTextFields()
    }
    

这似乎在 UIImageView 没有图像和没有图像时都能完美地工作。它也适用于旋转设备。

我遇到了两个问题,一是认为我需要首先在 Storyboard 中定义所有约束,二是认为我需要使用 CGRect 生成的视图将视图添加到层​​次结构中AVMakeRectWithAspectRatioInsideRect