pan gesture recongizer not working with if statement - swift

In my swift code below i have 2 objects with the goal of only one of them having a pan gesture applied to them at a time. Right now my code works with one of the boxes but when the if statement is applied I can control the other box by touching the first box. I can dragged and drop both boxes when the correct if statement is applied.
import UIKit
class ViewController: UIViewController {
var frontBox = UIButton()
var backBox = UIButton()
var selectorB = UIButton()
var selctorValue = 0
var panGesture = UIPanGestureRecognizer()
// constraint we will modify when slider is changed
var backBoxWidth: NSLayoutConstraint!
// constraints we will modify when backBox is dragged
var backBoxCenterY: NSLayoutConstraint!
var backBoxLeading: NSLayoutConstraint!
var FrontBoxWidth: NSLayoutConstraint!
// constraints we will modify when backBox is dragged
var FrontBoxCenterY: NSLayoutConstraint!
var FrontBoxLeading: NSLayoutConstraint!
var tim = 50.0
override func viewDidLoad() {
super.viewDidLoad()
[backBox,selectorB,frontBox].forEach{
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.backgroundColor = UIColor(
red: .random(in: 0.0...1),
green: .random(in: 0.9...1),
blue: .random(in: 0.7...1),
alpha: 1
)
}
NSLayoutConstraint.activate([
selectorB.bottomAnchor.constraint(equalTo: view.bottomAnchor),
selectorB.leadingAnchor.constraint(equalTo: view.leadingAnchor),
selectorB.heightAnchor.constraint(equalTo: view.heightAnchor,multiplier: 0.1),
selectorB.widthAnchor.constraint(equalTo: view.widthAnchor,multiplier: 1),
])
// backBox Width constraint
backBoxWidth = backBox.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.2)
// backBox CenterY constraint
backBoxCenterY = backBox.centerYAnchor.constraint(equalTo: view.centerYAnchor)
// backBox Leading constraint
backBoxLeading = backBox.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: CGFloat(tim))
// backBox Width constraint
FrontBoxWidth = frontBox.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.2)
// backBox CenterY constraint
FrontBoxCenterY = frontBox.centerYAnchor.constraint(equalTo: view.centerYAnchor)
// backBox Leading constraint
FrontBoxLeading = frontBox.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: CGFloat(tim))
NSLayoutConstraint.activate([
// backBox Height is constant
backBox.heightAnchor.constraint(equalTo: view.heightAnchor,multiplier: 0.5),
backBoxWidth,
backBoxLeading,
backBoxCenterY,
frontBox.heightAnchor.constraint(equalTo: view.heightAnchor,multiplier: 0.3),
FrontBoxWidth,
FrontBoxCenterY,
FrontBoxLeading,
])
panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
selectorB.addTarget(self, action: #selector(press), for: .touchDown)
frontBox.addGestureRecognizer(panGesture)
backBox.addGestureRecognizer(panGesture)
frontBox.isUserInteractionEnabled = true
backBox.isUserInteractionEnabled = true
}
#objc func draggedView(_ sender: UIPanGestureRecognizer) {
if selctorValue == 1 {
// update backBox Leading and CenterY constraints
let translation = sender.translation(in: self.view)
backBoxLeading.constant += translation.x
backBoxCenterY.constant += translation.y
sender.setTranslation(CGPoint.zero, in: self.view)
}
else {
let translation = sender.translation(in: self.view)
FrontBoxLeading.constant += translation.x
FrontBoxCenterY.constant += translation.y
sender.setTranslation(CGPoint.zero, in: self.view)
}
}
#objc func press(){
selctorValue = selctorValue == 0 ? 1 : 0
}
}
[![enter image description here][1]][1]

Eliminate the if statement and use object oriented programming. One pan gesture recognizer for different views is silly. Gesture recognizers can be enabled or disabled. So just give each view its own pan gesture recognizer and have the button toggle which one is enabled.

Related

align object with changeable constraints to center x point on the view

