【问题标题】:Using ScrollView Programmatically in Swift 3在 Swift 3 中以编程方式使用 ScrollView
【发布时间】:2017-12-09 10:58:08
【问题描述】:

我已经搜索了其他问题,但似乎仍然在 swift 3 中使用自动布局以编程方式创建我的滚动视图时遇到了一些问题。我可以让我的滚动视图显示如下图所示,但是当我滚动到底部时其他标签不显示,“滚动顶部”标签不消失。

希望有人可以帮助检查我下面的代码!

import UIKit

class ViewController: UIViewController {

let labelOne: UILabel = {
    let label = UILabel()
    label.text = "Scroll Top"
    label.backgroundColor = .red
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

let labelTwo: UILabel = {
    let label = UILabel()
    label.text = "Scroll Bottom"
    label.backgroundColor = .green
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()


override func viewDidLoad() {
    super.viewDidLoad()

    let screensize: CGRect = UIScreen.main.bounds
    let screenWidth = screensize.width
    let screenHeight = screensize.height
    var scrollView: UIScrollView!
    scrollView = UIScrollView(frame: CGRect(x: 0, y: 120, width: screenWidth, height: screenHeight))
    scrollView.contentSize = CGSize(width: screenWidth, height: 2000)
    scrollView.addSubview(labelOne)
    scrollView.addSubview(labelTwo)


    view.addSubview(labelOne)
    view.addSubview(labelTwo)
    view.addSubview(scrollView)

    // Visual Format Constraints
    view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": labelOne]))
    view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-100-[v0]", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": labelOne]))

    // Using iOS 9 Constraints in order to place the label past the iPhone 7 view
    view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .top, relatedBy: .equal, toItem: labelOne, attribute: .bottom, multiplier: 1, constant: screenHeight + 200))
    view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .right, relatedBy: .equal, toItem: labelOne, attribute: .right, multiplier: 1, constant: 0))
    view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .left, relatedBy: .equal, toItem: labelOne, attribute: .left, multiplier: 1, constant: 0)     

    }

}

【问题讨论】:

  • 为什么要将标签同时添加到滚动视图和 UIView 中?
  • ^ 这个。您将子视图添加到视图,而不是滚动视图。您还需要在labelTwoscrollView 之间设置一些约束,以便scrollView 可以正确计算内容大小。

标签: ios swift swift3 uiscrollview autolayout


【解决方案1】:

对我来说,这项工作就像魅力

class WithDrawConfirmationViewController: UIViewController, WithDrawConfirmationViewProtocol {

    var presenter: WithDrawConfirmationPresenterProtocol?
    private let viewbackgroundColor = UIColor(hexString: "#F5F5F8")
    // MARK:- View properties
    lazy var scrollView: UIScrollView = {
        let scrollView = UIScrollView()
        //view.backgroundColor = .red
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        return scrollView
    }()
    
