如何实时排列 UIStackView 中的元素?
how to arrange elements in UIStackView in realtime?
我有一个水平轴的 stackView,里面有 UIView,我从中创建了一个图表,
红色的条是 UIViews,灰色背景是 stackView(在代码中称为 mainStackView),现在我想实时移动那个条,我正在尝试制作一个排序可视化器,但我不知道如何我要那样做吗
在 UIKit 中(在 live playground 中一切都是编程的),这里是主文件的源代码
主文件
if (arrayToSort[j].frame.size.height > arrayToSort[j+1].frame.size.height){
// swap
var temp:UIView!
temp = arrayToSort[j]
arrayToSort[j] = arrayToSort[j+1]
arrayToSort[j+1] = temp
emptyStackView()
fillStackView(sortdArr: arrayToSort)
// delay here
}
这是 HomeViewController 的源代码
import UIKit
public class HomeViewController:UIViewController{
let stackView:UIStackView = {
let st = UIStackView()
st.axis = .horizontal
st.alignment = .center
st.distribution = .fill
st.layer.shadowColor = UIColor.gray.cgColor
st.layer.shadowOffset = .zero
st.layer.shadowRadius = 5
st.layer.shadowOpacity = 1
st.spacing = 10
st.translatesAutoresizingMaskIntoConstraints = false
return st
}()
let generateButton:UIButton = {
let btn = UIButton()
btn.setTitle("Generate Array", for: .normal)
btn.backgroundColor = UIColor(red: 0.92, green: 0.30, blue: 0.29, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let BubbleSort:UIButton = {
let btn = UIButton()
btn.setTitle("BubbleSort", for: .normal)
btn.backgroundColor = UIColor(red: 0.41, green: 0.43, blue: 0.88, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let MergeSort:UIButton = {
let btn = UIButton()
btn.setTitle("MergeSort", for: .normal)
btn.backgroundColor = UIColor(red: 0.10, green: 0.16, blue: 0.34, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let InsertionSort:UIButton = {
let btn = UIButton()
btn.setTitle("InsertionSort", for: .normal)
btn.backgroundColor = UIColor(red: 0.19, green: 0.22, blue: 0.32, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let SelectionSort:UIButton = {
let btn = UIButton()
btn.setTitle("SelectionSort", for: .normal)
btn.backgroundColor = UIColor(red: 0.51, green: 0.20, blue: 0.44, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let mainStackView:UIStackView = {
let st = UIStackView()
st.backgroundColor = .gray
st.axis = .horizontal
st.distribution = .fillEqually
st.alignment = .bottom
st.spacing = 1
st.translatesAutoresizingMaskIntoConstraints = false
return st
}()
let baseView:UIView = {
let vw = UIView()
vw.backgroundColor = UIColor(red: 0.07, green: 0.54, blue: 0.65, alpha: 1.00)
vw.translatesAutoresizingMaskIntoConstraints = false
vw.layer.cornerRadius = 3
vw.layer.masksToBounds = true
vw.heightAnchor.constraint(equalToConstant: 15).isActive = true
return vw
}()
public override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(stackView)
view.addSubview(mainStackView)
view.addSubview(baseView)
edgesForExtendedLayout = []
stackView.addArrangedSubview(generateButton)
stackView.addArrangedSubview(BubbleSort)
stackView.addArrangedSubview(MergeSort)
stackView.addArrangedSubview(InsertionSort)
stackView.addArrangedSubview(SelectionSort)
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
stackView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 50).isActive = true
baseView.bottomAnchor.constraint(equalTo: view.bottomAnchor,constant: -2).isActive = true
baseView.leftAnchor.constraint(equalTo: view.leftAnchor,constant: 5).isActive = true
baseView.rightAnchor.constraint(equalTo: view.rightAnchor,constant: -5).isActive = true
mainStackView.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 5).isActive = true
mainStackView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 5).isActive = true
mainStackView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -5).isActive = true
mainStackView.bottomAnchor.constraint(equalTo: baseView.topAnchor, constant: -5).isActive = true
setButtons()
buildRandomArray()
}
// MARK:- Actions
func setButtons(){
generateButton.addTarget(self, action: #selector(generatePressed), for: .touchUpInside)
BubbleSort.addTarget(self, action: #selector(bubbleSort), for: .touchUpInside)
MergeSort.addTarget(self, action: #selector(mergeSort), for: .touchUpInside)
InsertionSort.addTarget(self, action: #selector(insertionSort), for: .touchUpInside)
SelectionSort.addTarget(self, action: #selector(selectionSort), for: .touchUpInside)
}
func buildRandomArray(){
var randomNumber :CGFloat!
for _ in 1..<41{
let viewStick:UIView = {
let v = UIView()
v.backgroundColor = .red
v.translatesAutoresizingMaskIntoConstraints = false
randomNumber = CGFloat(Int.random(in: 160...600))
v.heightAnchor.constraint(equalToConstant: randomNumber).isActive = true
v.frame.size.height = randomNumber
return v
}()
mainStackView.addArrangedSubview(viewStick)
}
}
@objc func generatePressed(){
emptyStackView()
buildRandomArray()
print("Generating Array.....")
}
@objc func bubbleSort(){
let n = mainStackView.arrangedSubviews.count
var arrayToSort = mainStackView.arrangedSubviews
for i in 0..<n-1{
for j in 0..<n-i-1 {
if (arrayToSort[j].frame.size.height > arrayToSort[j+1].frame.size.height){
// swap
var temp:UIView!
temp = arrayToSort[j]
arrayToSort[j] = arrayToSort[j+1]
arrayToSort[j+1] = temp
self.emptyStackView()
self.fillStackView(sortdArr: arrayToSort)
// delay here
}
}
}
print("array sorted")
}
@objc func mergeSort(){
print("Merge Sort.....")
}
@objc func insertionSort(){
print("insertion Sort.....")
}
@objc func selectionSort(){
print("selection Sort.....")
}
func emptyStackView(){
for element in mainStackView.arrangedSubviews {
mainStackView.removeArrangedSubview(element)
}
}
func fillStackView(sortdArr:[UIView]){
for vw in sortdArr {
mainStackView.addArrangedSubview(vw)
}
}
}
要循环更新 UI,您必须给 UIKit 一个机会 运行。
例如,这个:
for i in 1...200 {
someView.frame.origin.x = CGFloat(i)
}
将NOT向右“滑动视图”——这将导致视图从x: 1
“跳转”到x: 200
。
因此,在您尝试可视化排序过程的循环中,屏幕上的任何内容都不会更新,直到您完成循环。
要让每次都发生一些事情,您可以使用计时器。
对于前面的示例,您可以这样做:
var i = 1
Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { timer in
someView.frame.origin.x = CGFloat(i)
i += 1
if i > 200 {
timer.invalidate()
}
}
现在视图将在屏幕上“缓慢滑动”。
因此,您需要重构带有计时器的排序代码。
我对您的代码进行了一些编辑,让您看到完成此任务的一种方法。试一试(它会在操场上 运行),并检查代码和 //comments。直到 buildRandomArray()
函数才开始更改:
编辑 - 我对这段代码做了一些修改...
- 有一个“样本值”数组,可以更容易地重新运行
- 添加了“重置”按钮以重新运行 原始值
- 使用示例值时,条形图现在是标签,可以更清楚地表明条形图正在“移动”
- 实现了插入排序
值得注意:当 运行 在操场上使用此功能时,在排序处于活动状态时点击按钮不是很灵敏。使用 iPad 模拟器或设备进行大量投注。
public class SortViewController:UIViewController{
let stackView:UIStackView = {
let st = UIStackView()
st.axis = .horizontal
st.alignment = .center
st.distribution = .fillEqually
st.layer.shadowColor = UIColor.gray.cgColor
st.layer.shadowOffset = .zero
st.layer.shadowRadius = 5
st.layer.shadowOpacity = 1
st.spacing = 10
st.translatesAutoresizingMaskIntoConstraints = false
return st
}()
let generateButton:UIButton = {
let btn = UIButton()
btn.setTitle("Generate", for: .normal)
btn.backgroundColor = UIColor(red: 0.92, green: 0.30, blue: 0.29, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let resetButton:UIButton = {
let btn = UIButton()
btn.setTitle("Reset", for: .normal)
btn.backgroundColor = UIColor(red: 0.25, green: 0.75, blue: 0.29, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let BubbleSort:UIButton = {
let btn = UIButton()
btn.setTitle("Bubble", for: .normal)
btn.backgroundColor = UIColor(red: 0.41, green: 0.43, blue: 0.88, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let MergeSort:UIButton = {
let btn = UIButton()
btn.setTitle("Merge", for: .normal)
btn.backgroundColor = UIColor(red: 0.10, green: 0.16, blue: 0.34, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let InsertionSort:UIButton = {
let btn = UIButton()
btn.setTitle("Insertion", for: .normal)
btn.backgroundColor = UIColor(red: 0.19, green: 0.22, blue: 0.32, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let SelectionSort:UIButton = {
let btn = UIButton()
btn.setTitle("Selection", for: .normal)
btn.backgroundColor = UIColor(red: 0.51, green: 0.20, blue: 0.44, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let mainStackView:UIStackView = {
let st = UIStackView()
st.backgroundColor = .gray
st.axis = .horizontal
st.distribution = .fillEqually
st.alignment = .bottom
st.spacing = 1
st.translatesAutoresizingMaskIntoConstraints = false
return st
}()
let baseView:UIView = {
let vw = UIView()
vw.backgroundColor = UIColor(red: 0.07, green: 0.54, blue: 0.65, alpha: 1.00)
vw.translatesAutoresizingMaskIntoConstraints = false
vw.layer.cornerRadius = 3
vw.layer.masksToBounds = true
vw.heightAnchor.constraint(equalToConstant: 15).isActive = true
return vw
}()
public override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(stackView)
view.addSubview(mainStackView)
view.addSubview(baseView)
edgesForExtendedLayout = []
stackView.addArrangedSubview(generateButton)
stackView.addArrangedSubview(resetButton)
stackView.addArrangedSubview(BubbleSort)
stackView.addArrangedSubview(MergeSort)
stackView.addArrangedSubview(InsertionSort)
stackView.addArrangedSubview(SelectionSort)
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
stackView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 50).isActive = true
baseView.bottomAnchor.constraint(equalTo: view.bottomAnchor,constant: -2).isActive = true
baseView.leftAnchor.constraint(equalTo: view.leftAnchor,constant: 5).isActive = true
baseView.rightAnchor.constraint(equalTo: view.rightAnchor,constant: -5).isActive = true
mainStackView.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 5).isActive = true
mainStackView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 5).isActive = true
mainStackView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -5).isActive = true
mainStackView.bottomAnchor.constraint(equalTo: baseView.topAnchor, constant: -5).isActive = true
setButtons()
buildRandomArray()
}
// MARK:- Actions
func setButtons(){
generateButton.addTarget(self, action: #selector(generatePressed), for: .touchUpInside)
resetButton.addTarget(self, action: #selector(resetPressed), for: .touchUpInside)
BubbleSort.addTarget(self, action: #selector(bubbleSort), for: .touchUpInside)
MergeSort.addTarget(self, action: #selector(mergeSort), for: .touchUpInside)
InsertionSort.addTarget(self, action: #selector(insertionSort), for: .touchUpInside)
SelectionSort.addTarget(self, action: #selector(selectionSort), for: .touchUpInside)
}
let sampleValues: [Int] = [
150, 175, 200, 225, 250, 275, 300, 325, 350, 375, 400, 425, 450, 475, 500
]
var currentArray: [UIView] = []
let barColor: UIColor = .cyan
let barHighlight: UIColor = .yellow
let timerInterval: TimeInterval = 0.10
func buildRandomArray(){
// we can populate the stack view much quicker by generating an array of views
// and then looping through that array to add them as arrangedSubviews
currentArray = []
// for ease during dev, use a set of predefined values
if true {
let hh = sampleValues.shuffled()
hh.forEach { h in
let viewStick: UILabel = {
let v = UILabel()
v.backgroundColor = barColor
v.text = "\(h)"
v.textAlignment = .center
v.heightAnchor.constraint(equalToConstant: CGFloat(h)).isActive = true
return v
}()
currentArray.append(viewStick)
}
} else {
var randomNumber :CGFloat!
for _ in 1..<41{
let viewStick:UIView = {
let v = UIView()
v.backgroundColor = barColor
v.translatesAutoresizingMaskIntoConstraints = false
randomNumber = CGFloat(Int.random(in: 160...500))
v.heightAnchor.constraint(equalToConstant: randomNumber).isActive = true
return v
}()
currentArray.append(viewStick)
}
}
fillStackView(sortdArr: currentArray)
}
@objc func generatePressed(){
// stop the timer if a sort is currently running
timer?.invalidate()
print("Generating Array.....")
emptyStackView()
buildRandomArray()
}
@objc func resetPressed(){
// stop the timer if a sort is currently running
timer?.invalidate()
print("Resetting.....")
fillStackView(sortdArr: currentArray)
}
var timer: Timer?
@objc func bubbleSort(){
print("Bubble Sort....")
// if a sort is running, stop it and reset the bars
if let t = timer, t.isValid {
resetPressed()
}
var j: Int = 0
var didSwap: Bool = false
var lastBarToCheck: Int = mainStackView.arrangedSubviews.count - 1
// set current bar to first bar
var curBar = mainStackView.arrangedSubviews[0]
// set new current bar background to barHighlight
curBar.backgroundColor = barHighlight
timer = Timer.scheduledTimer(withTimeInterval: timerInterval, repeats: true) { timer in
// if we have more bars to check
if j < lastBarToCheck {
if self.mainStackView.arrangedSubviews[j].frame.height > self.mainStackView.arrangedSubviews[j+1].frame.height {
// next bar is shorter than current bar, so
// swap the bar positions
self.mainStackView.insertArrangedSubview(curBar, at: j + 1)
// set the didSwap flag
didSwap = true
} else {
// next bar is taller
// set current bar background back to barColor
curBar.backgroundColor = self.barColor
// set current bar to next bar
curBar = self.mainStackView.arrangedSubviews[j+1]
// set new current bar background to barHighlight
curBar.backgroundColor = self.barHighlight
}
j += 1
} else {
if !didSwap {
// no bars were swapped, so
// set current bar back to barColor
curBar.backgroundColor = self.barColor
// stop the looping
timer.invalidate()
print("Done!")
} else {
// at least one swap occurred, so
// decrement number of bars to check
lastBarToCheck -= 1
// reset index
j = 0
// set current bar back to barColor
curBar.backgroundColor = self.barColor
// set current bar background to first bar
curBar = self.mainStackView.arrangedSubviews[j]
// set new current bar background to barHighlight
curBar.backgroundColor = self.barHighlight
// reset swap flag
didSwap = false
}
}
}
}
@objc func mergeSort(){
print("Merge Sort.....")
}
@objc func insertionSort(){
print("Insertion Sort.....")
// if a sort is running, stop it and reset the bars
if let t = timer, t.isValid {
resetPressed()
}
var index: Int = 1
var position: Int = 1
// set current bar to index bar
var curBar = mainStackView.arrangedSubviews[index]
// set new current bar background to barHighlight
curBar.backgroundColor = barHighlight
timer = Timer.scheduledTimer(withTimeInterval: timerInterval, repeats: true) { timer in
// if we have more bars to check
if index < self.mainStackView.arrangedSubviews.count {
// if we're not at the left-most bar
if position > 0 {
// if bar-to-the-left is taller than current bar
if self.mainStackView.arrangedSubviews[position - 1].frame.height > curBar.frame.height {
// move current bar one position to the left
self.mainStackView.insertArrangedSubview(curBar, at: position - 1)
position -= 1
} else {
// bar-to-the-left is shorter than current bar
index += 1
position = index
// set current bar background back to barColor
curBar.backgroundColor = self.barColor
// if we're not finished
if index < self.mainStackView.arrangedSubviews.count {
// set current bar to next bar
curBar = self.mainStackView.arrangedSubviews[index]
// set new current bar background to barHighlight
curBar.backgroundColor = self.barHighlight
}
}
} else {
// we're at the left-most bar
// increment index
index += 1
position = index
// set current bar background back to barColor
curBar.backgroundColor = self.barColor
// if we have more bars to check
if index < self.mainStackView.arrangedSubviews.count {
// set current bar to next bar
curBar = self.mainStackView.arrangedSubviews[index]
// set new current bar background to barHighlight
curBar.backgroundColor = self.barHighlight
}
}
} else {
// we've reached the end of the array
// stop the looping
timer.invalidate()
print("Done!")
}
}
}
@objc func selectionSort(){
print("selection Sort.....")
}
func emptyStackView(){
mainStackView.arrangedSubviews.forEach {
[=12=].removeFromSuperview()
}
}
func fillStackView(sortdArr:[UIView]){
sortdArr.forEach { vw in
vw.backgroundColor = self.barColor
mainStackView.addArrangedSubview(vw)
}
}
}
有关堆栈视图及其 arrangedSubviews
的提示...您无需清除并重新填充堆栈视图即可重新排列视图。
例如,如果我向堆栈视图添加 10 个标签:
// add 10 arranged subviews
for i in 0..<10 {
let v = UILabel()
v.text = "\(i)"
v.textAlignment = .center
v.backgroundColor = .green
stackView.addArrangedSubview(v)
}
它看起来像这样:
如果我想交换第 4 和第 5 个视图,我可以这样做:
// (arrays are zero-based)
stackView.insertArrangedSubview(stackView.arrangedSubviews[3], at: 4)
我得到:
我有一个水平轴的 stackView,里面有 UIView,我从中创建了一个图表,
红色的条是 UIViews,灰色背景是 stackView(在代码中称为 mainStackView),现在我想实时移动那个条,我正在尝试制作一个排序可视化器,但我不知道如何我要那样做吗 在 UIKit 中(在 live playground 中一切都是编程的),这里是主文件的源代码 主文件
if (arrayToSort[j].frame.size.height > arrayToSort[j+1].frame.size.height){
// swap
var temp:UIView!
temp = arrayToSort[j]
arrayToSort[j] = arrayToSort[j+1]
arrayToSort[j+1] = temp
emptyStackView()
fillStackView(sortdArr: arrayToSort)
// delay here
}
这是 HomeViewController 的源代码
import UIKit
public class HomeViewController:UIViewController{
let stackView:UIStackView = {
let st = UIStackView()
st.axis = .horizontal
st.alignment = .center
st.distribution = .fill
st.layer.shadowColor = UIColor.gray.cgColor
st.layer.shadowOffset = .zero
st.layer.shadowRadius = 5
st.layer.shadowOpacity = 1
st.spacing = 10
st.translatesAutoresizingMaskIntoConstraints = false
return st
}()
let generateButton:UIButton = {
let btn = UIButton()
btn.setTitle("Generate Array", for: .normal)
btn.backgroundColor = UIColor(red: 0.92, green: 0.30, blue: 0.29, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let BubbleSort:UIButton = {
let btn = UIButton()
btn.setTitle("BubbleSort", for: .normal)
btn.backgroundColor = UIColor(red: 0.41, green: 0.43, blue: 0.88, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let MergeSort:UIButton = {
let btn = UIButton()
btn.setTitle("MergeSort", for: .normal)
btn.backgroundColor = UIColor(red: 0.10, green: 0.16, blue: 0.34, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let InsertionSort:UIButton = {
let btn = UIButton()
btn.setTitle("InsertionSort", for: .normal)
btn.backgroundColor = UIColor(red: 0.19, green: 0.22, blue: 0.32, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let SelectionSort:UIButton = {
let btn = UIButton()
btn.setTitle("SelectionSort", for: .normal)
btn.backgroundColor = UIColor(red: 0.51, green: 0.20, blue: 0.44, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let mainStackView:UIStackView = {
let st = UIStackView()
st.backgroundColor = .gray
st.axis = .horizontal
st.distribution = .fillEqually
st.alignment = .bottom
st.spacing = 1
st.translatesAutoresizingMaskIntoConstraints = false
return st
}()
let baseView:UIView = {
let vw = UIView()
vw.backgroundColor = UIColor(red: 0.07, green: 0.54, blue: 0.65, alpha: 1.00)
vw.translatesAutoresizingMaskIntoConstraints = false
vw.layer.cornerRadius = 3
vw.layer.masksToBounds = true
vw.heightAnchor.constraint(equalToConstant: 15).isActive = true
return vw
}()
public override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(stackView)
view.addSubview(mainStackView)
view.addSubview(baseView)
edgesForExtendedLayout = []
stackView.addArrangedSubview(generateButton)
stackView.addArrangedSubview(BubbleSort)
stackView.addArrangedSubview(MergeSort)
stackView.addArrangedSubview(InsertionSort)
stackView.addArrangedSubview(SelectionSort)
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
stackView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 50).isActive = true
baseView.bottomAnchor.constraint(equalTo: view.bottomAnchor,constant: -2).isActive = true
baseView.leftAnchor.constraint(equalTo: view.leftAnchor,constant: 5).isActive = true
baseView.rightAnchor.constraint(equalTo: view.rightAnchor,constant: -5).isActive = true
mainStackView.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 5).isActive = true
mainStackView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 5).isActive = true
mainStackView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -5).isActive = true
mainStackView.bottomAnchor.constraint(equalTo: baseView.topAnchor, constant: -5).isActive = true
setButtons()
buildRandomArray()
}
// MARK:- Actions
func setButtons(){
generateButton.addTarget(self, action: #selector(generatePressed), for: .touchUpInside)
BubbleSort.addTarget(self, action: #selector(bubbleSort), for: .touchUpInside)
MergeSort.addTarget(self, action: #selector(mergeSort), for: .touchUpInside)
InsertionSort.addTarget(self, action: #selector(insertionSort), for: .touchUpInside)
SelectionSort.addTarget(self, action: #selector(selectionSort), for: .touchUpInside)
}
func buildRandomArray(){
var randomNumber :CGFloat!
for _ in 1..<41{
let viewStick:UIView = {
let v = UIView()
v.backgroundColor = .red
v.translatesAutoresizingMaskIntoConstraints = false
randomNumber = CGFloat(Int.random(in: 160...600))
v.heightAnchor.constraint(equalToConstant: randomNumber).isActive = true
v.frame.size.height = randomNumber
return v
}()
mainStackView.addArrangedSubview(viewStick)
}
}
@objc func generatePressed(){
emptyStackView()
buildRandomArray()
print("Generating Array.....")
}
@objc func bubbleSort(){
let n = mainStackView.arrangedSubviews.count
var arrayToSort = mainStackView.arrangedSubviews
for i in 0..<n-1{
for j in 0..<n-i-1 {
if (arrayToSort[j].frame.size.height > arrayToSort[j+1].frame.size.height){
// swap
var temp:UIView!
temp = arrayToSort[j]
arrayToSort[j] = arrayToSort[j+1]
arrayToSort[j+1] = temp
self.emptyStackView()
self.fillStackView(sortdArr: arrayToSort)
// delay here
}
}
}
print("array sorted")
}
@objc func mergeSort(){
print("Merge Sort.....")
}
@objc func insertionSort(){
print("insertion Sort.....")
}
@objc func selectionSort(){
print("selection Sort.....")
}
func emptyStackView(){
for element in mainStackView.arrangedSubviews {
mainStackView.removeArrangedSubview(element)
}
}
func fillStackView(sortdArr:[UIView]){
for vw in sortdArr {
mainStackView.addArrangedSubview(vw)
}
}
}
要循环更新 UI,您必须给 UIKit 一个机会 运行。
例如,这个:
for i in 1...200 {
someView.frame.origin.x = CGFloat(i)
}
将NOT向右“滑动视图”——这将导致视图从x: 1
“跳转”到x: 200
。
因此,在您尝试可视化排序过程的循环中,屏幕上的任何内容都不会更新,直到您完成循环。
要让每次都发生一些事情,您可以使用计时器。
对于前面的示例,您可以这样做:
var i = 1
Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { timer in
someView.frame.origin.x = CGFloat(i)
i += 1
if i > 200 {
timer.invalidate()
}
}
现在视图将在屏幕上“缓慢滑动”。
因此,您需要重构带有计时器的排序代码。
我对您的代码进行了一些编辑,让您看到完成此任务的一种方法。试一试(它会在操场上 运行),并检查代码和 //comments。直到 buildRandomArray()
函数才开始更改:
编辑 - 我对这段代码做了一些修改...
- 有一个“样本值”数组,可以更容易地重新运行
- 添加了“重置”按钮以重新运行 原始值
- 使用示例值时,条形图现在是标签,可以更清楚地表明条形图正在“移动”
- 实现了插入排序
值得注意:当 运行 在操场上使用此功能时,在排序处于活动状态时点击按钮不是很灵敏。使用 iPad 模拟器或设备进行大量投注。
public class SortViewController:UIViewController{
let stackView:UIStackView = {
let st = UIStackView()
st.axis = .horizontal
st.alignment = .center
st.distribution = .fillEqually
st.layer.shadowColor = UIColor.gray.cgColor
st.layer.shadowOffset = .zero
st.layer.shadowRadius = 5
st.layer.shadowOpacity = 1
st.spacing = 10
st.translatesAutoresizingMaskIntoConstraints = false
return st
}()
let generateButton:UIButton = {
let btn = UIButton()
btn.setTitle("Generate", for: .normal)
btn.backgroundColor = UIColor(red: 0.92, green: 0.30, blue: 0.29, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let resetButton:UIButton = {
let btn = UIButton()
btn.setTitle("Reset", for: .normal)
btn.backgroundColor = UIColor(red: 0.25, green: 0.75, blue: 0.29, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let BubbleSort:UIButton = {
let btn = UIButton()
btn.setTitle("Bubble", for: .normal)
btn.backgroundColor = UIColor(red: 0.41, green: 0.43, blue: 0.88, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let MergeSort:UIButton = {
let btn = UIButton()
btn.setTitle("Merge", for: .normal)
btn.backgroundColor = UIColor(red: 0.10, green: 0.16, blue: 0.34, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let InsertionSort:UIButton = {
let btn = UIButton()
btn.setTitle("Insertion", for: .normal)
btn.backgroundColor = UIColor(red: 0.19, green: 0.22, blue: 0.32, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let SelectionSort:UIButton = {
let btn = UIButton()
btn.setTitle("Selection", for: .normal)
btn.backgroundColor = UIColor(red: 0.51, green: 0.20, blue: 0.44, alpha: 1.00)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.italicSystemFont(ofSize: 20)
btn.layer.cornerRadius = 10
btn.layer.masksToBounds = true
btn.heightAnchor.constraint(equalToConstant: 38).isActive = true
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let mainStackView:UIStackView = {
let st = UIStackView()
st.backgroundColor = .gray
st.axis = .horizontal
st.distribution = .fillEqually
st.alignment = .bottom
st.spacing = 1
st.translatesAutoresizingMaskIntoConstraints = false
return st
}()
let baseView:UIView = {
let vw = UIView()
vw.backgroundColor = UIColor(red: 0.07, green: 0.54, blue: 0.65, alpha: 1.00)
vw.translatesAutoresizingMaskIntoConstraints = false
vw.layer.cornerRadius = 3
vw.layer.masksToBounds = true
vw.heightAnchor.constraint(equalToConstant: 15).isActive = true
return vw
}()
public override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(stackView)
view.addSubview(mainStackView)
view.addSubview(baseView)
edgesForExtendedLayout = []
stackView.addArrangedSubview(generateButton)
stackView.addArrangedSubview(resetButton)
stackView.addArrangedSubview(BubbleSort)
stackView.addArrangedSubview(MergeSort)
stackView.addArrangedSubview(InsertionSort)
stackView.addArrangedSubview(SelectionSort)
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
stackView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 50).isActive = true
baseView.bottomAnchor.constraint(equalTo: view.bottomAnchor,constant: -2).isActive = true
baseView.leftAnchor.constraint(equalTo: view.leftAnchor,constant: 5).isActive = true
baseView.rightAnchor.constraint(equalTo: view.rightAnchor,constant: -5).isActive = true
mainStackView.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 5).isActive = true
mainStackView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 5).isActive = true
mainStackView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -5).isActive = true
mainStackView.bottomAnchor.constraint(equalTo: baseView.topAnchor, constant: -5).isActive = true
setButtons()
buildRandomArray()
}
// MARK:- Actions
func setButtons(){
generateButton.addTarget(self, action: #selector(generatePressed), for: .touchUpInside)
resetButton.addTarget(self, action: #selector(resetPressed), for: .touchUpInside)
BubbleSort.addTarget(self, action: #selector(bubbleSort), for: .touchUpInside)
MergeSort.addTarget(self, action: #selector(mergeSort), for: .touchUpInside)
InsertionSort.addTarget(self, action: #selector(insertionSort), for: .touchUpInside)
SelectionSort.addTarget(self, action: #selector(selectionSort), for: .touchUpInside)
}
let sampleValues: [Int] = [
150, 175, 200, 225, 250, 275, 300, 325, 350, 375, 400, 425, 450, 475, 500
]
var currentArray: [UIView] = []
let barColor: UIColor = .cyan
let barHighlight: UIColor = .yellow
let timerInterval: TimeInterval = 0.10
func buildRandomArray(){
// we can populate the stack view much quicker by generating an array of views
// and then looping through that array to add them as arrangedSubviews
currentArray = []
// for ease during dev, use a set of predefined values
if true {
let hh = sampleValues.shuffled()
hh.forEach { h in
let viewStick: UILabel = {
let v = UILabel()
v.backgroundColor = barColor
v.text = "\(h)"
v.textAlignment = .center
v.heightAnchor.constraint(equalToConstant: CGFloat(h)).isActive = true
return v
}()
currentArray.append(viewStick)
}
} else {
var randomNumber :CGFloat!
for _ in 1..<41{
let viewStick:UIView = {
let v = UIView()
v.backgroundColor = barColor
v.translatesAutoresizingMaskIntoConstraints = false
randomNumber = CGFloat(Int.random(in: 160...500))
v.heightAnchor.constraint(equalToConstant: randomNumber).isActive = true
return v
}()
currentArray.append(viewStick)
}
}
fillStackView(sortdArr: currentArray)
}
@objc func generatePressed(){
// stop the timer if a sort is currently running
timer?.invalidate()
print("Generating Array.....")
emptyStackView()
buildRandomArray()
}
@objc func resetPressed(){
// stop the timer if a sort is currently running
timer?.invalidate()
print("Resetting.....")
fillStackView(sortdArr: currentArray)
}
var timer: Timer?
@objc func bubbleSort(){
print("Bubble Sort....")
// if a sort is running, stop it and reset the bars
if let t = timer, t.isValid {
resetPressed()
}
var j: Int = 0
var didSwap: Bool = false
var lastBarToCheck: Int = mainStackView.arrangedSubviews.count - 1
// set current bar to first bar
var curBar = mainStackView.arrangedSubviews[0]
// set new current bar background to barHighlight
curBar.backgroundColor = barHighlight
timer = Timer.scheduledTimer(withTimeInterval: timerInterval, repeats: true) { timer in
// if we have more bars to check
if j < lastBarToCheck {
if self.mainStackView.arrangedSubviews[j].frame.height > self.mainStackView.arrangedSubviews[j+1].frame.height {
// next bar is shorter than current bar, so
// swap the bar positions
self.mainStackView.insertArrangedSubview(curBar, at: j + 1)
// set the didSwap flag
didSwap = true
} else {
// next bar is taller
// set current bar background back to barColor
curBar.backgroundColor = self.barColor
// set current bar to next bar
curBar = self.mainStackView.arrangedSubviews[j+1]
// set new current bar background to barHighlight
curBar.backgroundColor = self.barHighlight
}
j += 1
} else {
if !didSwap {
// no bars were swapped, so
// set current bar back to barColor
curBar.backgroundColor = self.barColor
// stop the looping
timer.invalidate()
print("Done!")
} else {
// at least one swap occurred, so
// decrement number of bars to check
lastBarToCheck -= 1
// reset index
j = 0
// set current bar back to barColor
curBar.backgroundColor = self.barColor
// set current bar background to first bar
curBar = self.mainStackView.arrangedSubviews[j]
// set new current bar background to barHighlight
curBar.backgroundColor = self.barHighlight
// reset swap flag
didSwap = false
}
}
}
}
@objc func mergeSort(){
print("Merge Sort.....")
}
@objc func insertionSort(){
print("Insertion Sort.....")
// if a sort is running, stop it and reset the bars
if let t = timer, t.isValid {
resetPressed()
}
var index: Int = 1
var position: Int = 1
// set current bar to index bar
var curBar = mainStackView.arrangedSubviews[index]
// set new current bar background to barHighlight
curBar.backgroundColor = barHighlight
timer = Timer.scheduledTimer(withTimeInterval: timerInterval, repeats: true) { timer in
// if we have more bars to check
if index < self.mainStackView.arrangedSubviews.count {
// if we're not at the left-most bar
if position > 0 {
// if bar-to-the-left is taller than current bar
if self.mainStackView.arrangedSubviews[position - 1].frame.height > curBar.frame.height {
// move current bar one position to the left
self.mainStackView.insertArrangedSubview(curBar, at: position - 1)
position -= 1
} else {
// bar-to-the-left is shorter than current bar
index += 1
position = index
// set current bar background back to barColor
curBar.backgroundColor = self.barColor
// if we're not finished
if index < self.mainStackView.arrangedSubviews.count {
// set current bar to next bar
curBar = self.mainStackView.arrangedSubviews[index]
// set new current bar background to barHighlight
curBar.backgroundColor = self.barHighlight
}
}
} else {
// we're at the left-most bar
// increment index
index += 1
position = index
// set current bar background back to barColor
curBar.backgroundColor = self.barColor
// if we have more bars to check
if index < self.mainStackView.arrangedSubviews.count {
// set current bar to next bar
curBar = self.mainStackView.arrangedSubviews[index]
// set new current bar background to barHighlight
curBar.backgroundColor = self.barHighlight
}
}
} else {
// we've reached the end of the array
// stop the looping
timer.invalidate()
print("Done!")
}
}
}
@objc func selectionSort(){
print("selection Sort.....")
}
func emptyStackView(){
mainStackView.arrangedSubviews.forEach {
[=12=].removeFromSuperview()
}
}
func fillStackView(sortdArr:[UIView]){
sortdArr.forEach { vw in
vw.backgroundColor = self.barColor
mainStackView.addArrangedSubview(vw)
}
}
}
有关堆栈视图及其 arrangedSubviews
的提示...您无需清除并重新填充堆栈视图即可重新排列视图。
例如,如果我向堆栈视图添加 10 个标签:
// add 10 arranged subviews
for i in 0..<10 {
let v = UILabel()
v.text = "\(i)"
v.textAlignment = .center
v.backgroundColor = .green
stackView.addArrangedSubview(v)
}
它看起来像这样:
如果我想交换第 4 和第 5 个视图,我可以这样做:
// (arrays are zero-based)
stackView.insertArrangedSubview(stackView.arrangedSubviews[3], at: 4)
我得到: