#ios #swift #xcode #animation #autolayout
#iOS #swift #xcode #Анимация #автозапуск
Вопрос:
Я пытаюсь заставить набор представлений изображений анимироваться внутри и снаружи точки, например, как работает расширяющееся меню, страница создается с уже отображаемыми кнопками, и во время viewDidLayoutSubviews () я анимирую их. При нажатии кнопки я затем возвращаю их в исходное положение, все анимации основаны на изменении центра изображения. Проблема, с которой я сталкиваюсь, заключается в том, что по какой-то причине анимация немного смещена вправо. на больших устройствах это не так уж плохо, но на устройствах меньшего размера это большая проблема. весь пользовательский интерфейс построен с использованием автозапуска:
Вот код для настройки расположения центров, он запускается перед выполнением любой анимации:
micContainerLocation = micContainer.center
cameraContainerLocation = cameraContainer.center
pencilContainerLocation = pencilContainer.center
calendarContainerLocation = calendarContainer.center
Вот код анимации для анимации кнопок:
UIView.animate(withDuration: 0.3, animations: {
self.calendarContainer.center = self.calendarContainerLocation
self.cameraContainer.center = self.cameraContainerLocation
self.micContainer.center = self.micContainerLocation
self.pencilContainer.center = self.pencilContainerLocation
self.sendMessageButton.alpha = 0
self.openMenuButton.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 4))
})
Вот результат:
Любой совет был бы высоко оценен. Спасибо.
Редактировать:
Не уверен, поможет ли это, но вот изображение констант в автоматической компоновке.
и подробный список ограничений:
Комментарии:
1. Используете ли вы автоматическую компоновку и ограничения? Если вы их вообще не перемещаете (ни в сторону, ни назад), находятся ли кнопки в нужных местах?
2. Да, я использую автоматическую компоновку и ограничения. Если я закомментирую код, который перемещает их в viewDidLayoutSubviews (), тогда они находятся в правильном положении «открыто». Когда я затем нажимаю кнопку, чтобы фактически открыть их, они смещаются в положение смещения, которое можно увидеть на скриншоте.
3. Возможно, вы столкнулись с конфликтами между ограничениями и пытаетесь явно задать
.center
свойства. Покажите, как у вас ограничены кнопки для начала.4. Я исправил свой вопрос, включив в него то, как кнопки ограничены внутри их контейнера, я также задал кнопкам фон, чтобы лучше видеть, как работают некоторые ограничения
Ответ №1:
Я подозреваю, что проблема (или, по крайней мере, часть проблемы) заключается в смешивании явных .center
значений с ограничениями автоматической компоновки. Также возможно, что вы не получаете «истинные» значения центра.
Другой подход, использующий только автоматическую компоновку, заключается в создании массива ограничений для «скрытых» позиций и другого массива для «видимых» позиций. Затем отключите / активируйте, чтобы показать / скрыть кнопки.
И… это проще сделать, чем сказано 🙂
- Сначала расположите свои кнопки и контейнеры в раскадровке, как вы уже сделали.
- Затем выберите каждое из ограничений, влияющих на начальный и конечный края контейнеров.
- Установите эти ограничения в качестве заполнителей, чтобы они были удалены во время сборки (мы установим соответствующие ограничения в коде, поэтому нам не понадобится целая куча
@IBOutlet
ов). - Затем, как вы могли догадаться, установите ограничения в своем коде.
Дополнительным преимуществом является то, что кнопки автоматически изменят свой размер / положение, даже если ширина супервизора изменится — например, при повороте устройства — без необходимости в каком-либо коде для переоценки .center
свойств.
Вот изображение настройки заполнителя ограничения раскадровки:
Основная идея будет заключаться в:
Создайте набор ограничений, чтобы контейнеры кнопок были вне поля зрения слева от левого края. Это будут «скрытые» ограничения.
Создайте набор ограничений, чтобы контейнеры кнопок были на виду, и каждый из них был выровнен / подобран по размеру соответствующим образом. Это будут «видимые» ограничения.
Все это может показаться сложным, но если вы посмотрите на следующий пример, я думаю, вы увидите, что на самом деле происходит очень мало.
Этот пример должен выполняться как есть… просто создайте новую раскадровку и замените исходный код, а затем используйте следующий класс. Это не совсем точно то, что у вас есть, но оно должно быть достаточно близко, чтобы вы могли использовать его в качестве отправной точки.
Вот код:
//
// AnimButtonsViewController.swift
//
// Created by Don Mag on 3/15/19.
//
import UIKit
class AnimButtonsViewController: UIViewController {
@IBOutlet var pencilContainer: UIView!
@IBOutlet var cameraContainer: UIView!
@IBOutlet var micContainer: UIView!
@IBOutlet var calendarContainer: UIView!
@IBOutlet var buttonsContainer: UIView!
var hiddenConstraints = [NSLayoutConstraint]()
var visibleConstraints = [NSLayoutConstraint]()
override func viewDidLoad() {
super.viewDidLoad()
var c = NSLayoutConstraint()
// set up the "hidden" constraints
// constrain pencilContainer (1) *trailing* to buttonsContainer *leading*
// this will place it outside the view (to the left of left edge)
c = pencilContainer.trailingAnchor.constraint(equalTo: buttonsContainer.leadingAnchor)
hiddenConstraints.append(c)
// constrain leading of remaining buttons to leading of pencilContainer
// they are now all "overlaid" on top of each other, outside the view
c = cameraContainer.leadingAnchor.constraint(equalTo: pencilContainer.leadingAnchor)
hiddenConstraints.append(c)
c = micContainer.leadingAnchor.constraint(equalTo: pencilContainer.leadingAnchor)
hiddenConstraints.append(c)
c = calendarContainer.leadingAnchor.constraint(equalTo: pencilContainer.leadingAnchor)
hiddenConstraints.append(c)
// set up the "visible" constraints
// constrain first button (pencilContainer (1)) *leading* to buttonsContainer *leading*
// and each remaining button constrain its leading to the previous button's trailing
c = pencilContainer.leadingAnchor.constraint(equalTo: buttonsContainer.leadingAnchor)
visibleConstraints.append(c)
c = cameraContainer.leadingAnchor.constraint(equalTo: pencilContainer.trailingAnchor)
visibleConstraints.append(c)
c = micContainer.leadingAnchor.constraint(equalTo: cameraContainer.trailingAnchor)
visibleConstraints.append(c)
c = calendarContainer.leadingAnchor.constraint(equalTo: micContainer.trailingAnchor)
visibleConstraints.append(c)
// contrain last button (calendarContainer (4)) trailing to buttonsContrainer trailing
c = calendarContainer.trailingAnchor.constraint(equalTo: buttonsContainer.trailingAnchor)
visibleConstraints.append(c)
// activate the hidden constraints
NSLayoutConstraint.activate(hiddenConstraints)
}
@IBAction func showHideTapped(_ sender: Any) {
if let b = sender as? UIButton {
// just for demo purposes,
// if the button title is " " we need to "show" the buttons
// if the button title is "X" we need to "hide" the buttons
let t = b.currentTitle
if t == " " {
NSLayoutConstraint.deactivate(hiddenConstraints)
NSLayoutConstraint.activate(visibleConstraints)
b.setTitle("X", for: .normal)
} else {
NSLayoutConstraint.deactivate(visibleConstraints)
NSLayoutConstraint.activate(hiddenConstraints)
b.setTitle(" ", for: .normal)
}
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
})
}
}
}
и вот источник раскадровки:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Msu-cJ-7P1">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Anim Buttons View Controller-->
<scene sceneID="LEr-rD-3kQ">
<objects>
<viewController id="Msu-cJ-7P1" customClass="AnimButtonsViewController" customModule="XC10SWScratch" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Efg-NM-r2G">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tXi-AX-zDz" userLabel="Red View">
<rect key="frame" x="30" y="278.5" width="315" height="110"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BDK-vE-tUk">
<rect key="frame" x="4" y="30" width="273" height="50"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="EbR-gU-YBO">
<rect key="frame" x="0.0" y="0.0" width="68.5" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="unV-LI-1jJ">
<rect key="frame" x="14" y="5" width="40" height="40"/>
<color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="F0Z-oi-HlI"/>
<constraint firstAttribute="width" constant="40" id="P3x-Ak-lZY"/>
</constraints>
<state key="normal" title="1"/>
</button>
</subviews>
<color key="backgroundColor" red="0.0" green="0.99143940210000003" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="unV-LI-1jJ" firstAttribute="centerX" secondItem="EbR-gU-YBO" secondAttribute="centerX" id="ZEb-9h-WvV"/>
<constraint firstItem="unV-LI-1jJ" firstAttribute="centerY" secondItem="EbR-gU-YBO" secondAttribute="centerY" id="mB2-5w-Egs"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" secondItem="unV-LI-1jJ" secondAttribute="width" id="mYy-gx-PT4"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QjG-7X-1Sa">
<rect key="frame" x="68.5" y="0.0" width="68" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qYS-Qj-f9n">
<rect key="frame" x="14" y="5" width="40" height="40"/>
<color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="To5-l0-frk"/>
<constraint firstAttribute="width" constant="40" id="m9L-d5-Mac"/>
</constraints>
<state key="normal" title="2"/>
</button>
</subviews>
<color key="backgroundColor" red="0.0" green="0.99143940210000003" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="qYS-Qj-f9n" firstAttribute="centerX" secondItem="QjG-7X-1Sa" secondAttribute="centerX" id="nNp-T0-rxc"/>
<constraint firstItem="qYS-Qj-f9n" firstAttribute="centerY" secondItem="QjG-7X-1Sa" secondAttribute="centerY" id="pDK-Y0-lag"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5IF-rt-c8a">
<rect key="frame" x="136.5" y="0.0" width="68.5" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="E15-8G-pOx">
<rect key="frame" x="14" y="5" width="40" height="40"/>
<color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="ADz-st-xYC"/>
<constraint firstAttribute="width" constant="40" id="rIE-3o-Yih"/>
</constraints>
<state key="normal" title="3"/>
</button>
</subviews>
<color key="backgroundColor" red="0.0" green="0.99143940210000003" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="E15-8G-pOx" firstAttribute="centerY" secondItem="5IF-rt-c8a" secondAttribute="centerY" id="NW3-qd-aRL"/>
<constraint firstItem="E15-8G-pOx" firstAttribute="centerX" secondItem="5IF-rt-c8a" secondAttribute="centerX" id="cyb-wZ-bhq"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kHB-hc-CY5">
<rect key="frame" x="205" y="0.0" width="68" height="50"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QrB-eG-9oo">
<rect key="frame" x="14" y="5" width="40" height="40"/>
<color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="4Qh-a1-bzP"/>
<constraint firstAttribute="height" constant="40" id="maJ-ei-IEH"/>
</constraints>
<state key="normal" title="4"/>
</button>
</subviews>
<color key="backgroundColor" red="0.0" green="0.99143940210000003" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="QrB-eG-9oo" firstAttribute="centerX" secondItem="kHB-hc-CY5" secondAttribute="centerX" id="Enu-M7-oc0"/>
<constraint firstItem="QrB-eG-9oo" firstAttribute="centerY" secondItem="kHB-hc-CY5" secondAttribute="centerY" id="iwr-AG-t6j"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.45009386540000001" green="0.98132258650000004" blue="0.4743030667" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="kHB-hc-CY5" firstAttribute="centerY" secondItem="EbR-gU-YBO" secondAttribute="centerY" id="17U-hP-XiO"/>
<constraint firstItem="EbR-gU-YBO" firstAttribute="height" secondItem="BDK-vE-tUk" secondAttribute="height" id="2Kc-yZ-8EB"/>
<constraint firstItem="QjG-7X-1Sa" firstAttribute="width" secondItem="EbR-gU-YBO" secondAttribute="width" id="3bm-Ya-pZA"/>
<constraint firstItem="EbR-gU-YBO" firstAttribute="leading" secondItem="BDK-vE-tUk" secondAttribute="leading" placeholder="YES" id="6E3-jg-Ku3"/>
<constraint firstItem="EbR-gU-YBO" firstAttribute="centerY" secondItem="BDK-vE-tUk" secondAttribute="centerY" id="8hy-lM-jcp"/>
<constraint firstAttribute="trailing" secondItem="kHB-hc-CY5" secondAttribute="trailing" placeholder="YES" id="HQQ-As-qWL"/>
<constraint firstItem="QjG-7X-1Sa" firstAttribute="height" secondItem="EbR-gU-YBO" secondAttribute="height" id="HRx-Ud-bkP"/>
<constraint firstItem="5IF-rt-c8a" firstAttribute="height" secondItem="EbR-gU-YBO" secondAttribute="height" id="L5Q-Ee-ZSx"/>
<constraint firstItem="QjG-7X-1Sa" firstAttribute="leading" secondItem="EbR-gU-YBO" secondAttribute="trailing" placeholder="YES" id="NMH-3d-uhZ"/>
<constraint firstItem="5IF-rt-c8a" firstAttribute="centerY" secondItem="EbR-gU-YBO" secondAttribute="centerY" id="Nma-OU-J25"/>
<constraint firstItem="kHB-hc-CY5" firstAttribute="height" secondItem="EbR-gU-YBO" secondAttribute="height" id="WXs-c8-mbJ"/>
<constraint firstItem="kHB-hc-CY5" firstAttribute="width" secondItem="EbR-gU-YBO" secondAttribute="width" id="Y87-Ph-wOu"/>
<constraint firstAttribute="height" constant="50" id="YRG-m1-zQ0"/>
<constraint firstItem="5IF-rt-c8a" firstAttribute="leading" secondItem="QjG-7X-1Sa" secondAttribute="trailing" placeholder="YES" id="cTU-cd-6gQ"/>
<constraint firstItem="QjG-7X-1Sa" firstAttribute="centerY" secondItem="EbR-gU-YBO" secondAttribute="centerY" id="fd7-iE-WFg"/>
<constraint firstItem="5IF-rt-c8a" firstAttribute="width" secondItem="EbR-gU-YBO" secondAttribute="width" id="jAO-LF-jvX"/>
<constraint firstItem="kHB-hc-CY5" firstAttribute="leading" secondItem="5IF-rt-c8a" secondAttribute="trailing" placeholder="YES" id="kNi-xf-omT"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="smc-cM-nTo">
<rect key="frame" x="281" y="40" width="30" height="30"/>
<color key="backgroundColor" red="0.75406885150000003" green="0.75408679249999999" blue="0.75407713649999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" title=" ">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="showHideTapped:" destination="Msu-cJ-7P1" eventType="touchUpInside" id="spY-Hq-Xeu"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="0.14913141730000001" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="smc-cM-nTo" secondAttribute="trailing" constant="4" id="3Ry-gN-0Z4"/>
<constraint firstItem="smc-cM-nTo" firstAttribute="leading" secondItem="BDK-vE-tUk" secondAttribute="trailing" constant="4" id="I2C-fT-Op3"/>
<constraint firstItem="smc-cM-nTo" firstAttribute="centerY" secondItem="tXi-AX-zDz" secondAttribute="centerY" id="Mmb-uk-L5E"/>
<constraint firstItem="BDK-vE-tUk" firstAttribute="leading" secondItem="tXi-AX-zDz" secondAttribute="leading" constant="4" id="NXB-2K-Egb"/>
<constraint firstAttribute="height" constant="110" id="inu-kU-GH9"/>
<constraint firstItem="BDK-vE-tUk" firstAttribute="centerY" secondItem="tXi-AX-zDz" secondAttribute="centerY" id="kDF-XJ-qkj"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="tXi-AX-zDz" firstAttribute="leading" secondItem="12L-io-1Iw" secondAttribute="leading" constant="30" id="30d-ds-ZVg"/>
<constraint firstItem="12L-io-1Iw" firstAttribute="trailing" secondItem="tXi-AX-zDz" secondAttribute="trailing" constant="30" id="ofy-ZY-LjW"/>
<constraint firstItem="tXi-AX-zDz" firstAttribute="centerY" secondItem="Efg-NM-r2G" secondAttribute="centerY" id="rtS-1o-e1I"/>
</constraints>
<viewLayoutGuide key="safeArea" id="12L-io-1Iw"/>
</view>
<connections>
<outlet property="buttonsContainer" destination="BDK-vE-tUk" id="qkA-Rn-M8Z"/>
<outlet property="calendarContainer" destination="kHB-hc-CY5" id="red-ot-mmb"/>
<outlet property="cameraContainer" destination="QjG-7X-1Sa" id="P94-ES-YRb"/>
<outlet property="micContainer" destination="5IF-rt-c8a" id="ipp-Bt-B2m"/>
<outlet property="pencilContainer" destination="EbR-gU-YBO" id="Fr9-7f-BIa"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Y2G-rV-gCJ" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-478" y="2845"/>
</scene>
</scenes>
</document>
Результат (нажатие серой кнопки приведет к анимации ввода и вывода кнопок):
Комментарии:
1. Это было именно то, что я искал. Спасибо.