I want my swift code below to align the box to the center of the x axis. You can see from the gif below of what my code is doing. When the purple button is pressed I would like the box to be align. I am not sure how to d this because some of the constraints are declared as var I dont know where to go next.
import UIKit
class ViewController: UIViewController {
var slizer = UISlider()
var viewDrag = UIImageView()
var b2 = UIButton()
var panGesture = UIPanGestureRecognizer()
// Width, Leading and CenterY constraints for viewDrag
var widthConstraints: NSLayoutConstraint!
var viewDragLeadingConstraint: NSLayoutConstraint!
var viewDragCenterYConstraint: NSLayoutConstraint!
var tim: CGFloat = 50.0
var slidermultiplier: CGFloat = 0.6
override func viewDidLoad() {
super.viewDidLoad()
[viewDrag,slizer,b2].forEach{
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
b2.backgroundColor = .purple
NSLayoutConstraint.activate([
b2.bottomAnchor.constraint(equalTo: slizer.topAnchor),
b2.leadingAnchor.constraint(equalTo: view.leadingAnchor),
b2.heightAnchor.constraint(equalTo: view.heightAnchor,multiplier: 0.05),
b2.widthAnchor.constraint(equalTo: view.widthAnchor,multiplier: 1),
slizer.bottomAnchor.constraint(equalTo: view.bottomAnchor),
slizer.leadingAnchor.constraint(equalTo: view.leadingAnchor),
slizer.heightAnchor.constraint(equalTo: view.heightAnchor,multiplier: 0.2),
slizer.widthAnchor.constraint(equalTo: view.widthAnchor,multiplier: 1),
])
slizer.addTarget(self, action: #selector(increase), for: .valueChanged)
viewDrag.backgroundColor = .orange
// no point setting a frame, since
// viewDrag has .translatesAutoresizingMaskIntoConstraints = false
//viewDrag.frame = CGRect(x: view.center.x-view.frame.width * 0.05, y: view.center.y-view.frame.height * 0.05, width: view.frame.width * 0.1, height: view.frame.height * 0.1)
// start with viewDrag
// width = "slidermultiplier" percent of view width
widthConstraints = viewDrag.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: slidermultiplier)
// Leading = "tim" pts from view leading
viewDragLeadingConstraint = viewDrag.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: tim)
// centered vertically
viewDragCenterYConstraint = viewDrag.centerYAnchor.constraint(equalTo: view.centerYAnchor)
NSLayoutConstraint.activate([
// viewDrag height will never change, so we can set it here
viewDrag.heightAnchor.constraint(equalTo: view.heightAnchor,multiplier: 0.3),
// activate the 3 "modifiable" constraints
widthConstraints,
viewDragLeadingConstraint,
viewDragCenterYConstraint,
])
panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
viewDrag.isUserInteractionEnabled = true
viewDrag.addGestureRecognizer(panGesture)
// start the slider at the same percentage we've used
// for viewDrag's initial width
slizer.value = Float(slidermultiplier)
b2.addTarget(self, action: #selector(press), for: .touchDown)
}
#objc func press(){
}
#objc func draggedView(_ sender: UIPanGestureRecognizer) {
// old swift syntax
//self.view.bringSubview(toFront: viewDrag)
self.view.bringSubview(toFront: viewDrag)
let translation = sender.translation(in: self.view)
viewDragLeadingConstraint.constant += translation.x
viewDragCenterYConstraint.constant += translation.y
// don't do this
//viewDrag.center = CGPoint(x: viewDrag.center.x + translation.x , y: viewDrag.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: self.view)
}
#objc func increase() {
// get the new value of the slider
slidermultiplier = CGFloat(slizer.value)
// deactivate widthConstraints
widthConstraints.isActive = false
// create new widthConstraints with slider value as a multiplier
widthConstraints = viewDrag.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: slidermultiplier)
// activate the new widthConstraints
widthConstraints.isActive = true
}
}
I recommend to work directly with frame not constraints.
Constraints are suitable when to deal with mutable screen sizes and screen rotation.
Step 1: Put your movable objects into a container view
Step 2: Handle objects by coordinate inside container view
Step 3: Add container to your view controller's view and constraint layouting it with other elements
class ViewController: UIViewController {
#IBOutlet weak var container: Container!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
container.addPanGestures()
}
#IBAction func didTapLeftButton(_ sender: Any) {
container.setLeftAlign()
}
#IBAction func didTapCenterButton(_ sender: Any) {
container.setCenterXAlign()
}
#IBAction func didTapRightButton(_ sender: Any) {
container.setRightAlign()
}
#IBAction func slide(_ sender: UISlider) {
let scale = CGFloat(max(sender.value, 0.1))
container.setWidthScale(scale)
}
}
class Container: UIView {
func setLeftAlign() {
for view in self.subviews {
view.frame.origin = CGPoint(x: 0, y: view.frame.origin.y)
}
}
func setRightAlign() {
for view in self.subviews {
view.frame.origin = CGPoint(x: self.frame.width - view.frame.width, y:view.frame.origin.y)
}
}
func setCenterXAlign() {
for view in self.subviews {
var center = view.center
center.x = self.frame.width / 2
view.center = center
}
}
func setWidthScale(_ ratio: CGFloat) {
for view in self.subviews {
var frame = view.frame
let center = view.center
frame.size.width = self.frame.width * ratio
view.frame = frame
view.center = center
}
}
func addPanGestures() {
for v in self.subviews {
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didPan(_:)))
v.addGestureRecognizer(panGesture)
}
}
private var initCenter: CGPoint!
#objc func didPan(_ sender: UIPanGestureRecognizer) {
let view = sender.view!
let translation = sender.translation(in: view)
switch sender.state {
case .began:
initCenter = view.center
case .changed:
view.center = CGPoint(x: initCenter.x + translation.x, y: initCenter.y + translation.y)
case .cancelled:
view.center = initCenter
default:
return
}
}
}

