Swift - 用动画显示 header

Swift - Display header with animation

我对 Swift 编码有点陌生,我想通过寻找一些布局并尝试使用 AutoLayout 实现它们来训练自己。

这是我尝试实现的屏幕截图

https://i.imgur.com/9yzvnzp.png

这里需要的行为是在滚动时图片应该淡出并且标题应该这样设置

https://i.imgur.com/bLhyTGs.png

任何人都可以帮助我我应该怎么做吗? 我应该使用 ScrollView 吗?界面表视图? UICollectionView ?

我实际上正在开发一个执行类似操作的应用程序。

因此,您要做的第一件事是为背景创建一个 UIView,即显示 Squirtle 的蓝色部分。这个视图可以占据整个视图控制器。通过 IBOutlet 将其连接到您的 UIViewController 并将其命名为 backgroundView.

接下来,我们要在 backgroundView 上放置一个 UIScrollView。这个滚动视图应该被限制在父视图的顶部、底部、前导和尾部边缘,以便它覆盖整个视图控制器的框架。通过 IBOutlet 将此 UIScrollView 连接到您的 UIViewController 并将其命名为 scrollView。另外,确保 scrollViewbackgroundColor 设置为 clear。这样,我们就可以在 scrollView.

下面看到 backgroundView

在您的 UIViewController 内,您需要将 scrollView 的委托设置为 viewDidLoad 内的 self。您的 UIViewController 的代码现在应该如下所示:

class SquirtleViewController: UIViewController {
    @IBOutlet var backgroundView: UIView!
    @IBOutlet var scrollView: UIScrollView!

    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self
    }
}

extension SquirtleViewController: UIScrollViewDelegate {

}

稍后我们将为 scrollView 设置委托方法。这些将帮助我们知道滚动视图何时开始滚动到我们 SquirtleViewController.

的顶部

现在,我们的 scrollView 有了清晰的背景,实际上不会向我们的用户显示任何内容。这就是我们现在想要的。我们希望 scrollView 的顶部清晰,以便我们可以看到它后面的 backgroundView,而 scrollView 的底部有另一个视图,即 "content view"显示 STATSEVOLUTIONSMOVESHP

所以,让我们添加另一个 UIView 作为我们 scrollView 的子视图。通过 IBOutlet 将其连接到 SquirtleViewController 并将其命名为 contentView

现在,我们需要在 contentView 的上边缘和 scrollView 的上边缘之间创建一个约束。此约束的常量需要等于 backgroundView 内容的高度。这样,我们的 contentView 就不会掩盖我们想从 backgroundView 中看到的内容。我们还应该将这个高度保存为 backgroundViewContentHeight 以便我们以后可以引用它。

我们还需要 contentViewleadingtrailingbottom 约束与其父视图的约束相同,即 scrollView。这些不需要通过 IBOutlets.

连接

此外,给 contentView 一个 heightwidth 约束,并将它们分别作为 contentViewHeightcontentViewWidth 通过 IBOutlet。这将帮助我们稍后设置 scrollViewcontentSize

您的代码现在应该如下所示:

class SquirtleViewController: UIViewController {
    @IBOutlet var backgroundView: UIView!
    @IBOutlet var scrollView: UIScrollView!
    @IBOutlet var contentView: UIView!

    //New lines of code
    //Our constraints
    @IBOutlet var contentViewTop: NSLayoutConstraint!
    @IBOutlet var contentViewHeight: NSLayoutConstraint!
    @IBOutlet var contentViewWidth: NSLayoutConstraint!

    var backgroundViewContentHeight = 400

    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self
    }
}

extension SquirtleViewController: UIScrollViewDelegate {

}

现在,当 SquirtleViewController 布置其子视图时,我们将要设置 contentViewHeightcontentViewWidthconstant 属性。这将为我们的 scrollView 设置一个 contentSize。我们希望我们的 contentViewWidth 只是 SquirtleViewControllerview 宽度的大小。我们的 contentViewHeightconstant 会有点不同,因为它取决于我们 contentView 的子视图的高度。对于这个例子,我们假设 contentViewHeightconstant 应该是 1200。代码现在应该是这样的:

class SquirtleViewController: UIViewController {
    @IBOutlet var backgroundView: UIView!
    @IBOutlet var scrollView: UIScrollView!
    @IBOutlet var contentView: UIView!

    //Our constraints
    @IBOutlet var contentViewTop: NSLayoutConstraint!
    @IBOutlet var contentViewHeight: NSLayoutConstraint!
    @IBOutlet var contentViewWidth: NSLayoutConstraint!

    var backgroundViewContentHeight = 400

    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self
    }

    //New lines of code
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        contentViewWidth.constant = view.frame.width
        contentViewHeight.constant = 1200
    }
}

