I created a view with buttons and added the ability to move them using UIGestureRecognizer. I would like that when I start moving some buttons, the other buttons start moving as well, just like when I move apps on my iPhone. How do I get this to work?
Here is my code:
import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate {
var source = [String]()
var buttonView = UIView()
var dragg = UIPanGestureRecognizer()
private var initialCenter: CGPoint = .zero
override func viewDidLoad() {
super.viewDidLoad()
for _ in 0..<30 {
let rs = String.random()
source.append(rs)
}
dragg.delegate = self
buttonView.frame = CGRect.init(x: 0, y: 50, width: self.view.frame.size.width, height: self.view.frame.size.height)
self.view.addSubview(buttonView)
buttons()
}
func buttons() {
buttonView.subviews.forEach({ $0.removeFromSuperview() })
let offset = 10 // set offset on X & Y axes
let btnsOnX = 5 // how many buttons in row
let btnSize = ((Int(UIScreen.main.bounds.width)) - (offset * (btnsOnX + 1))) / btnsOnX
var xRow = 0
var xPosMultiplier = 0
for (i,element) in source.enumerated(){
xPosMultiplier += 1
if (i % btnsOnX == 0) { // change row
xRow += 1
xPosMultiplier = 0
}
let xPos = offset + (xPosMultiplier * offset) + (xPosMultiplier * btnSize)
let yPos = (offset + (xRow * offset) + (xRow * btnSize)) - (btnSize + offset)
let btn = UIButton()
btn.frame = CGRect(x: CGFloat(xPos), y: CGFloat(yPos + 0), width: CGFloat(btnSize), height: CGFloat(btnSize))
btn.tag = i
btn.layer.borderWidth = 0.5
btn.layer.cornerRadius = 10
btn.layer.borderColor = UIColor.lightGray.cgColor
btn.setTitle("\(element)", for: UIControl.State())
btn.titleEdgeInsets.left = 5
btn.titleEdgeInsets.right = 5
btn.backgroundColor = UIColor.white
btn.setTitleColor(UIColor.systemBlue, for: UIControl.State())
btn.titleLabel!.lineBreakMode = .byWordWrapping
btn.titleLabel!.textAlignment = .center
btn.titleLabel?.font = UIFont.systemFont(ofSize: CGFloat(10), weight: UIFont.Weight.bold)
dragg = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
btn.addGestureRecognizer(dragg)
buttonView.addSubview(btn)
if (i % btnsOnX == 0) {
xPosMultiplier = 0 // reset with new row after x was set
}
}
}
@objc func handlePan(_ sender: UIPanGestureRecognizer) {
if sender.state == .began || sender.state == .changed {
let translation = sender.translation(in: self.view)
sender.view!.center = CGPoint(x: sender.view!.center.x + translation.x, y: sender.view!.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: self.view)
}
}
}
extension String {
static func random(length: Int = 10) -> String {
let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var randomString: String = ""
for _ in 0..<length {
let randomValue = arc4random_uniform(UInt32(base.count))
randomString += "\(base[base.index(base.startIndex, offsetBy: Int(randomValue))])"
}
return randomString
}
}
I would like to ask how to create some "map" of buttons, or is it all handled differently?
I couldn't find anything on Google, unfortunately.
Your best choice would be to implement this with a UICollectionView
with a UICollectionViewDragDelegate
and UICollectionViewDropDelegate
. With a fairly simple implementation you will get what you want out of the box.
There is a plenty of tutorials on the internet that explain how to do it, but here is the link to the official documentation: Supporting Drag and Drop in Collection Views.