func is reseting position of pan gesture

The func addBlackView is adding a black view everytime the func is called. The black view is connected a to uiPangesture the problem is evertyime the func addblackview is called the code is reseting the position of wherever the first black has been moved. You can see what is goin on in the gif below. I just want the 1st black view to not move and stay in the same position if a new black view is Called.
import UIKit
class ViewController: UIViewController {
var image1Width2: NSLayoutConstraint!
var iHieght: NSLayoutConstraint!
var currentView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(currentView)
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
button.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16),
button.widthAnchor.constraint(equalToConstant: 100),
button.heightAnchor.constraint(equalToConstant: 80),
])
button.addTarget(self,action: #selector(addBlackView),for: .touchUpInside)
}
let slider:UISlider = {
let slider = UISlider(frame: .zero)
return slider
}()
private lazy var button: UIButton = {
let button = UIButton()
button.backgroundColor = .blue
button.setTitleColor(.white, for: .normal)
button.setTitle("add", for: .normal)
return button
}()
let blackView: UIView = {
let view = UIView()
view.backgroundColor = .black
return view
}()
var count = 0
#objc
private func addBlackView() {
let newBlackView = UIView(frame: CGRect(x: 20, y: 20, width: 100, height: 100)) // whatever frame you want
newBlackView.backgroundColor = .orange
self.view.addSubview(newBlackView)
self.currentView = newBlackView
let recognizer = UIPanGestureRecognizer(target: self, action: #selector(moveView(_:)))
newBlackView.addGestureRecognizer(recognizer)
newBlackView.isUserInteractionEnabled = true
image1Width2 = newBlackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.1)
image1Width2.isActive = true
iHieght = newBlackView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.1)
iHieght.isActive = true
count += 1
newBlackView.tag = (count)
newBlackView.translatesAutoresizingMaskIntoConstraints = false
newBlackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
newBlackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
#objc private func moveView(_ recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .changed:
let translation = recognizer.translation(in: self.view)
recognizer.view!.center = .init(x: recognizer.view!.center.x + translation.x,
y: recognizer.view!.center.y + translation.y)
recognizer.setTranslation(.zero, in: self.view)
default:
break
}
}
}
They always go back to the centre because you have constrained the black (orange) views to the centre:
newBlackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
newBlackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
You shouldn't even be able to drag any of the views at all, but I guess setting center ignores constraints for some reason. Anyway, when you add a new view, UIKit calls view.setNeedsLayout/layoutIfNeeded somewhere down the line, and this causes all the views to realise "oh wait, I'm supposed to be constrained to the centre!" and snap back. :D
If you want to keep using constraints, try storing the centre X and Y constraints of all the views in an array:
var centerXConstraints: [NSLayoutConstraint] = []
var centerYConstraints: [NSLayoutConstraint] = []
And append to them when you add a new view:
let yConstraint = newBlackView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
let xConstraint = newBlackView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
xConstraint.isActive = true
yConstraint.isActive = true
centerXConstraints.append(xConstraint)
centerYConstraints.append(yConstraint)
Then, rather than changing the center, change the constant of these constraints:
let centerXConstraint = centerXConstraints[recognizer.view!.tag - 1]
let centerYConstraint = centerYConstraints[recognizer.view!.tag - 1]
centerXConstraint.constant += translation.x
centerYConstraint.constant += translation.y
Alternatively, and this is what I would do, just remove all your constraints, and translateAutoresizingMaskIntoConstraints = true. This way you can freely set your center.

