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

iOS水果详情页背景动画致返回按钮与下滑关闭失效问题

问题描述

我开发了一款展示水果列表的iOS应用,点击任意水果会弹出详情页,显示该水果的全部信息与营养成分。当选中的水果含糖量超过10时,详情页背景需在红色与白色之间循环切换动画。目前遇到的问题是:这类带背景动画的详情页,其返回按钮(Tilbake)和下滑关闭页面的功能均失效,疑似有图层阻挡了屏幕交互,该如何解决?

问题分析

你的问题核心在于背景动画默认禁用了用户交互,同时当前的动画实现逻辑存在冗余嵌套:

  • UIView的animate方法默认会在动画期间将视图的userInteractionEnabled设为false,导致整个页面无法响应触摸操作
  • 你在第一个动画的completion回调里又启动了另一个重复动画,这种嵌套会持续占用动画队列,进一步加剧交互阻塞
解决方案

1. 修复动画逻辑,添加允许交互的选项

不需要嵌套两个重复动画,只需要一次动画设置.repeat和.autoreverse,同时加上.allowsUserInteraction选项,让动画运行时不阻止用户交互。

2. 优化动画实现

直接在单次动画中完成红白循环,避免不必要的嵌套:

func animatedBackground(){
    UIView.animate(withDuration: 1.0, delay: 0.0, options: [.repeat, .autoreverse, .allowsUserInteraction], animations: {
        self.view.backgroundColor = .red
    }, completion: nil)
}

3. 额外检查项

  • 确认返回按钮dismissButton的约束正确,确保它在屏幕可见区域内
  • 检查所有子视图,有没有意外设置userInteractionEnabled = false或者层级覆盖了按钮(当前代码里的标签默认不可交互,不会影响)
  • 如果是用present方式弹出详情页,下滑关闭需要确保呈现方式支持该交互(比如设置modalPresentationStyle = .pageSheet)
修改后的完整代码
import UIKit

class FruitDetailVC: UIViewController{
    var fruit: Fruit?
    