    lazy var contentView: UIView = {
        let view = UIView()
        //view.backgroundColor = .green
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    lazy var headerView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private lazy var titleLabel:UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = UIColor.themeBlack
        label.text = "Withdraw Confirmation"//"Loading.."
        label.textAlignment = .center
        label.font = UIFont(name: "AvenirNext-DemiBold", size: 20)
        label.numberOfLines = 0
        label.lineBreakMode = .byWordWrapping
        return label
    }()
    
    
    private lazy var descriptionLabel:UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = UIColor.themeGray
        label.text = "Please confirm your fixed deposit details before withdrawing your deposit"// "Loading.."
        label.textAlignment = .center
        label.font = UIFont(name: "AvenirNext-Medium", size: 14)
        label.numberOfLines = 0
        label.lineBreakMode = .byWordWrapping
        label.setContentHuggingPriority(1000, for: .vertical)
        return label
    }()
    
    private lazy var instructionLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = UIColor.themeGray
        label.text = "Complete fixed deposit will be withdrawn and the money will be debited to your account within 1-2 working days."//"Loading.."
        label.textAlignment = .left
        label.font = UIFont(name: "AvenirNext-Medium", size: 14)
        label.numberOfLines = 0
        label.lineBreakMode = .byWordWrapping
        return label
    }()
    
    private lazy var warningLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = UIColor.themeBlue
        label.text = "Axis bank will levy a penalty of 1% - 2% for premature withdrawal"//"Loading.."
        label.textAlignment = .left
        label.font = UIFont(name: "AvenirNext-DemiBold", size: 13)
        label.numberOfLines = 0
        label.lineBreakMode = .byWordWrapping
        return label
    }()
    
    private lazy var warningView: UIView = {
        let view = UIView()
        view.cornerradius = 5
        view.backgroundColor = UIColor(hexFromString: "#E8F4FD")
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    private lazy var fdInformationDetailView: FDPurchaseDetailView = {
        let view = FDPurchaseDetailView(isPoweredByViewVisible: false)
        //view.backgroundColor = .red
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    private lazy var footerCTAView: FooterButtonViewView = {
        let view = FooterButtonViewView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    // MARK:- Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        contentView.backgroundColor = viewbackgroundColor
        scrollView.backgroundColor = viewbackgroundColor
        setUpUI()
        presenter?.viewDidLoadTriggered()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    }
    
    private func setUpUI() {
        view.addSubview(scrollView)
        scrollView.addSubview(contentView)
        view.backgroundColor = viewbackgroundColor
        addFooter()
        setUpHeaderView()
        setUpFDDetailView()
        setUpInformationAndWarningView()
        //scrollView.backgroundColor = .lightGray
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: footerCTAView.topAnchor).isActive = true
        scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 100, right: 0)
        
        contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
        contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
        contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
        contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
        //contentView.bottomAnchor.constraint(equalTo: footerCTAView.topAnchor).isActive = true
    }
    
    private func setUpHeaderView() {
        contentView.addSubview(headerView)
        headerView.addSubviews([titleLabel, descriptionLabel])
        
        headerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
        headerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
        headerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4).isActive = true
       
        titleLabel.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 8).isActive = true
        titleLabel.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
        titleLabel.topAnchor.constraint(equalTo: headerView.topAnchor, constant: 8).isActive = true
        
        descriptionLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant:  20).isActive = true
        descriptionLabel.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 8).isActive = true
        descriptionLabel.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
        descriptionLabel.bottomAnchor.constraint(equalTo: headerView.bottomAnchor, constant: -20).isActive = true
    }
    
    
    private func setUpFDDetailView() {
        contentView.addSubview(fdInformationDetailView)
        fdInformationDetailView.topAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true
        fdInformationDetailView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
        fdInformationDetailView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
       
    }
    
    private func setUpInformationAndWarningView() {
        contentView.addSubview(instructionLabel)
        
        instructionLabel.topAnchor.constraint(equalTo: fdInformationDetailView.bottomAnchor, constant: 20).isActive = true
        instructionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
        instructionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
        
        warningView.addSubview(warningLabel)
        warningLabel.topAnchor.constraint(equalTo: warningView.topAnchor, constant: 8).isActive = true
        warningLabel.leadingAnchor.constraint(equalTo: warningView.leadingAnchor, constant: 20).isActive = true
        warningLabel.trailingAnchor.constraint(equalTo: warningView.trailingAnchor, constant: -20).isActive = true
        warningLabel.bottomAnchor.constraint(equalTo: warningView.bottomAnchor, constant: -8).isActive = true
        
        contentView.addSubview(warningView)
        
        warningView.topAnchor.constraint(equalTo: instructionLabel.bottomAnchor, constant: 8).isActive = true
        warningView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
        warningView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
       //warningView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        warningView.bottomAnchor.constraint(greaterThanOrEqualTo: contentView.bottomAnchor, constant: -20).isActive = true
        
    }
    
    private func addFooter() {
        view.addSubview(footerCTAView)
        footerCTAView.delegate = self
        footerCTAView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        footerCTAView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        footerCTAView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
    }
}