UIView is Being Over Written Every Time Func is Called

My swift codes goal is to place a uiview every time the button is pressed. In my gif you can see every time the blue button is called it is over written. When the code is pressed the gif should have 2 uiviews in it. You can see the transparent uiview of where the first view disappears. Basically all that is wrong with this code is when the addBlackView is called it should add to the views on the screen basically just like a infinite array.
import UIKit
class ViewController: UIViewController {
var image1Width2: NSLayoutConstraint!
var iHieght: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(slider)
slider.translatesAutoresizingMaskIntoConstraints = false
slider.value = 0.5
slider.isUserInteractionEnabled = true
NSLayoutConstraint.activate([
slider.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
slider.leadingAnchor.constraint(equalTo: view.leadingAnchor),
slider.heightAnchor.constraint(equalToConstant: 100),
slider.widthAnchor.constraint(equalTo: view.widthAnchor,multiplier: 1),
])
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
button.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16),
button.widthAnchor.constraint(equalToConstant: 100),
button.heightAnchor.constraint(equalToConstant: 80),
])
button.addTarget(self,action: #selector(addBlackView),for: .touchUpInside)
slider.addTarget(self, action: #selector(increase), for: .allEvents)
}
let slider:UISlider = {
let slider = UISlider(frame: .zero)
return slider
}()
private lazy var button: UIButton = {
let button = UIButton()
button.backgroundColor = .blue
button.setTitleColor(.white, for: .normal)
button.setTitle("add", for: .normal)
return button
}()
let blackView: UIView = {
let view = UIView()
view.backgroundColor = .black
return view
}()
#objc
private func addBlackView() {
self.view.addSubview(blackView)
blackView.translatesAutoresizingMaskIntoConstraints = false
blackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
blackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
image1Width2 = blackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.1)
image1Width2.isActive = true
iHieght = blackView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.1)
iHieght.isActive = true
view.layoutIfNeeded()
let recognizer = UIPanGestureRecognizer(target: self, action: #selector(moveView(_:)))
blackView.addGestureRecognizer(recognizer)
}
#objc private func moveView(_ recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
print("gesture began")
case .changed:
let translation = recognizer.translation(in: self.view)
recognizer.view!.center = .init(x: recognizer.view!.center.x + translation.x,
y: recognizer.view!.center.y + translation.y)
recognizer.setTranslation(.zero, in: self.view)
default:
break
}
}
#objc func increase() {
image1Width2.constant = CGFloat(slider.value) * view.frame.size.width * 0.10
iHieght.constant = CGFloat(slider.value) * view.frame.size.width * 0.10
}}
The problem is that you're reusing and resetting blackView every time you execute addBlackView, so the changes you've made will be lost (hence why the view goes back in the center after you pressed the button).
You would need to create a complete new view in addBlackView, which would be your 'currentView' that you are manipulating and then add add gesture recognizers to it. Then once you execute addBlackView again, the 'currentView' would be 'validated' (stored in an array or whatever you need to do with it) and then you create another one to manipulate.
Something like this:
private func addBlackView() {
let newBlackView = UIView(frame: CGRect(0, 0, 10, 10)) // whatever frame you want
self.view.addSubview(newBlackView)
self.currentView = newBlackView
}

Entering text into textfield causing uipangesture object to move back to its orginal position

