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

瑞克·德卡德

我正在做一个项目,我希望用户能够为同一个表单选择两种输入方法。我想出了一个包含两个自定义 UIView(以编程方式制作)的滚动视图。这是负责的视图控制器的代码:

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
    }

当它工作时,Xcode 给我一条关于自动布局的错误消息:

“ScrollView”的可滚动内容大小不明确

还有一个问题:第二个 UIView 上的内容没有居中,即使它应该是:

未居中内容的图片

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)")
}

关于如何解决这个问题的任何想法?非常感谢您提前。请放轻松,这是我关于stackoverflow的第一个问题。我对 swift 编程也很陌生。

唐麦格

为了让自己更轻松,

  • UIStackView向滚动视图添加水平
  • 设置 .distribution = .fillEqually
  • 将所有 4 边约束到滚动视图的 .contentLayoutGuide
  • 将其高度限制为滚动视图的 .frameLayoutGuide
  • 将您的自定义视图添加到堆栈视图
  • 第一个自定义视图的宽度限制为滚动视图的宽度.frameLayoutGuide

这是您的代码,使用该方法进行了修改:

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
    }
}

编辑

几个附加说明...

  1. UIScrollView 布局歧义。

正如我在最初的评论说,如果我们添加了一个UIScrollView在情节提要/覆盖整个院落uilder,但千万不要给它任何约束的内容,IB会抱怨说,它有可滚动的内容大小歧义-因为它确实我们还没有告诉 IB 内容是什么。

我们可以忽略它,也可以选择滚动视图,然后在 Size Inspector 窗格底部更改AmbiguityNever Verify

作为一般规则,您应该更正所有自动布局警告/错误,但在诸如此类的特定情况下 - 我们知道它是按照我们想要的方式设置的,并且我们将在运行时满足约束 - 它不会不去管它会很痛。

  1. UIDatePicker 没有水平居中。

它实际上居中的。如果添加此行:

datePicker.backgroundColor = .green

您会看到对象框架本身居中,但框架内的 UI 元素是左对齐的:

在此处输入图片说明

从快速研究来看,似乎无法改变。

现在,从 Apple 的文档中,我们看到:

您应该使用自动布局将日期选择器集成到您的布局中。虽然日期选择器可以调整大小,但它们应该以其内在的内容大小使用。

奇怪的是,如果我们UIDatePicker在 Storyboard 中添加 a ,将其更改Preferred StyleCompact,并给它 centerX 和 centerY 约束...... Storyboard 不相信它具有内在的内容大小。

如果我们通过代码添加它,只给它 X/Y 位置约束,它将以它的内在内容大小显示在我们想要的位置但是...如果我们跳入Debug View Hierarchy,Xcode 会告诉我们它的Position 和 size 是不明确的。

现在,还有什么更有趣的......

点击该控件并观看调试控制台填充535 行自动布局错误/警告!!!

一些快速调查——这些都是内部自动布局问题,与我们的代码或布局无关。

当 iOS 内置键盘开始显示自动完成选项时,我们会看到类似的问题。

这些可以安全地忽略。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何使用XCode的Interface Builder设计一个很长的Scrollview

Xcode / Interface Builder忽略“填充颜色”设置

Xcode 12无法使用Interface Builder将子视图添加到scrollview

使用UIViews进入UIViews

如何防止Interface Builder在子视图内容周围添加20pt填充?

使用Interface Builder中的自动布局而不是代码使UIView填充包含视图

如何以编程方式填充使用React构建的输入元素?

如何以编程方式使UICollectionView填充UITableViewCell?

使用ListAdapter填充ScrollView布局中的LinearLayout

使用NSArray中的UIButton自动填充scrollview

在Interface Builder中使用常量

仅使用Interface Builder的UIScrollView

iOS:以编程方式更改从Interface Builder设置的约束

使用Form Builder填充onInit上的输入

在Swift中以编程方式为OS X构建UI(无需Interface Builder)

如何使用Interface Builder设置UIScreenEdgePanGestureRecognizer?

如何在Interface Builder中使用RMMapView?

在Swift 3中以编程方式使用ScrollView

如何以编程方式分配UIViews自动布局边缘以匹配超级视图?

Nativescript:如何以编程方式禁用/启用ScrollView滚动?

如何以编程方式将ScrollView滚动到底部?

如何以编程方式将ScrollView滚动到底部

以编程方式定义颜色并在 Interface Builder 中使用它们

如何改组UIViews

使用 userDefaults 保存 UIViews

使矩形宽度填充ScrollView

StackView 未填充 Scrollview

ScrollView 不会填充

Swift和Interface Builder中的单例