【讨论】:

    【解决方案2】:

    将此控制器复制并粘贴到您的项目中

    class BaseScrollViewController: UIViewController {
    
    lazy var contentViewSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 100)
    lazy var scrollView: UIScrollView = {
        let view = UIScrollView(frame: .zero)
        view.backgroundColor = .white
        view.frame = self.view.bounds
        view.contentSize = contentViewSize
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    lazy var containerView: UIView = {
        let v = UIView()
        v.backgroundColor = .white
        v.frame.size = contentViewSize
        return v
    }()
    
    override func viewDidLoad() {
        view.backgroundColor = .white
        view.addSubview(scrollView)
        scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    
        scrollView.addSubview(containerView)
        setupContainer(containerView)
        super.viewDidLoad()
        
    }
    
    public func setupContainer(_ container: UIView) {
        
    }
    

    }

    以上代码的用法:

    class ClientViewController: BaseScrollViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // do your stuff here
    }
    
    override func setupContainer(_ container: UIView) {
        // add views here
    }
    

    }

    【讨论】:

      【解决方案3】:

      这些答案不适用于导航栏中的大标题。确保您的视图控制器的 viewDidLoad() 方法中有以下代码:

      self.navigationController?.navigationBar.prefersLargeTitles = false
      

      【讨论】:

        【解决方案4】:

        使用约束来定义滚动内容大小很容易——因此您不必进行任何手动计算。

        记住:

        1. 滚动视图的内容元素必须具有左/上/宽/高值。对于标签等对象,它们具有固有大小,因此您只需定义左侧和顶部。
        2. 滚动视图的内容元素定义了可滚动区域的边界 - contentSize - 但他们这样做是用底部和右侧约束。
        3. 结合这两个概念,您会发现您需要一个“连续链”,其中至少有一个元素定义上/左/下/右范围。

        这是一个简单的示例,它将直接在 Playground 页面中运行:

        import UIKit
        import PlaygroundSupport
        
        class TestViewController : UIViewController {
        
            let labelOne: UILabel = {
                let label = UILabel()
                label.text = "Scroll Top"
                label.backgroundColor = .red
                label.translatesAutoresizingMaskIntoConstraints = false
                return label
            }()
        
            let labelTwo: UILabel = {
                let label = UILabel()
                label.text = "Scroll Bottom"
                label.backgroundColor = .green
                label.translatesAutoresizingMaskIntoConstraints = false
                return label
            }()
        
            let scrollView: UIScrollView = {
                let v = UIScrollView()
                v.translatesAutoresizingMaskIntoConstraints = false
                v.backgroundColor = .cyan
                return v
            }()
        
        
            override func viewDidLoad() {
                super.viewDidLoad()
        
                // add the scroll view to self.view
                self.view.addSubview(scrollView)
        
                // constrain the scroll view to 8-pts on each side
                scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8.0).isActive = true
                scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8.0).isActive = true
                scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -8.0).isActive = true
                scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8.0).isActive = true
        
                // add labelOne to the scroll view
                scrollView.addSubview(labelOne)
        
                // constrain labelOne to left & top with 16-pts padding
                // this also defines the left & top of the scroll content
                labelOne.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 16.0).isActive = true
                labelOne.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 16.0).isActive = true
        
                // add labelTwo to the scroll view
                scrollView.addSubview(labelTwo)
        
                // constrain labelTwo at 400-pts from the left
                labelTwo.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 400.0).isActive = true
        
                // constrain labelTwo at 1000-pts from the top
                labelTwo.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 1000).isActive = true
        
                // constrain labelTwo to right & bottom with 16-pts padding
                labelTwo.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: -16.0).isActive = true
                labelTwo.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -16.0).isActive = true
        
            }
        
        }
        
        
        let vc = TestViewController()
        vc.view.backgroundColor = .yellow
        PlaygroundPage.current.liveView = vc
        

        【讨论】:

        • 你只是用故事板中的这个 liveView 让我大吃一惊。可以滚动吗?
        • 我试图实现同样的目标,但坚持你的第二点。能详细点吗?
        • 我想我已经明白了。实际上,您的答案是我在互联网上任何地方都能找到的唯一相关答案。还有许多 YouTube 视频,但您提供的帮助方式非常棒!谢谢@DonMag
        • 我建议不要使用等于 view 的约束,而是使用 self.view.safeAreaLayoutGuide。
        【解决方案5】:

        将滚动视图图像设置为壁纸:

        @IBOutlet var scroll_view_img: UIScrollView!
        
        var itemPhotoList = NSMutableArray()
        
        var button = NSMutableArray()    
        
        @IBOutlet var imageview_big: UIImageView!
        
        override func viewDidLoad() {
        
            super.viewDidLoad()
            itemPhotoList = ["grief-and-loss copy.jpg","aaa.jpg","image_4.jpeg"]        
        
            // button = ["btn1","btn2"]
        
            let width:CGFloat = 100
            let height:CGFloat = 100
            var xposition:CGFloat = 10
            var scroll_contont:CGFloat = 0
        
            for i in 0 ..< itemPhotoList.count
            {
                var button_img = UIButton()
                button_img = UIButton(frame: CGRect(x: xposition, y: 50, width: width, height: height))
                let img = UIImage(named:itemPhotoList[i] as! String)
                button_img.setImage(img, for: .normal)
                scroll_view_img.addSubview(button_img)
                button_img.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
                button_img.tag = i
                view.addSubview(scroll_view_img)
                xposition += width+10
                scroll_contont += width
                scroll_view_img.contentSize = CGSize(width: scroll_contont, height: height)
            }
        }
        
        func buttonAction(sender: UIButton!)
        {
            switch sender.tag {
            case 0:
                imageview_big.image = UIImage(named: "grief-and-loss copy.jpg")
            case 1:
                imageview_big.image = UIImage(named: "aaa.jpg")
            case 2:
                imageview_big.image = UIImage(named: "image_4.jpeg")
            default:
                break
            }
        }
        

        【讨论】:

          【解决方案6】:

          两件事。

          1.将标签添加到滚动视图,而不是您的视图

          您希望您的标签随着滚动视图滚动,那么您不应该将它添加到您的视图中。运行代码时,您可以滚动,但固定标签固定在您的视图上,而不是在滚动视图上

          2。确保您正确添加了约束

          在你的故事板上试一试,看看什么样的约束组合对于一个视图来说是足够的。一个标签至少需要 4 个约束。

          底线

          这是您的代码的修改版本。对于约束,我添加了向左填充、顶部填充、宽度和高度,并且它可以工作。我的代码是

          let labelOne: UILabel = {
              let label = UILabel()
              label.text = "Scroll Top"
              label.backgroundColor = .red
              label.translatesAutoresizingMaskIntoConstraints = false
              return label
          }()
          
          let labelTwo: UILabel = {
              let label = UILabel()
              label.text = "Scroll Bottom"
              label.backgroundColor = .green
              label.translatesAutoresizingMaskIntoConstraints = false
              return label
          }()
          
          
          override func viewDidLoad() {
              super.viewDidLoad()
          
              let screensize: CGRect = UIScreen.main.bounds
              let screenWidth = screensize.width
              let screenHeight = screensize.height
              var scrollView: UIScrollView!
              scrollView = UIScrollView(frame: CGRect(x: 0, y: 120, width: screenWidth, height: screenHeight))
          
              scrollView.addSubview(labelTwo)
          
              NSLayoutConstraint(item: labelTwo, attribute: .leading, relatedBy: .equal, toItem: scrollView, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
              NSLayoutConstraint(item: labelTwo, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 200).isActive = true
              NSLayoutConstraint(item: labelTwo, attribute: .top, relatedBy: .equal, toItem: scrollView, attribute: .topMargin, multiplier: 1, constant: 10).isActive = true
              NSLayoutConstraint(item: labelTwo, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30).isActive = true
          
              scrollView.contentSize = CGSize(width: screenWidth, height: 2000)
              view.addSubview(scrollView)
          
          }
          

          滚动视图看起来像这样

          【讨论】:

          • "1. 将标签添加到滚动视图,而不是您的视图",这不是必需的
          • @J.Doe 请通读 OP 遇到的问题。他抱怨 label1 没有随着滚动视图滚动并且第二个标签没有出现。这是通过向基本视图控制器的视图添加两个标签直接引起的,而不是通过滚动视图使它们可滚动。我不知道你说的不需要这个是什么意思。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-11
          • 2018-01-04
          • 1970-01-01
          • 2022-10-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多