处理包含能够折叠和展开的子视图的视图
Handling a view that contains a subview that is able to collapse and expand
在我的视图控制器中,我有一个用于托管子视图的视图。我们称之为 ViewA
。当控制器加载时,从 nib 加载的视图被设置为 ViewA
内的子视图。根据子视图中的内容,它的高度可以有不同的大小。
因此,我创建了一个委托,当子视图的高度发生变化时会发出警报,通知其父视图更新自己的高度:
UIViewController
class MyViewController: UIViewController, MyViewDelegate {
@IBOutlet weak var myView: UIView!
@IBOutlet weak var myViewHeightConstraint: NSLayoutConstraint!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let mySubView: MySubView = UINib(nibName: "MySubView", bundle: nil).instantiate(withOwner: MySubView(), options: nil)[0] as! MySubView
mySubview.translatesAutoresizingMaskIntoConstraints = false
mySubView.delegate = self
myView.addSubView(mySubView)
let leadingConstraint = NSLayoutConstraint(item: mySubView, attribute: .leading, relatedBy: .equal, toItem: myView, attribute: .leading, multiplier: 1, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: mySubView, attribute: .trailing, relatedBy: .equal, toItem: myView, attribute: .trailing, multiplier: 1, constant: 0)
let topConstraint = NSLayoutConstraint(item: mySubView, attribute: .top, relatedBy: .equal, toItem: myView, attribute: .top, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: mySubView, attribute: .bottom, relatedBy: .equal, toItem: myView, attribute: .bottom, multiplier: 1, constant: 0)
NSLayoutConstraint.activate([leadingConstraint, trailingConstraint, topConstraint, bottomConstraint])
}
override func viewDidLayoutSubviews() {
addShadowToView()
}
func addShadowToView() {
myView.layer.masksToBounds = false
myView.layer.shadowColor = UIColor.black.cgColor
myView.layer.shadowOpacity = 0.25
myView.layer.shadowOffset = CGSize(width: 0, height: 0)
myView.layer.shadowRadius = 5.0
myView.layer.shadowPath = UIBezierPath(rect. myView.bounds).cgPath
}
MySubViewDelegate(_ mySubView: MySubView, didUpdateHeightTo height: CGFloat) {
myViewHeightConstraint.constant = height
myView.updateConstraints()
addShadowToView()
}
}
MySubView
class MySubView: UIView {
var delegate: MySubViewDelegate?
@IBOutlet weak var aView: UIView!
@IBOutlet weak var aViewHeghtConstraint: NSLayoutConstraint!\
var isViewCollapsed = false
@IBAction func toggleView() {
aViewHeightConstraint.contant = isViewCollapsed ? 100 : 0
isViewCollapsed = !isViewCollapsed
updateConstraints()
delegate.MSView(self, didUpdateHeightTo height: self.frame.height)
}
}
protocol MySubViewDelegate {
func MSView(_ MySubView: MySubView, didUpdateHeightTo height: CGFloat)
}
是否有更好的方法将展开和折叠的子视图放入父视图中,父视图将能够更新自己的框架以适应其子视图的更改?
评论和发布代码后...
看起来你做了很多不需要做的事情。
有了适当的约束,您需要的代码就会少得多,而且您根本不需要您的协议/委托。
首先 - 这只是一个提示 - 您可以以更简单、更易读的方式在代码中定义约束:
myView.addSubview(mySubView)
NSLayoutConstraint.activate([
mySubView.topAnchor.constraint(equalTo: myView.topAnchor, constant: 0.0),
mySubView.bottomAnchor.constraint(equalTo: myView.bottomAnchor, constant: 0.0),
mySubView.leadingAnchor.constraint(equalTo: myView.leadingAnchor, constant: 0.0),
mySubView.trailingAnchor.constraint(equalTo: myView.trailingAnchor, constant: 0.0),
])
其次 - 也是一个提示 - 如果您创建一个 "shadow" 视图子 class,它可以自行处理您的阴影更新:
class MyShadowedView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
// non-changing properties - set on init
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.25
layer.shadowOffset = CGSize(width: 0, height: 0)
layer.shadowRadius = 5.0
}
override func layoutSubviews() {
super.layoutSubviews()
// update the shadowPath
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
}
}
当您将 myView
作为 UIView
添加到故事板时,只需将其 class 分配给 MyShadowedView
,您无需调用添加阴影 - 它会自动完成。
第三 - 根据您发布的代码,您的 myView
似乎有高度限制,而您将 mySubView
限制在其顶部和底部。这意味着 mySubView
将是 myView
的高度,并且永远不会改变。您的 "delegate" 函数 尝试 更改它,但它总是超过其限制的高度。
所以...
在您的 ViewController 中,您想添加一个 UIView
,将它的 class 分配给 MyShadowedView
,像往常一样给它 Top、Leading 和 Trailing 约束(或 Top、Width 和 CenterX,如果您需要的话)。
对于它的高度,给它一个关于它应该开始的高度约束,但是使该约束成为一个占位符将在 运行 时删除。这允许您在设计期间看到它(并避免 IB 抱怨缺少约束),但在 运行 时,您添加的子视图将控制其高度:
你的 xib 看起来像这样(好吧,简化了 - 我相信你有更多的元素在里面):
注意:为 aView
的底部约束赋予 999
的优先级也有助于避免 IB 约束警告。
当您点击该按钮时,您的代码将在 100
和 0
之间切换 aView
的高度限制常数。这将扩展/折叠其超级视图的高度,如果 its 超级视图(myView
在视图控制器中),它将控制高度。
您的完整代码最终为:
//
// TannerViewController.swift
//
// Created by Don Mag on 12/12/18.
//
import UIKit
class MySubView: UIView {
@IBOutlet weak var aView: UIView!
@IBOutlet weak var aViewHeightConstraint: NSLayoutConstraint!
var isViewCollapsed = false
@IBAction func toggleView(_ sender: Any) {
aViewHeightConstraint.constant = isViewCollapsed ? 100 : 0
isViewCollapsed = !isViewCollapsed
}
}
class TannerViewController: UIViewController {
@IBOutlet weak var myView: MyShadowedView!
override func viewDidLoad() {
super.viewDidLoad()
let mySubView: MySubView = UINib(nibName: "MySubView", bundle: nil).instantiate(withOwner: MySubView(), options: nil)[0] as! MySubView
mySubView.translatesAutoresizingMaskIntoConstraints = false
myView.addSubview(mySubView)
NSLayoutConstraint.activate([
mySubView.topAnchor.constraint(equalTo: myView.topAnchor, constant: 0.0),
mySubView.bottomAnchor.constraint(equalTo: myView.bottomAnchor, constant: 0.0),
mySubView.leadingAnchor.constraint(equalTo: myView.leadingAnchor, constant: 0.0),
mySubView.trailingAnchor.constraint(equalTo: myView.trailingAnchor, constant: 0.0),
])
}
}
class MyShadowedView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
// non-changing properties - set on init
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.25
layer.shadowOffset = CGSize(width: 0, height: 0)
layer.shadowRadius = 5.0
}
override func layoutSubviews() {
super.layoutSubviews()
// update the shadowPath
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
}
}
导致:
为了轻松测试,这里是故事板的来源:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="jPc-3G-hfP">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Tanner View Controller-->
<scene sceneID="f8L-af-3cE">
<objects>
<viewController id="jPc-3G-hfP" customClass="TannerViewController" customModule="SW4Temp" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="0I2-oK-Mx2">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uKj-M5-owl" customClass="MyShadowedView" customModule="SW4Temp" customModuleProvider="target">
<rect key="frame" x="40" y="120" width="295" height="100"/>
<color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="100" placeholder="YES" id="qvT-aM-Weq" userLabel="Placeholder Height = 100"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.99953407049999998" green="0.98835557699999999" blue="0.47265523669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="uKj-M5-owl" firstAttribute="top" secondItem="twx-NV-wpY" secondAttribute="top" constant="100" id="141-8C-ZNl"/>
<constraint firstItem="twx-NV-wpY" firstAttribute="trailing" secondItem="uKj-M5-owl" secondAttribute="trailing" constant="40" id="5Zs-Or-GhR"/>
<constraint firstItem="uKj-M5-owl" firstAttribute="leading" secondItem="twx-NV-wpY" secondAttribute="leading" constant="40" id="R95-i1-Xb2"/>
</constraints>
<viewLayoutGuide key="safeArea" id="twx-NV-wpY"/>
</view>
<connections>
<outlet property="myView" destination="uKj-M5-owl" id="uCY-bV-QJd"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="q6f-6s-ke9" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53.600000000000001" y="101.19940029985008"/>
</scene>
</scenes>
</document>
和mySubView.xib:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="MySubView" customModule="SW4Temp" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="332" height="202"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="T7q-1U-5iM">
<rect key="frame" x="106" y="20" width="120" height="30"/>
<color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="120" id="WJn-fl-dH1"/>
</constraints>
<state key="normal" title="Button"/>
<connections>
<action selector="toggleView:" destination="iN0-l3-epB" eventType="touchUpInside" id="LSR-3h-g1f"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Vtd-O9-gRZ">
<rect key="frame" x="40" y="70" width="252" height="100"/>
<color key="backgroundColor" red="0.46202266219999999" green="0.83828371759999998" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="100" id="e3B-MV-NZK"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.83216959239999999" green="0.98548370600000001" blue="0.47333085539999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="Vtd-O9-gRZ" secondAttribute="bottom" priority="999" constant="20" id="0aE-RM-0AZ"/>
<constraint firstItem="T7q-1U-5iM" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="20" id="Acg-yV-bn2"/>
<constraint firstItem="Vtd-O9-gRZ" firstAttribute="top" secondItem="T7q-1U-5iM" secondAttribute="bottom" constant="20" id="KVh-lw-Sst"/>
<constraint firstItem="T7q-1U-5iM" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="NUj-4y-fDg"/>
<constraint firstItem="Vtd-O9-gRZ" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="40" id="cS4-7R-wW7"/>
<constraint firstAttribute="trailing" secondItem="Vtd-O9-gRZ" secondAttribute="trailing" constant="40" id="kkG-9K-cEP"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="aView" destination="Vtd-O9-gRZ" id="SNl-ng-33p"/>
<outlet property="aViewHeightConstraint" destination="e3B-MV-NZK" id="S4R-ct-gzE"/>
</connections>
<point key="canvasLocation" x="12" y="-179"/>
</view>
</objects>
</document>
您应该在更新约束常量之后调用 layoutIfNeeded
以在检索新高度之前强制更新布局:
print("Before:", self.frame.height)
heightConstraint.constant += 10
layoutIfNeeded()
print("After:", self.frame.height)
结果:
Before: 132.0
After: 142.0
Before: 142.0
After: 152.0
Before: 152.0
After: 162.0
在我的视图控制器中,我有一个用于托管子视图的视图。我们称之为 ViewA
。当控制器加载时,从 nib 加载的视图被设置为 ViewA
内的子视图。根据子视图中的内容,它的高度可以有不同的大小。
因此,我创建了一个委托,当子视图的高度发生变化时会发出警报,通知其父视图更新自己的高度:
UIViewController
class MyViewController: UIViewController, MyViewDelegate {
@IBOutlet weak var myView: UIView!
@IBOutlet weak var myViewHeightConstraint: NSLayoutConstraint!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let mySubView: MySubView = UINib(nibName: "MySubView", bundle: nil).instantiate(withOwner: MySubView(), options: nil)[0] as! MySubView
mySubview.translatesAutoresizingMaskIntoConstraints = false
mySubView.delegate = self
myView.addSubView(mySubView)
let leadingConstraint = NSLayoutConstraint(item: mySubView, attribute: .leading, relatedBy: .equal, toItem: myView, attribute: .leading, multiplier: 1, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: mySubView, attribute: .trailing, relatedBy: .equal, toItem: myView, attribute: .trailing, multiplier: 1, constant: 0)
let topConstraint = NSLayoutConstraint(item: mySubView, attribute: .top, relatedBy: .equal, toItem: myView, attribute: .top, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: mySubView, attribute: .bottom, relatedBy: .equal, toItem: myView, attribute: .bottom, multiplier: 1, constant: 0)
NSLayoutConstraint.activate([leadingConstraint, trailingConstraint, topConstraint, bottomConstraint])
}
override func viewDidLayoutSubviews() {
addShadowToView()
}
func addShadowToView() {
myView.layer.masksToBounds = false
myView.layer.shadowColor = UIColor.black.cgColor
myView.layer.shadowOpacity = 0.25
myView.layer.shadowOffset = CGSize(width: 0, height: 0)
myView.layer.shadowRadius = 5.0
myView.layer.shadowPath = UIBezierPath(rect. myView.bounds).cgPath
}
MySubViewDelegate(_ mySubView: MySubView, didUpdateHeightTo height: CGFloat) {
myViewHeightConstraint.constant = height
myView.updateConstraints()
addShadowToView()
}
}
MySubView
class MySubView: UIView {
var delegate: MySubViewDelegate?
@IBOutlet weak var aView: UIView!
@IBOutlet weak var aViewHeghtConstraint: NSLayoutConstraint!\
var isViewCollapsed = false
@IBAction func toggleView() {
aViewHeightConstraint.contant = isViewCollapsed ? 100 : 0
isViewCollapsed = !isViewCollapsed
updateConstraints()
delegate.MSView(self, didUpdateHeightTo height: self.frame.height)
}
}
protocol MySubViewDelegate {
func MSView(_ MySubView: MySubView, didUpdateHeightTo height: CGFloat)
}
是否有更好的方法将展开和折叠的子视图放入父视图中,父视图将能够更新自己的框架以适应其子视图的更改?
评论和发布代码后...
看起来你做了很多不需要做的事情。
有了适当的约束,您需要的代码就会少得多,而且您根本不需要您的协议/委托。
首先 - 这只是一个提示 - 您可以以更简单、更易读的方式在代码中定义约束:
myView.addSubview(mySubView)
NSLayoutConstraint.activate([
mySubView.topAnchor.constraint(equalTo: myView.topAnchor, constant: 0.0),
mySubView.bottomAnchor.constraint(equalTo: myView.bottomAnchor, constant: 0.0),
mySubView.leadingAnchor.constraint(equalTo: myView.leadingAnchor, constant: 0.0),
mySubView.trailingAnchor.constraint(equalTo: myView.trailingAnchor, constant: 0.0),
])
其次 - 也是一个提示 - 如果您创建一个 "shadow" 视图子 class,它可以自行处理您的阴影更新:
class MyShadowedView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
// non-changing properties - set on init
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.25
layer.shadowOffset = CGSize(width: 0, height: 0)
layer.shadowRadius = 5.0
}
override func layoutSubviews() {
super.layoutSubviews()
// update the shadowPath
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
}
}
当您将 myView
作为 UIView
添加到故事板时,只需将其 class 分配给 MyShadowedView
,您无需调用添加阴影 - 它会自动完成。
第三 - 根据您发布的代码,您的 myView
似乎有高度限制,而您将 mySubView
限制在其顶部和底部。这意味着 mySubView
将是 myView
的高度,并且永远不会改变。您的 "delegate" 函数 尝试 更改它,但它总是超过其限制的高度。
所以...
在您的 ViewController 中,您想添加一个 UIView
,将它的 class 分配给 MyShadowedView
,像往常一样给它 Top、Leading 和 Trailing 约束(或 Top、Width 和 CenterX,如果您需要的话)。
对于它的高度,给它一个关于它应该开始的高度约束,但是使该约束成为一个占位符将在 运行 时删除。这允许您在设计期间看到它(并避免 IB 抱怨缺少约束),但在 运行 时,您添加的子视图将控制其高度:
你的 xib 看起来像这样(好吧,简化了 - 我相信你有更多的元素在里面):
注意:为 aView
的底部约束赋予 999
的优先级也有助于避免 IB 约束警告。
当您点击该按钮时,您的代码将在 100
和 0
之间切换 aView
的高度限制常数。这将扩展/折叠其超级视图的高度,如果 its 超级视图(myView
在视图控制器中),它将控制高度。
您的完整代码最终为:
//
// TannerViewController.swift
//
// Created by Don Mag on 12/12/18.
//
import UIKit
class MySubView: UIView {
@IBOutlet weak var aView: UIView!
@IBOutlet weak var aViewHeightConstraint: NSLayoutConstraint!
var isViewCollapsed = false
@IBAction func toggleView(_ sender: Any) {
aViewHeightConstraint.constant = isViewCollapsed ? 100 : 0
isViewCollapsed = !isViewCollapsed
}
}
class TannerViewController: UIViewController {
@IBOutlet weak var myView: MyShadowedView!
override func viewDidLoad() {
super.viewDidLoad()
let mySubView: MySubView = UINib(nibName: "MySubView", bundle: nil).instantiate(withOwner: MySubView(), options: nil)[0] as! MySubView
mySubView.translatesAutoresizingMaskIntoConstraints = false
myView.addSubview(mySubView)
NSLayoutConstraint.activate([
mySubView.topAnchor.constraint(equalTo: myView.topAnchor, constant: 0.0),
mySubView.bottomAnchor.constraint(equalTo: myView.bottomAnchor, constant: 0.0),
mySubView.leadingAnchor.constraint(equalTo: myView.leadingAnchor, constant: 0.0),
mySubView.trailingAnchor.constraint(equalTo: myView.trailingAnchor, constant: 0.0),
])
}
}
class MyShadowedView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
// non-changing properties - set on init
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.25
layer.shadowOffset = CGSize(width: 0, height: 0)
layer.shadowRadius = 5.0
}
override func layoutSubviews() {
super.layoutSubviews()
// update the shadowPath
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
}
}
导致:
为了轻松测试,这里是故事板的来源:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="jPc-3G-hfP">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Tanner View Controller-->
<scene sceneID="f8L-af-3cE">
<objects>
<viewController id="jPc-3G-hfP" customClass="TannerViewController" customModule="SW4Temp" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="0I2-oK-Mx2">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uKj-M5-owl" customClass="MyShadowedView" customModule="SW4Temp" customModuleProvider="target">
<rect key="frame" x="40" y="120" width="295" height="100"/>
<color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="100" placeholder="YES" id="qvT-aM-Weq" userLabel="Placeholder Height = 100"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.99953407049999998" green="0.98835557699999999" blue="0.47265523669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="uKj-M5-owl" firstAttribute="top" secondItem="twx-NV-wpY" secondAttribute="top" constant="100" id="141-8C-ZNl"/>
<constraint firstItem="twx-NV-wpY" firstAttribute="trailing" secondItem="uKj-M5-owl" secondAttribute="trailing" constant="40" id="5Zs-Or-GhR"/>
<constraint firstItem="uKj-M5-owl" firstAttribute="leading" secondItem="twx-NV-wpY" secondAttribute="leading" constant="40" id="R95-i1-Xb2"/>
</constraints>
<viewLayoutGuide key="safeArea" id="twx-NV-wpY"/>
</view>
<connections>
<outlet property="myView" destination="uKj-M5-owl" id="uCY-bV-QJd"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="q6f-6s-ke9" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53.600000000000001" y="101.19940029985008"/>
</scene>
</scenes>
</document>
和mySubView.xib:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="MySubView" customModule="SW4Temp" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="332" height="202"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="T7q-1U-5iM">
<rect key="frame" x="106" y="20" width="120" height="30"/>
<color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="120" id="WJn-fl-dH1"/>
</constraints>
<state key="normal" title="Button"/>
<connections>
<action selector="toggleView:" destination="iN0-l3-epB" eventType="touchUpInside" id="LSR-3h-g1f"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Vtd-O9-gRZ">
<rect key="frame" x="40" y="70" width="252" height="100"/>
<color key="backgroundColor" red="0.46202266219999999" green="0.83828371759999998" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="100" id="e3B-MV-NZK"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.83216959239999999" green="0.98548370600000001" blue="0.47333085539999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="Vtd-O9-gRZ" secondAttribute="bottom" priority="999" constant="20" id="0aE-RM-0AZ"/>
<constraint firstItem="T7q-1U-5iM" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="20" id="Acg-yV-bn2"/>
<constraint firstItem="Vtd-O9-gRZ" firstAttribute="top" secondItem="T7q-1U-5iM" secondAttribute="bottom" constant="20" id="KVh-lw-Sst"/>
<constraint firstItem="T7q-1U-5iM" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="NUj-4y-fDg"/>
<constraint firstItem="Vtd-O9-gRZ" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="40" id="cS4-7R-wW7"/>
<constraint firstAttribute="trailing" secondItem="Vtd-O9-gRZ" secondAttribute="trailing" constant="40" id="kkG-9K-cEP"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="aView" destination="Vtd-O9-gRZ" id="SNl-ng-33p"/>
<outlet property="aViewHeightConstraint" destination="e3B-MV-NZK" id="S4R-ct-gzE"/>
</connections>
<point key="canvasLocation" x="12" y="-179"/>
</view>
</objects>
</document>
您应该在更新约束常量之后调用 layoutIfNeeded
以在检索新高度之前强制更新布局:
print("Before:", self.frame.height)
heightConstraint.constant += 10
layoutIfNeeded()
print("After:", self.frame.height)
结果:
Before: 132.0
After: 142.0
Before: 142.0
After: 152.0
Before: 152.0
After: 162.0