Я понял, что не понимаю AutoLayout.
Я хочу измерить требуемую высоту представления с учетом постоянной ширины.
Это мой TestViewTwo.xib
TestViewTwo.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 и почему такие значения
Пожалуйста, помогите мне понять, что я делаю не так.
Я хочу получить приблизительный размер = 200x300
Я полагаю, что я здесь делаю что-то в корне не так. Я использую не соотношение.
Когда я устанавливаю постоянную ширину и высоту просмотра изображения
я получил
Estimated size: (200.0, 500.0), imageView.frame: (0.0, 0.0, 200.0, 300.0)
Когда я устанавливаю только постоянную высоту
я получил
Estimated size: (100.0, 500.0), imageView.frame: (0.0, 0.0, 414.0, 300.0)
Что не так в моем макете / коде, так что я не могу получить EstimatedSize = 200x300?
Прежде чем перейти к проблеме соотношения, давайте разберемся с постоянными размерами.





Распространенной проблемой systemLayoutSizeFitting является некорректная работа при использовании соотношения сторон. Он использует intrinsicContentSize для установки своей высоты, поэтому вы получите другое значение. Чтобы решить эту проблему, вам необходимо удалить соотношение сторон и явно указать высоту. В коде это будет выглядеть так:
imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 0.5).isActive = true
Также [см.] [1] аналогичный ответ.
После того, как вы предоставили немного больше информации, я обнаружил несколько проблем:
TestViewTwo, а не фактическую высоту UIImageView. TestViewTwo будет иметь высоту 500, поскольку у него нет нижнего якоря, и он будет растягиваться, чтобы заполнить доступное пространство.systemLayoutSizeFitting(_:withHorizontalFittingPriority:verticalFittingPriority:).На основании документации:
Use this method when you want to prioritize the view's constraints when determining the best possible size of the view. This method does not actually change the size of the view.
Применяя эти изменения, я подготовил для вас демоверсию, чтобы вы могли проверить ее и поэкспериментировать. Я назвал View, в котором находится 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 я назвал 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)")
Ты получаешь:
// Estimated size: (200.0, 300.0)
Но если вы напечатаете приблизительный размер представления, в котором оно находится
let estimatedSize = imageViewHollder.systemLayoutSizeFitting(CGSize(width: 200, height: 500), withHorizontalFittingPriority: .defaultHigh, verticalFittingPriority: .defaultHigh)
print("Estimated size: \(estimatedSize)")
Ты получаешь
Estimated size: (200.0, 500.0)
Это связано с тем, что, как объяснялось выше, представление держателя будет пытаться заполнить доступное пространство, поскольку оно не имеет нижней привязки.
@ievgen посмотреть обновление
Вам определенно нужно ограничить нижнюю часть изображения до нижней части его супервизора ...
Дайте нижнее ограничение Priority: High (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)
Обратите внимание, что для imageView.frame еще будет установлен НЕТ, поэтому он будет оценивать тот размер, который у вас есть в IB.
Также обратите внимание, что мы даем представлению изображения ограничение Bottom с приоритетом менее чем требуется. Это позволяет избежать предупреждений IB, когда размер кадра просмотра не соответствует точному соотношению 1:1.5, и позволяет избежать сообщений с предупреждениями / ошибками автоматической компоновки во время выполнения.
Вот источник 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>
И примеры классов для демонстрации:
class TestControllerTwo : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let testView = TestViewTwo()
// withHorizontalFittingPriority: .defaultHigh
// verticalFittingPriority: .defaultLow
// gives priority to the WIDTH
// returns a size based on fitting the Target WIDTH
let estimatedSizeW = testView.systemLayoutSizeFitting(
CGSize(width: 200, height: 500),
withHorizontalFittingPriority: .defaultHigh,
verticalFittingPriority: .defaultLow)
print("Width Priority Estimated size: \(estimatedSizeW)",
"imageView.frame: \(testView.imageView.frame)")
// withHorizontalFittingPriority: .defaultLow
// verticalFittingPriority: .defaultHigh
// gives priority to the HEIGHT
// returns a size based on fitting the Target HEIGHT
let estimatedSizeH = testView.systemLayoutSizeFitting(
CGSize(width: 200, height: 500),
withHorizontalFittingPriority: .defaultLow,
verticalFittingPriority: .defaultHigh)
print("Height Priority Estimated size: \(estimatedSizeH)",
"imageView.frame: \(testView.imageView.frame)")
}
}
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
}
}
Не могли бы вы показать или сказать, что находится внутри свернутых ограничений в представлении изображения?
@ievgen - извините, я обычно дважды и трижды проверяю, все ли развернуто в структуре документа. См. Правки к моему ответу ... Я заменил крышку экрана и включил исходный код XIB, а также пример кода для классов (класс TestViewTwo - это именно то, что вы разместили в своем вопросе).
Я проверил ваше предложение, оно не сработало. Поэтому я решил сделать шаг назад и попытаться оценить размер представления с постоянными размерами. Это тоже не работает. Итак, я чувствую, что в моем макете / коде что-то принципиально не так, и обновил свой вопрос