In stack view I have UIPickerView and I want to collapse and expand it on button taps. I want to use a simple animation but don't know how to achieve it, I have tried many ways but none of it lead to the correct appearance I always get to this
I want to make the picker also collapse which it does not. It only just disappears after the animation which does not look nice.
my code where self is the UIStackView:
UIView.animate(withDuration: 0.3, animations: { [unowned self] in
self.picker.isHidden = !open
self.layoutIfNeeded()
})
Stack view's automatic show/hide animation works great --- for some things. For others, such as with a Picker View, not so much (as you've seen).
One approach would be:
Picker views will not "squeeze" on their own though, so you'll get a "disappearing" picker view. If you want it to "squeeze" as it animates, you'll also need to animate its transform
Here is an example (I use contrasting colors to make it easy to see elements, and I've slowed the animation duration to make it obvious):
Here is sample code:
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
}
}
}
}
Using this layout:
and, here is the source of the Storyboard (so you can quickly try it out yourself):
<?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" width="375" height="667"/>
<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" width="359" height="338"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="clh-vv-1e4">
<rect key="frame" x="0.0" y="0.0" width="359" height="50"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="VMQ-JX-yNt">
<rect key="frame" x="8" y="8" width="343" height="34"/>
<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" width="163.5" height="34"/>
<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" width="163.5" height="34"/>
<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" width="359" height="232"/>
<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" width="343" height="216"/>
<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" width="359" height="40"/>
<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>