    var safeArea: UILayoutGuide!
    let nameLabel = UILabel()
    let genusLabel = UILabel()
    let familyLabel = UILabel()
    let orderLabel = UILabel()
    let idLabel = UILabel()
    let carbLabel = UILabel()
    let proteinLabel = UILabel()
    let fatLabel = UILabel()
    let caloriesLabel = UILabel()
    let sugarLabel = UILabel()
    let dividerLabel = UILabel()
    let sugarWarningLabel = UILabel()
    let dismissButton = UIButton()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupdata()
        setupLabel()
        setupDismissButton()
        safeArea = view.layoutMarginsGuide
        view.backgroundColor = .white
        // 替换强制解包为可选绑定,避免崩溃
        if let sugar = fruit?.nutritions.sugar, sugar > 10 {
            sugarWarningLabel.text = "This contains over 10 of sugar!!!"
            animatedBackground()
        }
    }
    
    func animatedBackground(){
        // 修复动画逻辑,添加允许交互选项
        UIView.animate(withDuration: 1.0, delay: 0.0, options: [.repeat, .autoreverse, .allowsUserInteraction], animations: {
            self.view.backgroundColor = .red
        }, completion: nil)
    }
    
    
    func setupLabel(){
        view.addSubview(nameLabel)
        view.addSubview(genusLabel)
        view.addSubview(familyLabel)
        view.addSubview(orderLabel)
        view.addSubview(idLabel)
        view.addSubview(carbLabel)
        view.addSubview(proteinLabel)
        view.addSubview(fatLabel)
        view.addSubview(caloriesLabel)
        view.addSubview(sugarLabel)
        view.addSubview(dividerLabel)
        view.addSubview(sugarWarningLabel)
        
        // 水果基础信息
        nameLabel.translatesAutoresizingMaskIntoConstraints = false
        nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        nameLabel.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        nameLabel.font = UIFont(name: "Verdana-Bold", size: 16)
        
        genusLabel.translatesAutoresizingMaskIntoConstraints = false
        genusLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        genusLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor).isActive = true
        genusLabel.font = UIFont(name: "Verdana", size: 14)
        
        familyLabel.translatesAutoresizingMaskIntoConstraints = false
        familyLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        familyLabel.topAnchor.constraint(equalTo: genusLabel.bottomAnchor).isActive = true
        familyLabel.font = UIFont(name: "Verdana", size: 14)
        
        orderLabel.translatesAutoresizingMaskIntoConstraints = false
        orderLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        orderLabel.topAnchor.constraint(equalTo: familyLabel.bottomAnchor).isActive = true
        orderLabel.font = UIFont(name: "Verdana", size: 14)
        
        idLabel.translatesAutoresizingMaskIntoConstraints = false
        idLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        idLabel.topAnchor.constraint(equalTo: orderLabel.bottomAnchor).isActive = true
        idLabel.font = UIFont(name: "Verdana", size: 14)
        
        // 营养信息分隔线
        dividerLabel.translatesAutoresizingMaskIntoConstraints = false
        dividerLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        dividerLabel.topAnchor.constraint(equalTo: idLabel.bottomAnchor).isActive = true
        dividerLabel.font = UIFont(name: "Verdana-Bold", size: 16)
        
        // 营养信息
        carbLabel.translatesAutoresizingMaskIntoConstraints = false
        carbLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        carbLabel.topAnchor.constraint(equalTo: dividerLabel.bottomAnchor).isActive = true
        carbLabel.font = UIFont(name: "Verdana", size: 14)
        
        proteinLabel.translatesAutoresizingMaskIntoConstraints = false
        proteinLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        proteinLabel.topAnchor.constraint(equalTo: carbLabel.bottomAnchor).isActive = true
        proteinLabel.font = UIFont(name: "Verdana", size: 14)
        
        fatLabel.translatesAutoresizingMaskIntoConstraints = false
        fatLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        fatLabel.topAnchor.constraint(equalTo: proteinLabel.bottomAnchor).isActive = true
        fatLabel.font = UIFont(name: "Verdana", size: 14)
        
        sugarLabel.translatesAutoresizingMaskIntoConstraints = false
        sugarLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        sugarLabel.topAnchor.constraint(equalTo: fatLabel.bottomAnchor).isActive = true
        sugarLabel.font = UIFont(name: "Verdana", size: 14)
        
        sugarWarningLabel.translatesAutoresizingMaskIntoConstraints = false
        sugarWarningLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        sugarWarningLabel.topAnchor.constraint(equalTo: sugarLabel.bottomAnchor).isActive = true
        sugarWarningLabel.font = UIFont(name: "Verdana-Bold", size: 14)
    }
    
    func setupdata(){
        if let fruit = fruit{
            nameLabel.text = fruit.name
            genusLabel.text = "Genus : \(fruit.genus)"
            familyLabel.text = "Family : \(fruit.family)"
            orderLabel.text = "Order : \(fruit.order)"
            dividerLabel.text = "Nutritional value"
            
            let id = fruit.id
            let idString = String(id)
            idLabel.text = "ID : \(idString)"
            
            let carbs = fruit.nutritions.carbohydrates
            let carbString = String(carbs)
            carbLabel.text = "Carbohydrates : \(carbString)"
            
            let protein = fruit.nutritions.protein
            let proteinString = String(protein)
            proteinLabel.text = "Proteins : \(proteinString)"
            
            let fat = fruit.nutritions.fat
            let fatString = String(fat)
            fatLabel.text = "Fat : \(fatString)"
            
            let calories = fruit.nutritions.calories
            let calorieString = String(calories)
            caloriesLabel.text = "Calories : \(calorieString)" // 修复原代码文字错误
            
            let sugar = fruit.nutritions.sugar
            let sugarString = String(sugar)
            sugarLabel.text = "Sugar : \(sugarString)"
        }
    }
    
    func setupDismissButton(){
        view.addSubview(dismissButton)
        
        dismissButton.translatesAutoresizingMaskIntoConstraints = false
        dismissButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true
        dismissButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        
        dismissButton.setTitle("Tilbake",for: .normal)
        dismissButton.setTitleColor(UIColor.black, for: .normal)
        dismissButton.addTarget(self, action: #selector(dismissAction), for: .touchUpInside)
    }
    
    @objc func dismissAction(){
        self.dismiss(animated: true)
    }
}

内容的提问来源于stack exchange,提问作者Jonathan S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 04:20:23