【问题标题】:UIView.animate works fine in viewDidAppear() but fires instantly anywhere elseUIView.animate 在 vi​​ewDidAppear() 中工作正常,但在其他任何地方立即触发
【发布时间】:2018-03-13 09:02:26
【问题描述】:

我已经查看并尝试了我可以在网上找到的所有解决方案,以了解为什么我的动画无法正常触发。披露:当放入 viewDidAppear() 时,它们可以正常工作。这个问题被问了无数次,但没有一个解决方案有效。动画的结果出现但没有指定延迟,它们是即时的。我想要的是使用带有 UIView.transition 的 isHidden 或带有 UIView.animate 的 alpha 淡入和淡出我的 requestRideButton。

我还想在按钮消失时移动它。我已经尝试了 self.view.layoutIfNeeded() 的每一种组合,无论是在动画闭包中还是在带有约束进出的动画闭包中。我也试过 self.view.superview?.layoutIfNeeded()

class RideRequestViewController: UIViewController, MKMapViewDelegate {

@IBOutlet weak var rideRequestBottomButtonConstraint: NSLayoutConstraint!
@IBOutlet weak var rideRequestButtonTopConstraint: NSLayoutConstraint!

// Views
@IBOutlet var mapView: MKMapView!

@IBOutlet var rideDetailsView: UIView!

@IBOutlet var requestRideButton: UIButton!

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // Layout map view to fill screen
    mapView.frame = view.bounds

    // Apply corner radius and shadow styling to floating views
    let cornerRadius: CGFloat = 5.0

    inputContainerView.layoutCornerRadiusAndShadow(cornerRadius: cornerRadius)
    originButton.layoutCornerRadiusMask(corners: [.topLeft, .topRight], cornerRadius: cornerRadius)
    paymentButton.layoutCornerRadiusMask(corners: .bottomLeft, cornerRadius: cornerRadius)
    priceButton.layoutCornerRadiusMask(corners: .bottomRight, cornerRadius: cornerRadius)

    rideDetailsView.layoutCornerRadiusAndShadow(cornerRadius: cornerRadius)
    pilotView.layoutCornerRadiusMask(corners: [.topLeft, .bottomLeft], cornerRadius: cornerRadius)
    vehicleView.layoutCornerRadiusMask(corners: [.topRight, .bottomRight], cornerRadius: cornerRadius)

    requestRideButton.layoutCornerRadiusAndShadow(cornerRadius: cornerRadius)

}

override func viewDidLoad() {
    ref = Database.database().reference()
}

func animate() {
    self.rideRequestButtonTopConstraint.constant = -44
    UIView.animate(withDuration: 1) {
        self.view.layoutIfNeeded()
    }
}

@IBAction
private func handleRequestRideButtonTapped() {
    switch rideRequestState {
    case .none:
        // Update to requesting state
        rideRequestState = .requesting
        // Perform payment request
        paymentContext.requestPayment()
    case .requesting:
        // Do nothing
        // Show button
        break
    case .active:
        // Complete the ride
        completeActiveRide()
    }
}

private func reloadRequestRideButton() {
    guard originPlacemark != nil && destinationPlacemark != nil && paymentContext.selectedPaymentMethod != nil else {
        // Show disabled state
        requestRideButton.backgroundColor = .riderGrayColor
        requestRideButton.setTitle("CONFIRM DELIVERY", for: .normal)
        requestRideButton.setTitleColor(.black, for: .normal)
        requestRideButton.setImage(nil, for: .normal)
        requestRideButton.isEnabled = false
        return
    }

    animate() // <--- view just disappears instantly instead
    switch rideRequestState {
    case .none:
        // Show enabled state
        requestRideButton.backgroundColor = .riderYellowColor
        requestRideButton.setTitle("CONFIRM DELIVERY", for: .normal)
        requestRideButton.setTitleColor(.black, for: .normal)
        requestRideButton.setImage(nil, for: .normal)
        requestRideButton.isEnabled = true
    case .requesting:
        // Show loading state
        requestRideButton.backgroundColor = .riderYellowColor
        requestRideButton.setTitle("...", for: .normal)
        requestRideButton.setTitleColor(.white, for: .normal)
        requestRideButton.setImage(nil, for: .normal)
        requestRideButton.isEnabled = false
    case .active:
        // Show completion state
        requestRideButton.backgroundColor = .white
        requestRideButton.setTitle("Complete Ride", for: .normal)
        requestRideButton.setTitleColor(.riderDarkBlueColor, for: .normal)
        requestRideButton.setImage(nil, for: .normal)
        requestRideButton.isEnabled = true
    }
}

【问题讨论】:

  • 从哪里调用此方法“reloadRequestRideButton”
  • 可能重复:stackoverflow.com/questions/12622424/…。也是其中一个答案的一部分 (@ankit):以下是教授 iOS 的 Martin Pilkington 和编写 Auto Layout 的 Ken Ferry 之间的 Twitter 对话。 Ken 解释说,虽然在动画块之外更改常量目前可能有效,但它并不安全,它们确实应该在动画块内进行更改。 twitter.com/kongtomorrow/status/440627401018466305
  • @ankit 完整的代码会很长,但它在不同的地方被调用,比如:private var pinchPlacemark: MKPlacemark? { didSet { reloadRequestRideButton() } }
  • @Scriptable 我在块中使用了 layoutIfNeeded() 但它不起作用
  • @Scriptable,如果我们不在块中编写 layoutIfNeeded 和来自 WWDC,如何触发动画,他们在那里调整了块外的常量,只是在里面做了布局。

标签: swift uiview uiviewanimation


【解决方案1】:

要动画的视图的更改应该在动画块内。

func animate() {
    UIView.animate(withDuration: 1) {
        self.rideRequestButtonTopConstraint.constant = -44
        self.view.layoutIfNeeded()
    }
}

更改上面一行中的约束只会更新一个值并移动视图。它不会为它设置动画

更新

因此,基于查看另一个answer 和一些额外的研究,UIKit 的作者建议我们更新约束并在动画块内调用 layoutIfNeeded,如上。

这是我用来测试的游乐场

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let movableView = UIView()
    var topConstraint: NSLayoutConstraint?

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
        view.addSubview(button)
        button.setTitle("Animate", for: .normal)
        button.addTarget(self, action: #selector(animate), for: .touchUpInside)

        view.addSubview(movableView)

        movableView.backgroundColor = .red
        movableView.translatesAutoresizingMaskIntoConstraints = false
        topConstraint = NSLayoutConstraint(item: movableView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 100)
        topConstraint?.isActive = true

        NSLayoutConstraint(item: movableView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
        NSLayoutConstraint(item: movableView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 100).isActive = true
        NSLayoutConstraint(item: movableView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 100).isActive = true
    }

    @objc func animate() {

        UIView.animate(withDuration: 4.0, animations: {
            self.topConstraint?.constant = -100
            self.view.layoutIfNeeded()
        })
    }
}


let vc = ViewController()

PlaygroundPage.current.liveView = vc.view

【讨论】:

  • 当我这样做时,结果甚至根本不会发生
  • @Scriptable, layoutIfNeeded() 应该在 block 中,而不是常量。
猜你喜欢
  • 1970-01-01
  • 2012-05-07
  • 1970-01-01
  • 1970-01-01
  • 2021-11-14
  • 1970-01-01
  • 2021-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多