为什么除非包装在 UIView.Animate 块中,否则我的 UIView.Transition 不起作用?

如何解决为什么除非包装在 UIView.Animate 块中,否则我的 UIView.Transition 不起作用?

我正在使用 UIView.transition 翻转卡片。

虽然故障排除它不起作用,但我偶然发现了一种使它起作用的方法 - 但我不知道为什么。我希望有人可以查看下面的两个代码块并帮助我理解为什么一个有效而另一个无效。我觉得很奇怪。

首先,这是实际工作的代码块。卡片翻转在视觉上完美无瑕。

UIView.animate(withDuration: 0.01) {
    imageView.alpha = 1.0
    imageView.layoutIfNeeded()       // Works with and without this layoutIfNeeded()
} completion: { (true) in
    UIView.transition(with: imageView,duration: 1.2,options: animation) {
        imageView.image = endingImage
        imageView.layoutIfNeeded()   // Works with and without this layoutIfNeeded()
    } completion: { (true) in
        if self.dealTicketState.isTicketFaceUp == true { self.faceDownView.alpha = 0.0 } else { self.faceDownView.alpha = 1.0 }
        UIView.animate(withDuration: 0.01) {
            self.coveringLabel.backgroundColor = .clear
            self.coveringLabel.layoutIfNeeded()
            imageView.removeFromSuperview()
            self.tickNumLabel.alpha = originalTicketNumAlpha
        }
    }
}