extension SquirtleViewController: UIScrollViewDelegate {

}

从这里,您可以根据需要向 backgroundViewcontentView 添加子视图,以显示宠物小精灵 (backgroundView) 以及宠物小精灵的统计信息 (contentView).但是,您仍然需要知道如何根据我们滚动 scrollView 的程度来更改 backgroundView 的内容。这就是 UIScrollViewDelegate 帮助我们的地方。

UIScrollViewDelegate 中有一个方法,每次我们的 scrollViewcontentOffset.y 发生变化时都会调用该方法。这基本上意味着每次我们更改 scrollView 滚动的数量时,都会调用此方法。

在这个方法中,我们可以交叉引用我们滚动的量和背景视图的高度。当我们的 scrollViewcontentOffset.y 接近我们 backgroundView 内容的高度时,我们可以淡出 Squirtle 的图像并淡入 UILabel,它简单地说 "Squirtle"(就像你的例子)。

因此,在您的情况下,我建议添加 Squirtle 的 UIImage 作为 contentView 的子视图,并通过 IBOutlet 作为 pokemonImageView 连接它。 pokemonImageView 的框架应该部分位于 contentView 之外(就像您的示例一样)。如果这样做,请确保 contentViewclipsToBounds 属性 设置为 false.

我还会添加一个 UILabel 作为 backgroundView 的子视图,并通过 IBOutlet 作为 pokemonNameLabel 将其连接到 SquirtleViewController。当我们的 SquirtleViewController 视图加载时,我们应该将 pokemonNameLabel.alpha 设置为零,以便它最初是隐藏的。

您的代码现在应该如下所示:

class SquirtleViewController: UIViewController {
    @IBOutlet var backgroundView: UIView!

    //New line of code
    @IBOutlet var pokemonNameLabel: UILabel!
    @IBOutlet var scrollView: UIScrollView!
    @IBOutlet var contentView: UIView!

    //New line of code
    @IBOutlet var pokemonImageView: UIImageView!

    //Our constraints
    @IBOutlet var contentViewTop: NSLayoutConstraint!
    @IBOutlet var contentViewHeight: NSLayoutConstraint!
    @IBOutlet var contentViewWidth: NSLayoutConstraint!

    var backgroundViewContentHeight = 400

    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self

        //New line of code
        pokemonNameLabel.alpha = 0
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        contentViewWidth.constant = view.frame.width
        contentViewHeight.constant = 1200
    }
}

extension SquirtleViewController: UIScrollViewDelegate {

}

现在我们只需要在 class 扩展中添加 scrollViewDidScroll 方法。这是将告诉我们 scrollViewcontentOffset.y 属性 的方法,以便我们知道我们滚动了多少。随着这个数字的增加,我们的 pokemonImageView 的 alpha 应该减少到零,我们的 pokemonNameLabel 的 alpha 应该增加到 1。

在这个方法中,我会将 scrollView.contentOffset.y 除以 backgroundView 的高度减去 pokemonNameLabel 的高度。然后我们可以使用这个小数来设置我们各自的 pokemonImageViewpokemonNameLabel.

的 alpha

您的代码现在应该如下所示:

class SquirtleViewController: UIViewController {
    @IBOutlet var backgroundView: UIView!

    //New line of code
    @IBOutlet var pokemonNameLabel: UILabel!
    @IBOutlet var scrollView: UIScrollView!
    @IBOutlet var contentView: UIView!

    //New line of code
    @IBOutlet var pokemonImageView: UIImageView!

    //Our constraints
    @IBOutlet var contentViewTop: NSLayoutConstraint!
    @IBOutlet var contentViewHeight: NSLayoutConstraint!
    @IBOutlet var contentViewWidth: NSLayoutConstraint!

    var backgroundViewContentHeight = 400

    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self

        //New line of code
        pokemonNameLabel.alpha = 0
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        contentViewWidth.constant = view.frame.width
        contentViewHeight.constant = 1200
    }
}

extension SquirtleViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        //Subtracting contentView.frame.height from scrollView.frame.height
        let alphaDecimal = scrollView.contentOffset.y / (backgroundViewContentHeight - pokemonNameLabel.frame.height)
        pokemonNameLabel.alpha = alphaDecimal
        pokemonImageView.alpha = 1 - alphaDecimal
    }
}

现在,当我们向上滚动时,可以看到 backgroundView 缩小,我们的 pokemonImageView 会淡出,我们的 pokemonNameLabel 会淡入。会有一个点in-between 其中两者的 alpha 均为 0.5。你可以随意使用这个 scrollViewDidScroll 方法or 最适合你。