UIStackView 显示/隐藏动画
Posted
技术标签:
【中文标题】UIStackView 显示/隐藏动画【英文标题】:UIStackView show/hide animation 【发布时间】:2019-09-19 09:11:45 【问题描述】:在堆栈视图中,我有 UIPickerView,我想在点击按钮时折叠和展开它。我想使用一个简单的动画,但不知道如何实现它,我尝试了很多方法,但都没有导致正确的外观我总是得到这个
我想让选择器也崩溃,但它不会。它只是在动画后消失,看起来不太好。
我的代码中 self 是 UIStackView:
UIView.animate(withDuration: 0.3, animations: [unowned self] in
self.picker.isHidden = !open
self.layoutIfNeeded()
)
【问题讨论】:
在UITableView
中动态添加/删除一行可能比UIStackView
更好。
我认为 UIStackView 旨在随时随地动态更改内容。
不,这是UITableView
的目的之一。 UIStackView
用于轻松水平或垂直布置视图。
【参考方案1】:
堆栈视图的自动显示/隐藏动画效果很好 --- 对于 一些 的东西。对于其他人来说,比如使用 Picker View,就没有那么多了(如你所见)。
一种方法是:
将选取器视图嵌入到常规视图中 限制它垂直居中 为包含视图添加默认高度(例如比选择器视图略高) 为视图的高度约束设置动画不过,选择器视图不会自行“挤压”,因此您会得到一个“消失”的选择器视图。如果您希望它在动画时“挤压”,您还需要对其变换进行动画处理
这是一个示例(我使用对比色使元素更容易看到,并且我已经放慢了动画持续时间以使其更明显):
这里是示例代码:
class StackDemoViewController: UIViewController
@IBOutlet var pickerHolderView: UIView!
@IBOutlet var pickerHolderHeightConstraint: NSLayoutConstraint!
@IBOutlet var normalButton: UIButton!
@IBOutlet var squeezeButton: UIButton!
@IBOutlet var thePickerView: UIDatePicker!
// this will be assigned in viewDidLoad
var defaultPickerHolderViewHeight: CGFloat = 0.0
// anim duration - change to something like 1.0 to see the effect in "slo-motion"
let animDuration = 0.3
override func viewDidLoad()
super.viewDidLoad()
// get the original picker holder view height constant
defaultPickerHolderViewHeight = pickerHolderHeightConstraint.constant
@IBAction func normalAnim(_ sender: Any)
// local bool
let bIsHidden = pickerHolderView.isHidden
// if the picker holder view is currently hidden, show it
if bIsHidden
pickerHolderView.isHidden = false
// if picker holder height constant is > 0 (it's open / showing)
// set it to 0
// else
// set it to defaultPickerHolderViewHeight
self.pickerHolderHeightConstraint.constant = self.pickerHolderHeightConstraint.constant > 0 ? 0 : defaultPickerHolderViewHeight
// animate the change
UIView.animate(withDuration: animDuration, animations:
self.view.layoutIfNeeded()
) finished in
// if the picker holder view was showing (NOT hidden)
// hide it
if !bIsHidden
self.pickerHolderView.isHidden = true
// disable squeeze button until view is showing again
self.squeezeButton.isEnabled = false
else
// re-enable squeeze button
self.squeezeButton.isEnabled = true
@IBAction func squeezeAnim(_ sender: Any)
// local bool
let bIsHidden = pickerHolderView.isHidden
var t = CGAffineTransform.identity
// if the picker holder view is currently hidden, show it
if bIsHidden
pickerHolderView.isHidden = false
else
// we're going to hide it
t = CGAffineTransform(scaleX: 1.0, y: 0.01)
// if picker holder height constant is > 0 (it's open / showing)
// set it to 0
// else
// set it to defaultPickerHolderViewHeight
self.pickerHolderHeightConstraint.constant = self.pickerHolderHeightConstraint.constant > 0 ? 0 : defaultPickerHolderViewHeight
// animate the change
UIView.animate(withDuration: animDuration, animations:
self.thePickerView.transform = t
self.view.layoutIfNeeded()
) finished in
// if the picker holder view was showing (NOT hidden)
// hide it
if !bIsHidden
self.pickerHolderView.isHidden = true
// disable normal button until view is showing again
self.normalButton.isEnabled = false
else
// re-enable normal button
self.normalButton.isEnabled = true
使用此布局:
而且,这里是 Storyboard 的来源(所以你可以自己快速尝试):
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="ios.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Zg0-f1-bBK">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Stack Demo View Controller-->
<scene sceneID="Itw-fL-6gO">
<objects>
<viewController id="Zg0-f1-bBK" customClass="StackDemoViewController" customModule="TranslateTest" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="rze-A8-JnC">
<rect key="frame" x="0.0" y="0.0" />
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="vDP-gh-oah">
<rect key="frame" x="8" y="120" />
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="clh-vv-1e4">
<rect key="frame" x="0.0" y="0.0" />
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="VMQ-JX-yNt">
<rect key="frame" x="8" y="8" />
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Zb9-rN-qPb">
<rect key="frame" x="0.0" y="0.0" />
<color key="backgroundColor" red="0.99806135890000003" green="0.96808904409999996" blue="0.12760734560000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" title="Normal"/>
<connections>
<action selector="normalAnim:" destination="Zg0-f1-bBK" eventType="touchUpInside" id="zwU-Bs-ZlI"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="v2b-2E-upp">
<rect key="frame" x="179.5" y="0.0" />
<color key="backgroundColor" red="0.99806135890000003" green="0.96808904409999996" blue="0.12760734560000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" title="With Squeeze"/>
<connections>
<action selector="squeezeAnim:" destination="Zg0-f1-bBK" eventType="touchUpInside" id="ARc-fQ-XRE"/>
</connections>
</button>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" red="1" green="0.14913141730000001" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="VMQ-JX-yNt" secondAttribute="trailing" constant="8" id="T0v-du-5Aj"/>
<constraint firstItem="VMQ-JX-yNt" firstAttribute="top" secondItem="clh-vv-1e4" secondAttribute="top" constant="8" id="Y2j-KP-ylE"/>
<constraint firstItem="VMQ-JX-yNt" firstAttribute="leading" secondItem="clh-vv-1e4" secondAttribute="leading" constant="8" id="mKK-5Q-IhS"/>
<constraint firstAttribute="bottom" secondItem="VMQ-JX-yNt" secondAttribute="bottom" constant="8" id="uJf-Y8-Uun"/>
</constraints>
</view>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6L1-Bv-SxB">
<rect key="frame" x="0.0" y="58" />
<subviews>
<datePicker contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" datePickerMode="dateAndTime" minuteInterval="1" translatesAutoresizingMaskIntoConstraints="NO" id="0A6-0Z-m7u">
<rect key="frame" x="8" y="8" />
<color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<date key="date" timeIntervalSinceReferenceDate="590598642.83352995">
<!--2019-09-19 15:10:42 +0000-->
</date>
</datePicker>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="0A6-0Z-m7u" firstAttribute="centerY" secondItem="6L1-Bv-SxB" secondAttribute="centerY" id="Eqi-Od-JBH"/>
<constraint firstItem="0A6-0Z-m7u" firstAttribute="leading" secondItem="6L1-Bv-SxB" secondAttribute="leading" constant="8" id="IEp-7K-buG"/>
<constraint firstAttribute="height" constant="232" id="e1y-wA-jqj"/>
<constraint firstAttribute="trailing" secondItem="0A6-0Z-m7u" secondAttribute="trailing" constant="8" id="hLe-WM-Qnx"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Standard UILabel" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="X5m-RD-zx4">
<rect key="frame" x="0.0" y="298" />
<color key="backgroundColor" red="0.46202266219999999" green="0.83828371759999998" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="4c2-X0-9Kb"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
</subviews>
<color key="backgroundColor" red="0.52747867609999999" green="1" blue="0.55622484120000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="k9S-Qf-yG1" firstAttribute="trailing" secondItem="vDP-gh-oah" secondAttribute="trailing" constant="8" id="5C9-Ef-syQ"/>
<constraint firstItem="vDP-gh-oah" firstAttribute="top" secondItem="k9S-Qf-yG1" secondAttribute="top" constant="100" id="cuG-HE-aDz"/>
<constraint firstItem="vDP-gh-oah" firstAttribute="leading" secondItem="rze-A8-JnC" secondAttribute="leading" constant="8" id="f5f-qW-BJ2"/>
</constraints>
<viewLayoutGuide key="safeArea" id="k9S-Qf-yG1"/>
</view>
<connections>
<outlet property="normalButton" destination="Zb9-rN-qPb" id="0sr-a2-wa9"/>
<outlet property="pickerHolderHeightConstraint" destination="e1y-wA-jqj" id="t7m-zQ-RwA"/>
<outlet property="pickerHolderView" destination="6L1-Bv-SxB" id="hkf-zy-GIS"/>
<outlet property="squeezeButton" destination="v2b-2E-upp" id="fFe-hm-qzd"/>
<outlet property="thePickerView" destination="0A6-0Z-m7u" id="ubt-fR-mx9"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="e1N-yd-USh" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2244" y="126.38680659670166"/>
</scene>
</scenes>
</document>
【讨论】:
非常感谢您的回答!它给了我一个我在互联网上找不到的洞察力!【参考方案2】:您可以将usingSpringWithDamping
与动画代码一起使用
【讨论】:
【参考方案3】:Swift 5:上滑动画
1- 将高度设置为 216(PickerView 标准)。
2- 设置引导到安全区域的尾随。
3- 将底部设置为“SuperViewBottom”为 -216。
4- 将第 3 行的 IBOutlet 设为可选的 NSLayoutConstraint。
然后:
import UIKit
class ViewController: UIViewController
@IBOutlet weak var bottom: NSLayoutConstraint!
override func viewDidLoad()
super.viewDidLoad()
bottom.constant = -216
-(IBAction)button:(id)sender
UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations:
self.bottom.constant = 0
self.view.layoutIfNeeded()
) (AnimationComplete ) in
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations:
self.bottom.constant = -216
self.view.layoutIfNeeded()
) (AnimationComplete ) in
它应该像 Apple 标准动画一样工作。
祝你好运?
【讨论】:
以上是关于UIStackView 显示/隐藏动画的主要内容,如果未能解决你的问题,请参考以下文章