My swift code below has a image view connected to a pangesture. When something is entered into a textfield when after the image view is moved. The image view reverts back to its original position.The gif represents that what is going on. I just don't want the effect of the pangesutre to be nullified after text is entered into the textfield.
LINK TO GITHUB https://github.com/redrock34/sse
import UIKit
class ViewController: UIViewController {
var pic = UIImageView()
let fight = (0..<10).map { _ in UIImageView() }
var textEnter = UITextField()
var g2 = UIPanGestureRecognizer()
var slider = UISlider()
override func viewDidLoad() {
super.viewDidLoad()
fight[0].image = UIImage(named: "a.png")
fight.forEach{
$0.isUserInteractionEnabled = true
}
[slider,textEnter].forEach{
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.backgroundColor = .blue
}
slider.backgroundColor = .clear
g2 = UIPanGestureRecognizer(target: self, action: #selector(ViewController.g1Method))
fight[0].addGestureRecognizer(g2)
pic.backgroundColor = .clear
pic.backgroundColor = .systemGreen
fight.forEach{
$0.backgroundColor = .clear
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
[pic].forEach{
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
// Do any additional setup after loading the view.
NSLayoutConstraint.activate ([
pic.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant :0),
pic.topAnchor.constraint(equalTo: fight[0].bottomAnchor, constant : 0),
pic.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.62, constant: 0),
pic.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant : 0),
textEnter.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant :0),
textEnter.topAnchor.constraint(equalTo: view.topAnchor, constant : 0),
textEnter.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.1, constant: 0),
textEnter.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant : 0),
fight[0].trailingAnchor.constraint(equalTo: view.trailingAnchor, constant :0),
fight[0].topAnchor.constraint(equalTo: textEnter.bottomAnchor, constant : 0),
fight[0].heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.10, constant: 0),
fight[0].widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.10, constant: 0),
fight[0].leadingAnchor.constraint(equalTo: view.leadingAnchor, constant : 0),
slider.topAnchor.constraint(equalTo: pic.bottomAnchor, constant : 0),
slider.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.08, constant: 0),
slider.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1, constant: 0),
slider.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant : 0),
])
textEnter.textAlignment = .center
self.view.sendSubviewToBack(pic)
}
#objc func g1Method(_ sender: UIPanGestureRecognizer){
let tranistioon = sender.translation(in: self.view)
sender.view!.center = CGPoint(x: sender.view!.center.x + tranistioon.x, y: sender.view!.center.y + tranistioon.y)
sender.setTranslation(CGPoint.zero,in: self.view) }
}
Try creating a class of UIImageVIew and write the following code:
import UIKit
class draggableImage:UIImageView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderWidth = 1
self.layer.borderColor = UIColor.red.cgColor
self.isUserInteractionEnabled = true
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first;
let location = touch?.location(in: self.superview);
if(location != nil)
{
self.frame.origin = CGPoint(x: location!.x-self.frame.size.width/2, y: location!.y-self.frame.size.height/2);
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
Don't forget to assign the class draggableImage to the Image View from the Attribute Menu.
You cannot mix explicit frame setting with auto-layout constraints for the same object.
For example, if you:
add a label to the main view
set label.translatesAutoresizingMaskIntoConstraints = false
set constraints for the label
then, via code...
change the .center property of the label
you will see the label move, but when the next UI update happens (you edit a text field, tap a button, rotate the device, etc), auto-layout will reset the frame of the label based on its constraints.
So, in your pan gesture handler, you can either update the .constant values for the fight[0] object (instead of changing its .center), or...
You can leave label.translatesAutoresizingMaskIntoConstraints = true for your fight objects and explicitly set their frames and centers.
Note: in either case, you do not want to constrain any other elements relative to the fight objects, or they will move when you move fight[0].
Here is a modification to your ViewController class (from your GitHub zip), implementing the method of not using auto-layout constraints on your fight objects.
class ViewController: UIViewController {
var pic = UIImageView()
let fight = (0..<10).map { _ in UIImageView() }
var textEnter = UITextField()
var g2 = UIPanGestureRecognizer()
var slider = UISlider()
override func viewDidLoad() {
super.viewDidLoad()
fight[0].image = UIImage(named: "a.png")
fight.forEach{
$0.isUserInteractionEnabled = true
}
[slider,textEnter].forEach{
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.backgroundColor = .blue
}
slider.backgroundColor = .clear
g2 = UIPanGestureRecognizer(target: self, action: #selector(ViewController.g1Method))
fight[0].addGestureRecognizer(g2)
pic.backgroundColor = .systemGreen
fight.forEach{
$0.backgroundColor = .clear
view.addSubview($0)
// do NOT use auto-layout for fight views
//$0.translatesAutoresizingMaskIntoConstraints = false
}
[pic].forEach{
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
// need a non-rendering "spacer" to vertically separate textEnter from Pic
let spacer = UILayoutGuide()
view.addLayoutGuide(spacer)
// NOTE: do NOT constrain any elements relative to fight views
NSLayoutConstraint.activate ([
// constrain textEnter top / leading / trailing to view
textEnter.topAnchor.constraint(equalTo: view.topAnchor, constant : 0),
textEnter.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant : 0),
textEnter.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant :0),
// constrain textEnter height to 0.1 * view height
textEnter.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.1, constant: 0),
// constrain spacer top to bottom of textEnter
spacer.topAnchor.constraint(equalTo: textEnter.bottomAnchor, constant: 0.0),
// constrain spacer leading to view leading
spacer.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0.0),
// constrain spacer height to 0.1 * view height
spacer.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.10),
// spacer width doesn't matter
spacer.widthAnchor.constraint(equalToConstant: 1.0),
// constrain pic Top to spacer bottom
pic.topAnchor.constraint(equalTo: spacer.bottomAnchor, constant: 0.0),
// constrain pic leading / trailing to view
pic.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant : 0),
pic.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant :0),
// constrain pic height as you had it
pic.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.62, constant: 0),
// slider constraints
slider.topAnchor.constraint(equalTo: pic.bottomAnchor, constant : 0),
slider.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.08, constant: 0),
slider.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1, constant: 0),
slider.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant : 0),
])
textEnter.textAlignment = .center
self.view.sendSubviewToBack(pic)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// set fight[0] frame *after* textEnter has been laid-out
fight[0].frame.size = CGSize(width: view.frame.width * 0.10, height: view.frame.height * 0.10)
let x = view.frame.origin.x
let y = textEnter.frame.origin.y + textEnter.frame.size.height
fight[0].frame.origin = CGPoint(x: x, y: y)
}
#objc func g1Method(_ sender: UIPanGestureRecognizer){
let tranistioon = sender.translation(in: self.view)
sender.view!.center = CGPoint(x: sender.view!.center.x + tranistioon.x, y: sender.view!.center.y + tranistioon.y)
sender.setTranslation(CGPoint.zero,in: self.view)
}
}
Use this method instead
var existingTransition : CGAffineTransform?
#objc func g1Method(_ sender: UIPanGestureRecognizer){
guard let child = sender.view else{return}
let transitionPoint = sender.translation(in: self.view)
let newTransition = CGAffineTransform(translationX: transitionPoint.x, y: transitionPoint.y)
switch sender.state {
case .ended,.cancelled:// on End
if let existing = existingTransition{
self.existingTransition = newTransition.concatenating(existing)
}else{
self.existingTransition = newTransition
}
default://on change and other states
if let existing = existingTransition{
child.transform = newTransition
.concatenating(existing)
}else{
child.transform = newTransition
}
}
self.view.layoutIfNeeded()
}
Sorry if my code is messy. But I checked some scenaios its working fine.
happy codding

