Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 当其中一个子视图使用比率约束时,如何估计首选UIView大小?_Ios_Swift_Uiview_Autolayout_Measure - Fatal编程技术网

Ios 当其中一个子视图使用比率约束时,如何估计首选UIView大小?

Ios 当其中一个子视图使用比率约束时,如何估计首选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

我意识到我不懂自动布局

我想在给定恒定宽度的情况下测量视图所需的高度

这是我的TestViewTwo.xib

TestView2.swift

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]类似的答案

更新: 在您提供了更多信息后,我看到了一些问题:

  • 您得到的高度是500,因为您试图估计的是
    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]类似的答案

    更新: 在您提供了更多信息后,我看到了一些问题:

  • 您得到的高度是500,因为您试图估计的是
    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