简体   繁体   English

Swift:如何以编程方式使用 UIViews 从 Interface Builder 填充 ScrollView

[英]Swift: How to fill a ScrollView from Interface Builder with UIViews programmatically

I am working on a project where I want the user to be able to select two methods of input for the same form.我正在开发一个项目,我希望用户能够使用 select 为同一表单提供两种输入方法。 I came up with a scrollview that contains two custom UIViews (made programmatically).我想出了一个包含两个自定义 UIView(以编程方式制作)的滚动视图。 Here is the code for the responsible view controller:这是负责视图 controller 的代码:

import UIKit

class MainVC: UIViewController, UIScrollViewDelegate {

    @IBOutlet weak var scrollView: UIScrollView!
    
    @IBOutlet weak var pageControl: UIPageControl!
    
    var customView1: CustomView1 = CustomView1()
    
    var customView2: customView2 = CustomView2()
    
    var frame = CGRect.zero
    
    func setupScrollView() {

        pageControl.numberOfPages = 2
        
        frame.origin.x = 0
        frame.size = scrollView.frame.size
        
        customView1 = customView1(frame: frame)

        self.scrollView.addSubview(customView1)
        
        frame.origin.x = scrollView.frame.size.width
        frame.size = scrollView.frame.size
        
        customView2 = CustomView2(frame: frame)
        
        self.scrollView.addSubview(customView2)
        
        self.scrollView.contentSize = CGSize(width: scrollView.frame.size.width * 2, height: scrollView.frame.size.height)
        
        self.scrollView.delegate = self
        
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
        pageControl.currentPage = Int(pageNumber)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupScrollView()
        
        scrollView.delegate = self
    }

While it works, Xcode gives me an error message for auto layout:虽然它有效,但 Xcode 给了我一条自动布局的错误消息:

Scrollable content size is ambiguous for "ScrollView" “ScrollView”的可滚动内容大小不明确

Also a problem: content on the second UIView is not centered, even though it should be:还有一个问题:第二个 UIView 上的内容没有居中,即使它应该是:

picture of the not centered content未居中内容的图片

import UIKit

class customView2: UIView {

lazy var datePicker: UIDatePicker = {
    let datePicker = UIDatePicker()
    datePicker.translatesAutoresizingMaskIntoConstraints = false
    return datePicker
  }()

//initWithFrame to init view from code
override init(frame: CGRect) {
    super.init(frame: frame)
    setupView()
}
  
//initWithCode to init view from xib or storyboard
required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setupView()
}

func setupView () {
    self.backgroundColor = .systemYellow
    
    datePicker.datePickerMode = .date
    
    datePicker.addTarget(self, action: #selector(self.datePickerValueChanged(_:)), for: .valueChanged)
    
    addSubview(datePicker)
    
    setupLayout()
}

func setupLayout() {
    
    let view = self
    
    NSLayoutConstraint.activate([
        datePicker.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        datePicker.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
        datePicker.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
        datePicker.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2)
    ])
}

@objc func datePickerValueChanged(_ sender: UIDatePicker) {
    let dateFormatter: DateFormatter = DateFormatter()
    dateFormatter.dateFormat = "dd.MM.yyyy"
    let selectedDate: String = dateFormatter.string(from: sender.date)
    print("Selected value \(selectedDate)")
}

Any ideas on how to solve this?关于如何解决这个问题的任何想法? Thank you very much in advance.非常感谢您提前。 And please go easy on me, this is my first question on stackoverflow.请 go 对我轻松一点,这是我在 stackoverflow 上的第一个问题。 I am also fairly new to programming in swift.我对 swift 中的编程也相当陌生。

To make things easier on yourself,为了让自己更轻松,

  • add a horizontal UIStackView to the scroll view将水平UIStackView添加到滚动视图
  • set .distribution =.fillEqually设置.distribution =.fillEqually
  • constrain all 4 sides to the scroll view's .contentLayoutGuide将所有 4 面约束到滚动视图的.contentLayoutGuide
  • constrain its height to the scroll view's .frameLayoutGuide将其高度限制为滚动视图的.frameLayoutGuide
  • add your custom views to the stack view将您的自定义视图添加到堆栈视图
  • constrain the width of the first custom view to the width of the scroll view's .frameLayoutGuide第一个自定义视图的宽度限制为滚动视图的.frameLayoutGuide的宽度

Here is your code, modified with that approach:这是您的代码,使用该方法进行了修改:

class MainVC: UIViewController, UIScrollViewDelegate {
    
    @IBOutlet weak var scrollView: UIScrollView!
    
    @IBOutlet weak var pageControl: UIPageControl!
    
    var customView1: CustomView1 = CustomView1()
    
    var customView2: CustomView2 = CustomView2()
    
    func setupScrollView() {
        
        pageControl.numberOfPages = 2
        
        // let's put the two custom views in a horizontal stack view
        let stack = UIStackView()
        stack.axis = .horizontal
        stack.distribution = .fillEqually
        stack.translatesAutoresizingMaskIntoConstraints = false
        
        stack.addArrangedSubview(customView1)
        stack.addArrangedSubview(customView2)
        
        // add the stack view to the scroll view
        scrollView.addSubview(stack)
        
        let contentG = scrollView.contentLayoutGuide
        let frameG = scrollView.frameLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // constrain stack view to all 4 sides of content layout guide
            stack.topAnchor.constraint(equalTo: contentG.topAnchor),
            stack.leadingAnchor.constraint(equalTo: contentG.leadingAnchor),
            stack.trailingAnchor.constraint(equalTo: contentG.trailingAnchor),
            stack.bottomAnchor.constraint(equalTo: contentG.bottomAnchor),

            // stack view Height equal to scroll view frame layout guide height
            stack.heightAnchor.constraint(equalTo: frameG.heightAnchor),
            
            // stack is set to fillEqually, so we only need to set
            //  width of first custom view equal to scroll view frame layout guide width
            customView1.widthAnchor.constraint(equalTo: frameG.widthAnchor),
            
        ])
        
