You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Storyboard实现红色视图随按钮点击的横向滚动动画

iOS Storyboard实现红色指示器横向滚动动画

需求说明

点击one/two/three三个按钮时,下方红色指示器通过平移动画与被点击按钮的横向中心线对齐,效果如下图:
示例效果图

实现步骤

一、调整Storyboard约束

首先修改原有约束避免冲突,新增可动态修改的对齐约束:

  • 找到红色指示器视图(id: qMv-J3-IRX),删除原有leading to 父容器leadingMargin约束,保留width=60、height=2、top到stackView底部间距为7.5这三个约束
  • 给红色指示器添加新约束:centerX与one按钮的centerX相等,约束优先级设为1000
  • 将上述新增的centerX约束拖线到关联的ViewController类中,命名为indicatorCenterX
  • 将三个按钮依次拖线到ViewController类中作为IBOutlet属性,同时把三个按钮的Touch Up Inside点击事件都关联到同一个响应方法

二、编写动画逻辑

在ViewController中实现约束更新和动画触发代码:

import UIKit

class ViewController: UIViewController {
    // 红色指示器centerX约束
    @IBOutlet weak var indicatorCenterX: NSLayoutConstraint!
    // 三个按钮属性
    @IBOutlet weak var btnOne: UIButton!
    @IBOutlet weak var btnTwo: UIButton!
    @IBOutlet weak var btnThree: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 初始状态默认对齐第一个按钮
        updateIndicatorPosition(target: btnOne, animated: false)
    }
    
    // 三个按钮统一绑定该点击事件
    @IBAction func buttonDidClick(_ sender: UIButton) {
        updateIndicatorPosition(target: sender, animated: true)
    }
    
    private func updateIndicatorPosition(target: UIButton, animated: Bool) {
        // 计算约束偏移量:目标按钮的横向中心 - 按钮父容器的横向中心
        guard let superView = btnOne.superview else { return }
        indicatorCenterX.constant = target.center.x - superView.center.x
        
        if animated {
            // 触发动画更新布局
            UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseInOut) {
                self.view.layoutIfNeeded()
            }
        } else {
            view.layoutIfNeeded()
        }
    }
}

原Storyboard完整XML代码

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Y6W-OH-hqX">
    <device id="retina6_1" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="s0d-6b-0kx">
            <objects>
                <viewController id="Y6W-OH-hqX" customClass="ViewController" customModule="testA" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc">
                        <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="d1U-7B-Tgv">
                                <rect key="frame" x="87" y="384" width="240" height="128"/>
                                <subviews>
                                    <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="eWg-Zb-swh">
                                        <rect key="frame" x="33" y="48.5" width="174" height="31"/>
                                        <subviews>
                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Zlh-MN-0CO">
                                                <rect key="frame" x="0.0" y="0.0" width="58" height="31"/>
                                                <color key="backgroundColor" red="0.59656708079999998" green="0.99476033450000001" blue="0.48015679830000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                <state key="normal" title="Button"/>
                                                <buttonConfiguration key="configuration" style="plain" title="one"/>
                                            </button>
                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Nhe-kU-2XT">
                                                <rect key="frame" x="58" y="0.0" width="58" height="31"/>
                                                <color key="backgroundColor" systemColor="systemTealColor"/>
                                                <state key="normal" title="Button"/>
                                                <buttonConfiguration key="configuration" style="plain" title="two"/>
                                            </button>
                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Xmg-JV-Sut">
                                                <rect key="frame" x="116" y="0.0" width="58" height="31"/>
                                                <color key="backgroundColor" red="0.59656708079999998" green="0.99476033450000001" blue="0.48015679830000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                <state key="normal" title="Button"/>
                                                <buttonConfiguration key="configuration" style="plain" title="three"/>
                                            </button>
                                        </subviews>
                                    </stackView>
                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qMv-J3-IRX">
                                        <rect key="frame" x="8" y="87" width="60" height="2"/>
                                        <color key="backgroundColor" systemColor="systemRedColor"/>
                                        <constraints>
                                            <constraint firstAttribute="height" constant="2" id="Cbd-ML-DDk"/>
                                            <constraint firstAttribute="width" constant="60" id="h9e-vK-yTX"/>
                                        </constraints>
                                    </view>
                                </subviews>
                                <color key="backgroundColor" systemColor="opaqueSeparatorColor"/>
                                <constraints>
                                    <constraint firstItem="eWg-Zb-swh" firstAttribute="centerY" secondItem="d1U-7B-Tgv" secondAttribute="centerY" id="0AA-FD-qJg"/>
                                    <constraint firstAttribute="height" constant="128" id="7Wb-jR-WDe"/>
                                    <constraint firstItem="eWg-Zb-swh" firstAttribute="centerX" secondItem="d1U-7B-Tgv" secondAttribute="centerX" id="CQD-Td-NtF"/>
                                    <constraint firstAttribute="width" constant="240" id="Oc9-es-sz8"/>
                                    <constraint firstItem="qMv-J3-IRX" firstAttribute="top" secondItem="eWg-Zb-swh" secondAttribute="bottom" constant="7.5" id="mm1-7c-P1U"/>
                                    <constraint firstItem="qMv-J3-IRX" firstAttribute="leading" secondItem="d1U-7B-Tgv" secondAttribute="leadingMargin" id="rob-qn-8zm"/>
                                </constraints>
                            </view>
                        </subviews>
                        <viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                        <constraints>
                            <constraint firstItem="d1U-7B-Tgv" firstAttribute="centerX" secondItem="5EZ-qb-Rvc" secondAttribute="centerX" id="t8W-rw-5oD"/>
                            <constraint firstItem="d1U-7B-Tgv" firstAttribute="centerY" secondItem="5EZ-qb-Rvc" secondAttribute="centerY" id="upa-9K-XOs"/>
                        </constraints>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="137.68115942028987" y="137.94642857142856"/>
        </scene>
    </scenes>
    <resources>
        <systemColor name="opaqueSeparatorColor">
            <color red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        </systemColor>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
        <systemColor name="systemRedColor">
            <color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        </systemColor>
        <systemColor name="systemTealColor">
            <color red="0.18823529411764706" green="0.69019607843137254" blue="0.7803921568627451" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        </systemColor>
    </resources>
</document>

内容的提问来源于stack exchange,提问作者jiexishede

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 23:54:03