但我不明白为什么我似乎需要将 UIView.transition() 包装到调用 UIView.animate() 的完成处理程序中,以便翻转动画工作。
*(注意:如果我从 animate() 块中拉出“imageView.alpha = 1.0”并在调用 UIView.animate() 之前立即放置它 - 翻转动画不会发生(带有或没有 layoutIfNeeded() 调用。它只是切换图像。*

现在,这是我期望工作的代码 - 但是当我使用此代码而不是上面的代码时,没有“翻转”转换。卡片图像会立即在正面朝上和正面朝下图像之间切换。此处的“UIView.transition”调用与上述代码中的调用相同。这里唯一的区别是它NOT被包裹在一个 0.01 秒的 UIView.animate 完成块中。

imageView.alpha = 1.0
imageView.layoutIfNeeded()

UIView.transition(with: imageView,options: animation) {
            imageView.image = endingImage
            imageView.layoutIfNeeded()    // same behavIoUr with and without this line
} completion: { (true) in
    if self.dealTicketState.isTicketFaceUp == true { self.faceDownView.alpha = 0.0 } else { self.faceDownView.alpha = 1.0 }
    UIView.animate(withDuration: 0.01) {
        self.coveringLabel.backgroundColor = .clear
        self.coveringLabel.layoutIfNeeded()
        imageView.removeFromSuperview()
        self.tickNumLabel.alpha = originalTicketNumAlpha
    }
}

这个转换结束了我的 flipTicket() 函数在这两种情况下,此转换之前的代码是相同的。我不打算包括它,因为我认为没有必要理解这个问题——但话说回来——我知道什么?以下是上述剪辑之前的内容

func flipTicket() {
    let originalTicketNumAlpha = self.tickNumLabel.alpha
    self.tickNumLabel.alpha = 0.0
    
    let tempFaceDownImage:UIImage = self.dealTicketState.faceDownImage
    let tempFaceUpImage:UIImage = getCurrentFaceUpImage()
    var endingImage:UIImage = self.dealTicketState.faceDownImage
    
    let imageView = UIImageView()
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.contentMode = .scaletoFill
    imageView.clipsToBounds = true
    imageView.alpha = 0.0
    
    self.coveringLabel.alpha = 1.0
    self.coveringLabel.backgroundColor = .black
    self.coveringLabel.layoutIfNeeded()
    
    var animation:UIView.Animationoptions = .transitionFlipFromLeft
    
    if faceDownView.alpha == 1.0 {
        animation = .transitionFlipFromright
        imageView.image = tempFaceDownImage
        endingImage = tempFaceUpImage
    } else {
        animation = .transitionFlipFromLeft
        imageView.image = tempFaceUpImage
    }
    self.addSubview(imageView)
    NSLayoutConstraint.activate([
        imageView.topAnchor.constraint(equalTo: self.topAnchor),imageView.bottomAnchor.constraint(equalTo: self.bottomAnchor),imageView.leadingAnchor.constraint(equalTo: self.leadingAnchor),imageView.trailingAnchor.constraint(equalTo: self.trailingAnchor),])
    imageView.layoutIfNeeded()

背景: 此代码是代表扑克牌的自定义 UI 控件的一部分。它由几个子视图组成。最上面的子视图最初是一个 UIImageView,它保存了卡片背面的图像。这让我可以简单地切换该顶视图的 alpha 以将卡片显示为面朝上或面朝下。然后我向控件添加一个 topMost 视图 - 一个带有“background = .clear”的 UILabel 并附加了一个 UITapGestureRecognizer 到它。当点击控件时,将调用函数,该函数旨在为卡片的翻转设置动画。

为了构建动画,我调用了 getCurrentFaceUp() 函数,该函数临时将卡片的 faceDownView 的 alpha 设置为 0(因此我可以根据当前配置对卡片下方的卡片进行快照)。它返回卡片“面朝上”视图的 UIImage。我已经有了 faceDown 视图的 UIImage。这些是过渡所需的 2 张图片

所以...然后我将 topMost UILabel 的背景颜色设置为 .black,创建一个新的临时 UIImageView 并将其放在现有控件的顶部。我将临时 imageView 设置为最初显示控件上当前可见的 2 个图像中的任何一个。然后我运行翻转过渡,更改背景控件的配置以匹配新状态,将标签背景更改回 .clear 并处理临时 UIImageView。
(如果有更好的方法来实现这一点,我愿意倾听,但这篇文章的主要目的是了解为什么我的代码看起来很奇怪。)

当我在寻找一种为卡片翻转设置动画的方法时,我发现了一个 YouTube 视频,该视频演示了 UIView.transition() 与翻转动画。它不需要使用 UIView.animate() 包装器来使其工作 - 所以我很确定是我做错了 - 但我花了几个小时测试变体并寻找其他有这个问题的人,我一直没能找到答案。

非常感谢任何可以帮助我了解这里发生的事情的人......

解决方法

有点难说(没有尝试实际运行您的代码),但我认为您可能做的比需要的多。

看看这个...

我们将从两个“卡片视图”子类开始 - Front 和 Back(我们将在下一步中对它们进行“样式化”):

class CardFrontView: UIView {
}
class CardBackView: UIView {
}

然后是一个“扑克牌视图”类,它包含一个“前”视图(青色)和一个“后”视图(红色)。在初始化时,我们添加子视图并将“前”视图设置为隐藏。点击后,我们将在 Front 和 Back 视图之间运行翻转过渡:

class PlayingCardView: UIView {
    
    let cardFront: CardFrontView = CardFrontView()
    let cardBack: CardBackView = CardBackView()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {

        // add both card views
        // constraining all 4 sides to self
        [cardFront,cardBack].forEach { v in
            addSubview(v)
            v.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                v.topAnchor.constraint(equalTo: topAnchor),v.leadingAnchor.constraint(equalTo: leadingAnchor),v.trailingAnchor.constraint(equalTo: trailingAnchor),v.bottomAnchor.constraint(equalTo: bottomAnchor),])
        }
        
        cardFront.backgroundColor = .cyan
        cardBack.backgroundColor = .red

        // start with cardFront hidden
        cardFront.isHidden = true
        
        // add a tap recognizer
        let t = UITapGestureRecognizer(target: self,action: #selector(flipMe))
        addGestureRecognizer(t)
        
    }
    
    @objc func flipMe() -> Void {
        // fromView is the one that is NOT hidden
        let fromView = cardBack.isHidden ? cardFront : cardBack

        // toView is the one that IS hidden
        let toView = cardBack.isHidden ? cardBack : cardFront

        // if we're going from back-to-front
        //  flip from left
        // else
        //  flip from right
        let direction: UIView.AnimationOptions = cardBack.isHidden ? .transitionFlipFromRight : .transitionFlipFromLeft
        
        UIView.transition(from: fromView,to: toView,duration: 0.5,options: [direction,.showHideTransitionViews],completion: { b in
                            // if we want to do something on completion
                          })
    }
    
}

然后是一个简单的控制器示例:

class FlipCardVC: UIViewController {
    
    let pCard: PlayingCardView = PlayingCardView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let g = view.safeAreaLayoutGuide
        
        pCard.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(pCard)
        
        NSLayoutConstraint.activate([
            pCard.centerXAnchor.constraint(equalTo: g.centerXAnchor),pCard.centerYAnchor.constraint(equalTo: g.centerYAnchor),pCard.widthAnchor.constraint(equalToConstant: 200.0),pCard.heightAnchor.constraint(equalTo: pCard.widthAnchor,multiplier: 1.5),])
        
    }
    
}

结果:

enter image description here

所以,下一步,我们将向 Front 和 Back 视图添加一些样式 -- 没有更改 PlayingCardView 功能...只是几行新行来设置样式...>

卡片正面视图 - 带有圆角、边框和边角和中心的标签:

class CardFrontView: UIView {
    
    var theLabels: [UILabel] = []
    
    var cardID: Int = 0 {
        didSet {
            theLabels.forEach {
                $0.text = "\(cardID)"
            }
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        
        for i in 1...5 {
            let v = UILabel()
            v.font = .systemFont(ofSize: 24.0)
            v.translatesAutoresizingMaskIntoConstraints = false
            addSubview(v)
            switch i {
            case 1:
                v.topAnchor.constraint(equalTo: topAnchor,constant: 10.0).isActive = true
                v.leadingAnchor.constraint(equalTo: leadingAnchor,constant: 16.0).isActive = true
            case 2:
                v.topAnchor.constraint(equalTo: topAnchor,constant: 10.0).isActive = true
                v.trailingAnchor.constraint(equalTo: trailingAnchor,constant: -16.0).isActive = true
            case 3:
                v.bottomAnchor.constraint(equalTo: bottomAnchor,constant: -10.0).isActive = true
                v.leadingAnchor.constraint(equalTo: leadingAnchor,constant: 16.0).isActive = true
            case 4:
                v.bottomAnchor.constraint(equalTo: bottomAnchor,constant: -10.0).isActive = true
                v.trailingAnchor.constraint(equalTo: trailingAnchor,constant: -16.0).isActive = true
            default:
                v.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
                v.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
            }
            theLabels.append(v)
        }
        
        layer.cornerRadius = 6
        
        // border
        layer.borderWidth = 1.0
        layer.borderColor = UIColor.gray.cgColor
        
    }
    
}

看起来像这样:

enter image description here

卡片背面视图 - 带有圆角、边框和交叉影线图案:

class CardBackView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {

        layer.cornerRadius = 6
        
        // border
        layer.borderWidth = 1.0
        layer.borderColor = UIColor.gray.cgColor
        
        layer.masksToBounds = true
        
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        // simple cross-hatch pattern
        let hReplicatorLayer = CAReplicatorLayer()
        let vReplicatorLayer = CAReplicatorLayer()
        
        let line = CAShapeLayer()
        let pth = UIBezierPath()
        
        pth.move(to: CGPoint(x: 0.0,y: 0.0))
        pth.addLine(to: CGPoint(x: 20.0,y: 20.0))
        pth.move(to: CGPoint(x: 20.0,y: 0.0))
        pth.addLine(to: CGPoint(x: 0.0,y: 20.0))
        line.strokeColor = UIColor.yellow.cgColor
        line.lineWidth = 1
        line.path = pth.cgPath
        
        var instanceCount = Int((bounds.maxX + 0.0) / 20.0)
        hReplicatorLayer.instanceCount = instanceCount
        hReplicatorLayer.instanceTransform = CATransform3DMakeTranslation(20,0)
        
        instanceCount = Int((bounds.maxY + 0.0) / 20.0)
        vReplicatorLayer.instanceCount = instanceCount
        vReplicatorLayer.instanceTransform = CATransform3DMakeTranslation(0,20,0)
        
        hReplicatorLayer.addSublayer(line)
        vReplicatorLayer.addSublayer(hReplicatorLayer)
        layer.addSublayer(vReplicatorLayer)
    }

}

看起来像这样:

enter image description here

扑克牌视图 - 唯一的变化是将前卡背景颜色设置为白色,并将其“ID”设置为 5:

class PlayingCardView: UIView {

    let cardFront: CardFrontView = CardFrontView()
    let cardBack: CardBackView = CardBackView()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        
        // add both card views
        // constraining all 4 sides to self
        [cardFront,])
        }
        
        cardFront.backgroundColor = .white
        cardFront.cardID = 5
        
        cardBack.backgroundColor = .red

        // start with cardFront hidden
        cardFront.isHidden = true
        
        // add a tap recognizer
        let t = UITapGestureRecognizer(target: self,action: #selector(flipMe))
        addGestureRecognizer(t)
        
    }
    
    @objc func flipMe() -> Void {
        // fromView is the one that is NOT hidden
        let fromView = cardBack.isHidden ? cardFront : cardBack
        
        // toView is the one that IS hidden
        let toView = cardBack.isHidden ? cardBack : cardFront
        
        // if we're going from back-to-front
        //  flip from left
        // else
        //  flip from right
        let direction: UIView.AnimationOptions = cardBack.isHidden ? .transitionFlipFromRight : .transitionFlipFromLeft
        
        UIView.transition(from: fromView,completion: { b in
                            // if we want to do something on completion
                          })
    }
    
}

最后,相同的控制器示例:

class FlipCardVC: UIViewController {
    
    let pCard: PlayingCardView = PlayingCardView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let g = view.safeAreaLayoutGuide
        
        pCard.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(pCard)
        
        NSLayoutConstraint.activate([
            pCard.centerXAnchor.constraint(equalTo: g.centerXAnchor),])
        
    }
    
}

这是新的结果:

enter image description here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?