        self.scrollView.delegate = self
        
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
        pageControl.currentPage = Int(pageNumber)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupScrollView()
        
        scrollView.delegate = self
    }
}

Edit编辑

Couple additional notes...几个附加说明...

  1. UIScrollView layout ambiguity. UIScrollView布局不明确。

As I said in my initial comment, if we add a UIScrollView in Storyboard / I nterface B uilder, but do NOT give it any constrained content, IB will complain that it has Scrollable Content Size Ambiguity -- because it does .正如我在最初的评论中所说,如果我们在 Storyboard / 接口B uilder 中添加UIScrollView ,但给它任何受约束的内容,IB 会抱怨它具有可滚动内容大小歧义——因为它确实如此 We haven't told IB what the content will be.我们还没有告诉 IB 内容是什么。

We can either ignore it, or select the scroll view and, at the bottom of the Size Inspector pane, change Ambiguity to Never Verify .我们可以忽略它,或者 select 滚动视图,然后在 Size Inspector 窗格的底部,将Ambiguity更改为Never Verify

As a general rule, you should correct all auto-layout warnings / errors, but in specific cases such as this - where we know that it's setup how we want, and we'll be satisfying constraints at run-time - it doesn't hurt to leave it alone.作为一般规则,您应该更正所有自动布局警告/错误,但在诸如此类的特定情况下 - 我们知道它是按照我们想要的方式设置的,并且我们将在运行时满足约束 - 它不会离开它很痛苦。

  1. UIDatePicker not being centered horizontally. UIDatePicker未水平居中。

It actually is centered.它实际上居中的。 If you add this line:如果添加此行:

datePicker.backgroundColor = .green

You'll see that the object frame itself is centered, but the UI elements inside the frame are left-aligned:您会看到 object 框架本身居中,但框架内的 UI 元素是左对齐的:

在此处输入图像描述

From quick research, it doesn't appear that can be changed.从快速研究来看,似乎无法改变。

Now, from Apple's docs , we see:现在,从 Apple 的文档中,我们看到:

You should integrate date pickers in your layout using Auto Layout.您应该使用自动布局将日期选择器集成到您的布局中。 Although date pickers can be resized, they should be used at their intrinsic content size.尽管可以调整日期选择器的大小,但它们应该以其固有的内容大小使用。

Curiously, if we add a UIDatePicker in Storyboard, change its Preferred Style to Compact , and give it centerX and centerY constraints... Storyboard doesn't believe it has an intrinsic content size.奇怪的是,如果我们在 Storyboard 中添加一个UIDatePicker ,将其Preferred Style更改为Compact ,并给它 centerX 和 centerY 约束...... Storyboard 不相信它具有内在的内容大小。

If we add it via code, giving it only X/Y position constraints, it will show up where we want it at its intrinsic content size .如果我们通过代码添加它,只给它 X/Y position 约束,它将以其固有的内容大小显示在我们想要的位置。 But... if we jump into Debug View Hierarchy , Xcode tells us its Position and size are ambiguous.但是......如果我们跳转到Debug View Hierarchy , Xcode 告诉我们它的Position 和大小是不明确的。

Now, what's even more fun...现在,更有趣的是……

Tap that control and watch the Debug console fill with 535 Lines of auto-layout errors / warnings!!!点击该控件并观看调试控制台填充535 行自动布局错误/警告!!!

Some quick investigation -- these are all internal auto-layout issues, and have nothing to do with our code or layout.一些快速调查——这些都是内部自动布局问题,与我们的代码或布局无关。

We see similar issues with the iOS built-in keyboard when it starts showing auto-complete options.当 iOS 内置键盘开始显示自动完成选项时,我们会看到类似的问题。

Those are safe to ignore.这些是可以安全忽略的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 以编程方式将由Interface Builder安排的自定义UIView替换为UILabels - Replace custom UIViews arranged by Interface Builder with UILabels programmatically 如何以编程方式填充scrollview宽度 - How to fill scrollview width programmatically Swift-UIViews以编程方式排序 - Swift - UIViews order programmatically 如何以编程方式将 ScrollView 设置到初始位置 (Swift) - How to set ScrollView to the initial position programmatically (Swift) 如何以编程方式创建的uibutton以编程方式显示弹出窗口(不使用接口构建器) - How to display a popover programmatically from a uibutton that is also created programmatically (Not using interface builder) 在 Swift 3 中以编程方式使用 ScrollView - Using ScrollView Programmatically in Swift 3 如何使用XCode的Interface Builder设计一个很长的Scrollview - How to design a very long Scrollview with XCode's Interface Builder 在没有界面构建器的情况下,使用UIViewController和UIViews切换视图的最佳方法是什么? - What is the best way to switch views with UIViewController and UIViews without interface builder? 使用Interface Builder动态调整UILabel的大小并在其下方重新定位UIViews - Resize UILabel dynamically and reposition UIViews below it using Interface Builder 您如何以编程方式使UIView成为其中的textLabel的高度? (在Swift中) - How do you programmatically make UIViews which are the height of textLabels inside them? (In Swift)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM