ios - 具有动态大小内容的 UIScrollView

标签 ios swift xcode uiscrollview storyboard

(Xcode 11, swift )

作为 iOS 和 Autolayout 的新手,我正在努力实现一个相当简单的 (恕我直言) View ,该 View 显示一个 [垂直] 项目列表。唯一的问题是项目是动态决定的,它们中的每一个都可以是文本或图像(其中任何一个都可能相当大,因此需要滚动)。 WebView 不是一个选项,因此它必须在 native 实现。

我是这样理解这个过程的:

  • 在 IB 中创建一个 UIScrollView 并将其调整为外框的大小。
  • 创建一个容器 View 作为 UIScrollView 的 subview (同样是在 IB 中)并设置相同的大小。
  • 设置两者宽度相等的约束
  • 在运行时,使用 UILabels/UIImageViews 填充容器 View ,并以编程方式设置约束以确保正确的布局。
  • “告诉”scrollview subview 的高度,以使其管理其滚动。

这是正确的方法吗?它似乎对我不起作用(对于动态添加非常高的图像到容器 View 的玩具示例 - 我无法让滚动工作)。执行上述过程中最后一步的正确方法是什么 - 只需将 ScrollView 的 contentSize 强制为填充的容器 View 的大小(它似乎对我不起作用)。任何帮助将不胜感激。

最佳答案

在运行时向 ScrollView 添加多个元素时,您可能会发现使用 UIStackView 更容易...如果设置正确,它会随着每个添加的对象自动增加高度.

作为一个简单的例子...

1) 首先添加一个 UIScrollView(我给它设置了蓝色背景以便于查看)。在所有 4 个边上将其约束为零:

enter image description here

请注意,我们看到“红圈”表示缺少/冲突约束。暂时忽略它。

2) 添加一个UIView作为 ScrollView 的“内容 View ”(我给它一个systemYellow背景以便于查看)。根据Content Layout Guide在所有 4 个边上将其限制为零——这将(最终)定义 ScrollView 的内容大小。同时根据Frame Layout Guide 将其限制为等宽等高:

enter image description here

重要步骤:选择高度约束,并在 Size Inspector Pane 中选择 Placeholder - Remove at build time 复选框。这将在设计期间满足 IB 中的自动布局,但将允许该 View 的高度根据需要缩小/增大。

3) 添加垂直 UIStackView 到“内容 View ”。在所有 4 个边上将其约束为零。配置其属性为Fill/Fill/8(如下图):

enter image description here

4) 添加一个@IBOutlet 连接到 View Controller 类中的堆栈 View 。现在,在运行时,当您将 UI 元素添加到堆栈 View 时,所有“可滚动性”都将由自动布局处理。

这是一个示例类:

class DynaScrollViewController: UIViewController {

    @IBOutlet var theStackView: UIStackView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // local var so we can reuse it
        var theLabel = UILabel()
        var theImageView = UIImageView()

        // create a new label
        theLabel = UILabel()
        // this gets set to false when the label is added to a stack view,
        // but good to get in the habit of setting it
        theLabel.translatesAutoresizingMaskIntoConstraints = false
        // multi-line
        theLabel.numberOfLines = 0
        // cyan background to make it easy to see
        theLabel.backgroundColor = .cyan
        // add 9 lines of text to the label
        theLabel.text = (1...9).map({ "Line \($0)" }).joined(separator: "\n")

        // add it to the stack view
        theStackView.addArrangedSubview(theLabel)

        // add another label
        theLabel = UILabel()
        // multi-line
        theLabel.numberOfLines = 0
        // yellow background to make it easy to see
        theLabel.backgroundColor = .yellow
        // add 5 lines of text to the label
        theLabel.text = (1...5).map({ "Line \($0)" }).joined(separator: "\n")

        // add it to the stack view
        theStackView.addArrangedSubview(theLabel)

        // create a new UIImageView
        theImageView = UIImageView()
        // this gets set to false when the label is added to a stack view,
        // but good to get in the habit of setting it
        theImageView.translatesAutoresizingMaskIntoConstraints = false
        // load an image for it - I have one named background
        if let img = UIImage(named: "background") {
            theImageView.image = img
        }
        // let's give the image view a 4:3 width:height ratio
        theImageView.widthAnchor.constraint(equalTo: theImageView.heightAnchor, multiplier: 4.0/3.0).isActive = true

        // add it to the stack view
        theStackView.addArrangedSubview(theImageView)

        // add another label
        theLabel = UILabel()
        // multi-line
        theLabel.numberOfLines = 0
        // yellow background to make it easy to see
        theLabel.backgroundColor = .green
        // add 2 lines of text to the label
        theLabel.text = (1...2).map({ "Line \($0)" }).joined(separator: "\n")

        // add it to the stack view
        theStackView.addArrangedSubview(theLabel)

        // add another UIImageView
        theImageView = UIImageView()
        // this gets set to false when the label is added to a stack view,
        // but good to get in the habit of setting it
        theImageView.translatesAutoresizingMaskIntoConstraints = false
        // load a different image for it - I have one named AquariumBG
        if let img = UIImage(named: "AquariumBG") {
            theImageView.image = img
        }
        // let's give this image view a 1:1 width:height ratio
        theImageView.heightAnchor.constraint(equalTo: theImageView.widthAnchor, multiplier: 1.0).isActive = true

        // add it to the stack view
        theStackView.addArrangedSubview(theImageView)

    }

}

如果执行了这些步骤,您应该得到以下输出:

enter image description here

并且,在滚动到底部之后:

enter image description here

关于ios - 具有动态大小内容的 UIScrollView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59809094/

相关文章:

ios - 如何将打印项目添加到 Firebase 数据库?

Swift - 添加一个常量,接受参数并返回值

iphone - CoreData 应用程序目录在 iOS3 上崩溃

ios - MailCompositionService 意外退出 ios

ios - 访问iOS6 UIPageViewController创建的UIPageControl?

ios - 使用 NSDataDetector 就像 Apple 的 Notes 应用程序一样

ios - UIContainerView 是否指向另一个 Storyboard 中的 ViewController?

ios - RevMob 全屏广告在 iOS 上崩溃

ios - Xcode 10.2.1 和 iOS 12.3 测试版

尝试在 xcode 中退出程序时出现 C++ 11db 错误。初学者级