我的代码中存在约束问题。我看到以下类型的错误:
将尝试通过打破限制来恢复〈NSLayoutConstraint:0x600000ea6440 UI视图:0x7fc684509760.height == UI滚动视图:0x7fc685818000.height(活动)〉
在UIViewAlertForUnsatisfiableConstraints处创建一个符号断点,以便在调试器中捕获此错误。〈UIKitCore/UIView. h〉中列出的UIView上的UISconstraintBasedLayoutDebugging类别中的方法也可能会有所帮助。
我认为约束是问题所在。
我遇到的另一个问题是buttonview中的按钮。它们有点偏移,有时当numN改变时,它不适合屏幕。
import UIKit
import Foundation
class PianoRollView: UIView {
var numN = 127
var numT = 4
override func draw(_ rect: CGRect) {
super.draw(rect)
// Draw the grid
let context = UIGraphicsGetCurrentContext()
context?.setStrokeColor(UIColor.black.cgColor)
context?.setLineWidth(1.0)
let noteHeight = rect.height / CGFloat(numN)
for i in 1..<numN {
let y = CGFloat(i) * noteHeight
context?.move(to: CGPoint(x: 0, y: y))
context?.addLine(to: CGPoint(x: rect.width, y: y))
}
let timeSlotWidth = rect.width / CGFloat(numT)
for i in 1..<numT {
let x = CGFloat(i) * timeSlotWidth
context?.move(to: CGPoint(x: x, y: 0))
context?.addLine(to: CGPoint(x: x, y: rect.height))
}
// Add line to right of grid
context?.move(to: CGPoint(x: rect.width, y: 0))
context?.addLine(to: CGPoint(x: rect.width, y: rect.height))
// Add line to left of grid
context?.move(to: CGPoint(x: 0, y: 0))
context?.addLine(to: CGPoint(x: 0, y: rect.height))
// Add line to top of grid
context?.move(to: CGPoint(x: 0, y: 0))
context?.addLine(to: CGPoint(x: rect.width, y: 0))
// Draw bottom line
context?.move(to: CGPoint(x: 0, y: rect.height))
context?.addLine(to: CGPoint(x: rect.width, y: rect.height))
context?.strokePath()
}
}
class ViewController: UIViewController, UIScrollViewDelegate {
let scrollView = UIScrollView()
let pianoRollView = PianoRollView()
// Create a container view to hold both the PianoRollView and the ButtonView
let containerView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
// Add the UIScrollView to the view controller's view
view.addSubview(scrollView)
// Set the container view as the content view of the scroll view
scrollView.addSubview(containerView)
// Disable the autoresizing mask for both the UIScrollView and the PianoRollView
scrollView.translatesAutoresizingMaskIntoConstraints = false
pianoRollView.translatesAutoresizingMaskIntoConstraints = false
// Set the constraints for the UIScrollView to fill the view controller's view
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
// Initialize the piano roll view
pianoRollView.frame = CGRect(x: 0, y: 0, width: 2000, height: 2000)
pianoRollView.layer.zPosition = 1
containerView.frame = pianoRollView.frame
containerView.addSubview(pianoRollView)
// Set the constraints for the PianoRollView to fill the container view
pianoRollView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
pianoRollView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
pianoRollView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
pianoRollView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
pianoRollView.widthAnchor.constraint(equalTo: containerView.widthAnchor).isActive = true
// Set the constraints for the container view to fill the scroll view
containerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
containerView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
containerView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true
// Add a UIView under the PianoRollView
let buttonView = UIView()
buttonView.translatesAutoresizingMaskIntoConstraints = false
buttonView.frame = pianoRollView.frame
containerView.addSubview(buttonView)
// Set the constraints for the ButtonView to align with the PianoRollView
buttonView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
buttonView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
buttonView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
buttonView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
buttonView.heightAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true
// calculate size of notes
let noteHeight = Int(round(pianoRollView.frame.height / CGFloat(pianoRollView.numN)) )
// Add buttons to the buttonView with the same height as the space between the grids
for i in 0..<pianoRollView.numN {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
buttonView.addSubview(button)
button.leadingAnchor.constraint(equalTo: buttonView.leadingAnchor).isActive = true
button.trailingAnchor.constraint(equalTo: buttonView.trailingAnchor).isActive = true
button.topAnchor.constraint(equalTo: buttonView.topAnchor, constant: CGFloat(i) * CGFloat(noteHeight)).isActive = true
button.bottomAnchor.constraint(equalTo: buttonView.topAnchor, constant: CGFloat(i+1) * CGFloat(noteHeight)).isActive = true
button.setTitle("Button \(i)", for: .normal)
button.setTitleColor(.black, for: .normal)
button.backgroundColor = .red
}
// Set the content size of the scroll view to match the size of the piano roll view
scrollView.contentSize = containerView.bounds.size
// Enable scrolling in both directions
scrollView.isScrollEnabled = true
scrollView.showsVerticalScrollIndicator = true
scrollView.showsHorizontalScrollIndicator = true
// Set the background color of the piano roll view to white
pianoRollView.backgroundColor = UIColor.clear
// Set the delegate of the scroll view to self
scrollView.delegate = self
// Set the minimum zoom scale
let minZoomScale = max(view.bounds.width / pianoRollView.bounds.width, view.bounds.height / pianoRollView.bounds.height)
scrollView.minimumZoomScale = minZoomScale
// scrollView.minimumZoomScale = 1
scrollView.maximumZoomScale = 5
}
// Return the container view in the viewForZooming method
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return containerView
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
// Adjust the content offset so that the content stays centered when zooming
let horizontalInset = max(0, (scrollView.bounds.width - scrollView.contentSize.width) / 2)
let verticalInset = max(0, (scrollView.bounds.height - scrollView.contentSize.height) / 2)
scrollView.contentInset = UIEdgeInsets(top: verticalInset, left: horizontalInset, bottom: verticalInset, right: horizontalInset)
}
}
1条答案
按热度按时间u5i3ibmn1#
问题的一部分是您将显式帧设置与约束混合在一起。
作为一项规则,当使用
UIScrollView
时,我们要么设置框架和.contentSize
***,要么***使用约束,让自动布局为我们完成所有工作。将代码组织到“相关”任务中会非常有帮助。例如:
在您的项目中,
scrollView
/pianoRollView
/containerView
被创建为类属性,因此viewDidLoad()
中的第一件事将是:接下来,将它们添加到视图层次结构中:
我们希望使用自动布局,因此请使用以下命令:
现在我们可以在所有这些视图上设置约束,相对于彼此...
注意,我们不在
pianoRollView
上设置heightAnchor
。您还可以将
numN
按钮添加到buttonView
......这使得在上述视图上设置约束 * 之后 * 进行操作更有意义。您的代码指示您希望pianoView(因此buttonView)的“高度”为2000。然后您希望使用
numN
按钮均匀填充该高度。在您发布的代码中,您是这样计算按钮的高度的:
因此,高度为2000,numN为127:
您正在使用
round()
,这很好,因为不使用部分点是一个好主意。如果我们使用
round()
,我们得到:但是,如果我们有127个按钮,每个按钮的高度为16...
所以我们看不到最下面的两个按钮!
我们可以尝试使用
floor()
......然后得到:但是,如果我们有127个按钮,每个按钮的高度为15...
所以我们在底部有95分的“差距”!
您可能要做的是使用
floor()
和ceil()
,然后比较这些值,看看哪个值最接近2000。这可以用更少的行来编写,但需要澄清:
noteHeight
现在等于16
,totalHeight
等于2032
......大约是2000。与其在
buttonView
上设置高度,然后循环并设置按钮的框架,不如继续利用自动布局。我们将创建约束的“垂直链”:
buttonView
的顶部buttonView
的底部该约束链现在:
您遇到的另一个问题是,您对按钮的
noteHeight
使用了不同的计算方法:对于
pianoRollView
中的水平行:因此,每个按钮的高度为
16-points
,但线条的间距为15.748031496062993
。一个更好的选择是将
noteHeight
设为pianoRollView
的一个属性......并在计算viewDidLoad()
中的noteHeight
(按钮高度)时设置它。下面是完整的内容,还有一些额外的修改。通读评论,这样你就明白了发生了什么:
如果其中任何一个不清楚--或者即使您现在已经全部理解了--您可以通过学习一系列Auto-Layout /
UIScrollView
教程来真正受益...