Ios 当其中一个子视图使用比率约束时,如何估计首选UIView大小?
我意识到我不懂自动布局 我想在给定恒定宽度的情况下测量视图所需的高度 这是我的TestViewTwo.xib TestView2.swiftIos 当其中一个子视图使用比率约束时,如何估计首选UIView大小?,ios,swift,uiview,autolayout,measure,Ios,Swift,Uiview,Autolayout,Measure,我意识到我不懂自动布局 我想在给定恒定宽度的情况下测量视图所需的高度 这是我的TestViewTwo.xib TestView2.swift import UIKit class TestViewTwo: UIView { @IBOutlet weak var imageView: UIImageView! override init(frame: CGRect) { super.init(frame: frame) commonIn
import UIKit
class TestViewTwo: UIView {
@IBOutlet weak var imageView: UIImageView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
let nib = Bundle.main.loadNibNamed("TestViewTwo", owner: self, options: nil)
let view = nib!.first as! UIView
addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
view.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
view.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
view.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0).isActive = true
view.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
}
}
测试控制器
import Foundation
import UIKit
class TestControllerTwo : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let testView = TestViewTwo()
let estimatedSize = testView.systemLayoutSizeFitting(CGSize(width: 200, height: 500))
print("Estimated size: \(estimatedSize), imageView.frame: \(testView.imageView.frame)")
}
}
输出是
Estimated size: (100.0, 500.0), imageView.frame: (0.0, 0.0, 414.0, 621.0)
我不明白为什么估计宽度是100?这是从哪里来的?
为什么估计高度是500而不是300(200x1.5)?
我也不明白为什么要设置imageView框架,为什么要设置这样的值
请帮助我理解我做错了什么
我想获得estimatedSize=200x300
更新:
我想我在这里犯了一些根本性的错误。
我使用的不是比率
当我设置图像视图的恒定宽度和高度时
我明白了
当我仅设置恒定高度时
我明白了
我的布局/代码有什么问题,以至于我没有得到estimatedSize=200x300?
在移动到比率问题之前,让我们先处理常量维度。使用纵横比时,
systemLayoutSizeFitting
的一个常见问题是,它不能正常工作。它使用intrinsicContentSize
设置其高度,因此得到的值不同。为了解决此问题,需要删除纵横比并显式设置高度。在代码中,它看起来像:
imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 0.5).isActive = true
也[见][1]类似的答案
更新:
在您提供了更多信息后,我看到了一些问题:
TestViewTwo
高度,而不是实际的UIImageView
TestViewTwo
的高度为500,因为它没有底部固定点,将拉伸以填充可用空间systemLayoutSizeFitting(\u:withHorizontalFittingPriority:verticalFittingPriority:)
UIImageView
的视图命名为ImageView
:
class ImageView: UIView {
let imageView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
imageView.image = UIImage(named: "image1")
addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
imageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
imageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0).isActive = true
imageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 300).isActive = true
backgroundColor = .blue
}
}
TestViewTwo
I命名为ImageViewHolder
:
class ImageViewHolder: UIView {
let view = ImageView(frame: .zero)
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
view.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
view.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
view.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0).isActive = true
// Bottom anchor should not be set here, as the view has explicitly defined it's height
backgroundColor = .red
}
}
这是ViewController,您可以在其中进行测试:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// I gave a frame to this so you can see the actual layout
// But the estimation logic also works if you pass here .zero a
let imageViewHollder = ImageViewHolder(frame: CGRect(x: 0, y: 100, width: 200, height: 500))
let estimatedSize = imageViewHollder.view.systemLayoutSizeFitting(CGSize(width: 200, height: 500), withHorizontalFittingPriority: .defaultHigh, verticalFittingPriority: .defaultHigh)
print("Estimated size: \(estimatedSize)")
// Just to see the actual layout
view.addSubview(imageViewHollder)
}
}
请注意,打印imageView的估计大小时:
let estimatedSize = imageViewHollder.view.systemLayoutSizeFitting(CGSize(width: 200, height: 500), withHorizontalFittingPriority: .defaultHigh, verticalFittingPriority: .defaultHigh)
print("Estimated size: \(estimatedSize)")
你会得到:
但是如果打印保存它的视图的估计大小
let estimatedSize = imageViewHollder.systemLayoutSizeFitting(CGSize(width: 200, height: 500), withHorizontalFittingPriority: .defaultHigh, verticalFittingPriority: .defaultHigh)
print("Estimated size: \(estimatedSize)")
你得到
估计尺寸:(200.0500.0)
这是因为如上所述,holder视图将尝试填充可用空间,因为它没有底部锚。使用纵横比时,systemLayoutSizeFitting的一个常见问题是它不能正常工作。它使用
intrinsicContentSize
设置其高度,因此得到的值不同。为了解决此问题,需要删除纵横比并显式设置高度。在代码中,它看起来像:
imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 0.5).isActive = true
也[见][1]类似的答案
更新:
在您提供了更多信息后,我看到了一些问题:
TestViewTwo
高度,而不是实际的UIImageView
TestViewTwo
的高度为500,因为它没有底部固定点,将拉伸以填充可用空间systemLayoutSizeFitting(\u:withHorizontalFittingPriority:verticalFittingPriority:)
UIImageView
的视图命名为ImageView
:
class ImageView: UIView {
let imageView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
imageView.image = UIImage(named: "image1")
addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
imageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
imageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0).isActive = true
imageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 300).isActive = true
backgroundColor = .blue
}
}
TestViewTwo
I命名为ImageViewHolder
:
class ImageViewHolder: UIView {
let view = ImageView(frame: .zero)
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
view.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
view.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
view.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0).isActive = true
// Bottom anchor should not be set here, as the view has explicitly defined it's height
backgroundColor = .red
}
}
这是ViewController,您可以在其中进行测试:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// I gave a frame to this so you can see the actual layout
// But the estimation logic also works if you pass here .zero a
let imageViewHollder = ImageViewHolder(frame: CGRect(x: 0, y: 100, width: 200, height: 500))
let estimatedSize = imageViewHollder.view.systemLayoutSizeFitting(CGSize(width: 200, height: 500), withHorizontalFittingPriority: .defaultHigh, verticalFittingPriority: .defaultHigh)
print("Estimated size: \(estimatedSize)")
// Just to see the actual layout
view.addSubview(imageViewHollder)
}
}
请注意,打印imageView的估计大小时:
let estimatedSize = imageViewHollder.view.systemLayoutSizeFitting(CGSize(width: 200, height: 500), withHorizontalFittingPriority: .defaultHigh, verticalFittingPriority: .defaultHigh)
print("Estimated size: \(estimatedSize)")
你会得到:
但是如果打印保存它的视图的估计大小
let estimatedSize = imageViewHollder.systemLayoutSizeFitting(CGSize(width: 200, height: 500), withHorizontalFittingPriority: .defaultHigh, verticalFittingPriority: .defaultHigh)
print("Estimated size: \(estimatedSize)")
你得到
估计尺寸:(200.0500.0)
这是因为如上所述,holder视图将尝试填充可用空间,因为它没有底部锚定。您肯定需要将图像视图的底部约束到其superview的底部 给予底部约束
优先级:高(750)
然后,当您想知道基于给定矩形的估计高度时:
let estimatedSize = testView.systemLayoutSizeFitting(CGSize(width: 200, height: 500),
withHorizontalFittingPriority: .defaultHigh,
verticalFittingPriority: .defaultLow)
print("Estimated size: \(estimatedSize), imageView.frame: \(testView.imageView.frame)")
// output: Estimated size: (200.0, 300.0), imageView.frame: (0.0, 0.0, 197.0, 295.5)
let estimatedSize = testView.systemLayoutSizeFitting(CGSize(width: 200, height: 500),
withHorizontalFittingPriority: .defaultLow,
verticalFittingPriority: .defaultHigh)
print("Estimated size: \(estimatedSize), imageView.frame: \(testView.imageView.frame)")
// output: Estimated size: (333.5, 500.0), imageView.frame: (0.0, 0.0, 197.0, 295.5)
如果您想知道基于给定矩形的估计宽度:
let estimatedSize = testView.systemLayoutSizeFitting(CGSize(width: 200, height: 500),
withHorizontalFittingPriority: .defaultHigh,
verticalFittingPriority: .defaultLow)
print("Estimated size: \(estimatedSize), imageView.frame: \(testView.imageView.frame)")
// output: Estimated size: (200.0, 300.0), imageView.frame: (0.0, 0.0, 197.0, 295.5)
let estimatedSize = testView.systemLayoutSizeFitting(CGSize(width: 200, height: 500),
withHorizontalFittingPriority: .defaultLow,
verticalFittingPriority: .defaultHigh)
print("Estimated size: \(estimatedSize), imageView.frame: \(testView.imageView.frame)")
// output: Estimated size: (333.5, 500.0), imageView.frame: (0.0, 0.0, 197.0, 295.5)
请注意,imageView.frame
尚未设置,因此它将计算为IB中的任何大小
还请注意,我们为图像视图提供了一个低于所需优先级的底部约束。这避免了视图框大小未精确调整为1:1.5
比率时出现IB警告,并避免了运行时出现自动布局警告/错误消息
以下是XIB的来源:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_0" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="TestViewTwo" customModule="DrawingTutorial" customModuleProvider="target">
<connections>
<outlet property="imageView" destination="QgA-Qr-3jM" id="MGu-3W-9i4"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="197" height="391"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="QgA-Qr-3jM">
<rect key="frame" x="0.0" y="0.0" width="197" height="295.5"/>
<color key="backgroundColor" red="0.99998801950000005" green="0.62141335009999998" blue="0.00022043679199999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" secondItem="QgA-Qr-3jM" secondAttribute="height" multiplier="1:1.5" id="CpW-r1-rJA"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" red="0.45009386540000001" green="0.98132258650000004" blue="0.4743030667" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="QgA-Qr-3jM" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="3As-tz-AZL"/>
<constraint firstAttribute="trailing" secondItem="QgA-Qr-3jM" secondAttribute="trailing" id="4Q2-dC-O75"/>
<constraint firstItem="QgA-Qr-3jM" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="xJ2-05-m7l"/>
<constraint firstAttribute="bottom" secondItem="QgA-Qr-3jM" secondAttribute="bottom" priority="750" id="xy9-yL-2gg"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="109.6875" y="126.23239436619718"/>
</view>
</objects>
</document>
您肯定需要将图像视图的底部约束到其superview的底部 给予底部约束
优先级:高(750)
然后,当您想知道基于给定矩形的估计高度时:
let estimatedSize = testView.systemLayoutSizeFitting(CGSize(width: 200, height: 500),
withHorizontalFittingPriority: .defaultHigh,
verticalFittingPriority: .defaultLow)
print("Estimated size: \(estimatedSize), imageView.frame: \(testView.imageView.frame)")
// output: Estimated size: (200.0, 300.0), imageView.frame: (0.0, 0.0, 197.0, 295.5)
let estimatedSize = testView.systemLayoutSizeFitting(CGSize(width: 200, height: 500),
withHorizontalFittingPriority: .defaultLow,
verticalFittingPriority: .defaultHigh)
print("Estimated size: \(estimatedSize), imageView.frame: \(testView.imageView.frame)")
// output: Estimated size: (333.5, 500.0), imageView.frame: (0.0, 0.0, 197.0, 295.5)
如果您想知道基于给定矩形的估计宽度:
let estimatedSize = testView.systemLayoutSizeFitting(CGSize(width: 200, height: 500),
withHorizontalFittingPriority: .defaultHigh,
verticalFittingPriority: .defaultLow)
print("Estimated size: \(estimatedSize), imageView.frame: \(testView.imageView.frame)")
// output: Estimated size: (200.0, 300.0), imageView.frame: (0.0, 0.0, 197.0, 295.5)
let estimatedSize = testView.systemLayoutSizeFitting(CGSize(width: 200, height: 500),
withHorizontalFittingPriority: .defaultLow,
verticalFittingPriority: .defaultHigh)
print("Estimated size: \(estimatedSize), imageView.frame: \(testView.imageView.frame)")
// output: Estimated size: (333.5, 500.0), imageView.frame: (0.0, 0.0, 197.0, 295.5)
请注意,imageView.frame
将否