ios - 以编程方式创建 UIScrollView 作为另一个 UIView 的 subview 的必要步骤是什么?

标签 ios iphone swift uiview uiscrollview

TL;DR -> 创建一个扩展 UIScrollView 的类,实例化该类,然后使用 NSLayoutConstraints 将其添加为现有 UIView 的 subview 需要哪些必要步骤?

我处于需要将 UIScrollView 添加到现有 UIViewController 的 subview (不是 self.view - self.view 的 subview )的情况。我也不使用 IB,因为我想了解我的代码。我已经设法布置了每个 UI 组件,并且可以看到我的 View 层次结构已在调试器中正确组装。

不幸的是,我的 UIScrollView 不会滚动,并且由于某种原因,我的 subview 都没有在视觉上显示(请注意,我的 ScrollView 的直接 subview 也是 ScrollView ,所以我的问题可能是我对 UIScrollView 类的理解)。

enter image description here

上图显示了我的UI的大致结构,浅灰色区域是我的UIScrollView。我有一个扩展 UIScrollView 和 UIScrollDelegate 的类(如果需要后一个父类,则不是 100%)。类看起来像这样

import UIKit

class PlayWorkout: UIScrollView, UIScrollViewDelegate{

var workoutInstructionTiles : [WorkoutInstruction] = [WorkoutInstruction]();

var heightsTotal : Int = 0; //This variable will keep track of the running total for each of the workout tile subviews to help with positioning

init(workoutSubviews : [WorkoutInstruction]){

    super.init(frame : CGRect.zero);
    workoutInstructionTiles = workoutSubviews;
    appendTiles();

    self.translatesAutoresizingMaskIntoConstraints = false;

}


func appendTiles(){

    print("appending tiles \(workoutInstructionTiles.count)");
    for(var i = 0; i < workoutInstructionTiles.count; i++){

        let setCount = workoutInstructionTiles[i].getNumSets();

        workoutInstructionTiles[i].layer.cornerRadius = 5;
        workoutInstructionTiles[i].backgroundColor    = UIColor.whiteColor();
        workoutInstructionTiles[i].translatesAutoresizingMaskIntoConstraints = false;
        changeContentSize(workoutInstructionTiles[i]);
        self.addSubview(workoutInstructionTiles[i]);

        //Formula for position relative to top of the ScrollView will be 10 (margin) + 20 (label space) + setCount * 20 (content) + heightsTotal
        let verticalConstraint = NSLayoutConstraint(item: workoutInstructionTiles[i], attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: CGFloat(30  + heightsTotal));

        let leftConstraint = NSLayoutConstraint(item: workoutInstructionTiles[i], attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 10);

        let rightConstraint = NSLayoutConstraint(item: workoutInstructionTiles[i], attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 10);

        let heightConstraint = NSLayoutConstraint(item: workoutInstructionTiles[i], attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: CGFloat(setCount * 20 + 20));

        self.addConstraints([verticalConstraint, leftConstraint, rightConstraint, heightConstraint]);
        heightsTotal += setCount * 20;
    }

}



func setViewConstraints(superView : UIView) -> Void{

    let verticalConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant:30);

    let horizontalConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.CenterXWithinMargins, multiplier: 1.0, constant: 0);

    let heightConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: -60);

    let widthConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0);

    superView.addConstraints([verticalConstraint, horizontalConstraint, heightConstraint, widthConstraint]);

}

func changeContentSize(aView : UIView){

    var contentRect : CGRect = CGRect.zero;
    for view in aView.subviews {
        contentRect = CGRectUnion(contentRect, view.frame);
    }
    self.contentSize = contentRect.size;
}



required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

然后,我在单击按钮开始锻炼时实现该类。从我到目前为止能够确定的情况来看,似乎我需要为 UIScrollView 调用/设置的唯一方法是

scrollView.contentSize = CGSize(width: aWidth, height: aHeight)
scrollView.translateAutoResizingMaskIntoConstraints = false

所以,这是我的类实例化 - 这是在 UIViewController 内的 UIGestureRecognizer 事件处理程序内部,它还扩展了 UIScrollViewDelegate:

workoutConfig = PlayWorkout(workoutSubviews : workoutInstructions);
workoutConfig.delegate = self;
workoutConfig.changeContentSize(workoutConfig);
workoutConfig.translatesAutoresizingMaskIntoConstraints = false;
self.contentView.addSubview(workoutConfig);
workoutConfig.setViewConstraints(self.contentView);

我还使用了一个名为 changeContentSize() 的方法,该方法改编自发布到 SO 的 Objective-C 方法,如下所示:

func changeContentSize(aView : UIView){

    var contentRect : CGRect = CGRect.zero;
    for view in aView.subviews {
        contentRect = CGRectUnion(contentRect, view.frame);
    }
    self.contentSize = contentRect.size;
}

我知道这需要大量阅读,但我想做到透彻。我感谢任何帮助。谢谢!

最佳答案

好吧,经过 3 天的疯狂研究并走上各种荒谬的道路来实现这一目标,我终于明白了,而且实际上并不难(苹果公司的记录非常糟糕)。我将尝试分享我的发现 - 这个链接最终帮助我理解了如何做到这一点,所以我觉得有义务在到期时给予信任:

http://spin.atomicobject.com/2014/03/05/uiscrollview-autolayout-ios/#comment-541731

好的,这是使用自动布局创建可 ScrollView 的逐步步骤。

1 - 一个 UIScrollView 只能有 1 个 subview ,通常应该是一个 UIView 保存所有可滚动的内容。确保创建一个 UIView,将任何你想要滚动的内容添加到该 UIView,然后将该 UIView 添加为你的 UIScrollView 的 subview

2 - 将 ScrollView 固定到它的父 View ,无论你想要什么约束,只要确保你设置了顶部、前导、尾随和底部。这是我的代码的样子(在我的例子中,self 是一个扩展 UIScrollView 的类):

func setViewConstraints(superView : UIView) -> Void{

    let verticalConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant:60);

    let leftConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0);

    let rightConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0);

    let bottomConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Bottom, relatedBy: .Equal, toItem: superView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0);


    superView.addConstraints([verticalConstraint, leftConstraint, bottomConstraint,rightConstraint]);

}

3 - 将步骤 1 中提到的子 UIView 固定到 ScrollView ,确保设置 -> 顶部、前导、尾随和底部。这通常应该被精确固定,除非 UIView 被设计为小于 ScrollView 的尺寸。完成此步骤是为了确保设备知道放置 UIView 的位置。我的代码看起来像这样(同样,self 指的是扩展 UIScrollView 的类)

    let pinTop = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant:0);

    let pinBottom = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0);

    let pinLeft = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0);

    let pinRight = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0);


    self.addConstraints([pinTop, pinBottom, pinLeft, pinRight]);

4 - 为了让 ScrollView 了解 View 的内容大小,您必须根据 ScrollView 监督或不涉及 ScrollView 的计算来设置 UIVIEW 的高度和宽度。不要担心设置 UIView 相对于 ScrollView superview 的其他尺寸,cocoa api 推断内容大小只需要高度和宽度。我的代码看起来像这样(在这种情况下,self.viewController.contentView 指的是 ScrollView 的 super View ):

    let widthConstraint = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: self.viewController.contentView, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0);


    let heightConstraint = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: CGFloat(heightsTotal + (self.workoutInstructionTiles.count * 10)));


    self.viewController.contentView.addConstraints([heightConstraint, widthConstraint]);

如果每个步骤都正确执行,您的结果应该是一个可滚动的 View 。现在我明白了,如果我可以帮助任何人处理他们的代码,请发表评论。

关于ios - 以编程方式创建 UIScrollView 作为另一个 UIView 的 subview 的必要步骤是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34504676/

相关文章:

ios - 在文本字段中定位文本

ios - 将 JSON 数据发布到特定对象 iOS

iphone - 从txt列表文件中读取并在ios中设置许多对象

iphone - 如何在 UIBarButtonItem 上触发高亮效果

ios - 在 Swift 中的 TableView 和另一个 ViewController 之间传递数据

ios - [iOS][ReactNative v0.19.0] 自定义 View 出现意外的黑色背景

iphone - 获取 GDataFeedPhotoAlbum 返回错误的对象类型

ios - 错误 : Access to current location is currently disable. 需要位置访问权限才能连接阅读器并创建付款

json - swift json反序列化错误

java - Swift 3.0 中的 FileHandle 和 java 中的 FileInputStream 的区别