use uipangesture on a nslayout constraint

My swift code below is using nslayout constraints in view did load. I have tried to place a uipangestureRecognizer on a imageview. To get the image view to move around the uiview controller. Right now If I touch the imageview nothing happens. I understand that I have placed permenent constraints in view did load. I just dont know how to get the imageview to move around. The image view I am trying to move is fight[0].
import UIKit
class ViewController: UIViewController {
var pic = UIImageView()
var draw = UIView()
let fight = (0..<10).map { _ in UIImageView() }
var g2 = UIPanGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
fight[0].image = UIImage(named: "a.png")
g2 = UIPanGestureRecognizer(target: self, action: #selector(ViewController.g1Method))
fight[0].addGestureRecognizer(g2)
fight.forEach{
$0.backgroundColor = .clear
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
[pic,draw].forEach{
$0.backgroundColor = .red
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
// Do any additional setup after loading the view.
NSLayoutConstraint.activate ([
fight[0].trailingAnchor.constraint(equalTo: view.trailingAnchor, constant :0),
fight[0].topAnchor.constraint(equalTo: view.topAnchor, constant : 50),
fight[0].heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.10, constant: 0),
fight[0].widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.10, constant: 0),
fight[0].leadingAnchor.constraint(equalTo: view.leadingAnchor, constant : 0),
])
}
#objc func g1Method(_ sender: UIPanGestureRecognizer){
self.view.bringSubviewToFront(sender.view!)
let tranistioon = sender.translation(in: self.view)
sender.view!.center = CGPoint(x: sender.view!.center.x + tranistioon.x, y: sender.view!.center.y + tranistioon.y)
sender.setTranslation(CGPoint.zero,in: self.view) }
}
By default, UIImageView do not have user interaction enabled.
Add this line inside viewDidLoad():
fight[0].isUserInteractionEnabled = true
You should now be able to drag that image view around.