Liam Rahav Liam Rahav - 17 days ago 4
Swift Question

iOS UIImageView tap only registering on top part of view

I am working on a project in which a Tic Tac Toe grid is being drawn on the screen.

The issue is that, for the bottom row of "tiles", the

UITapGestureRecognizer
is not working properly. For the top two rows of the grid, when any part of the
UIImageView
that the recognizer is assigned to is tapped, the proper function gets called. However, for the bottom row, it only works when the very top of the
UIImageView
is tapped.

Here is the code:
func drawTiles() {
for view in subviews {
if (view as? UIImageView != nil) {
view.removeFromSuperview()
}
}

// Upper Left Tile [0][0]
var view = UIImageView(image: battleGround.tiles[0][0].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(upperLeftTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX, y: frame.minY, width: frame.width / 3, height: frame.height / 3)
addSubview(view)

// Upper Middle Tile [0][1]
view = UIImageView(image: battleGround.tiles[0][1].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(upperCenterTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX + frame.width / 3, y: frame.minY, width: frame.width / 3, height: frame.height / 3)
addSubview(view)

// Upper Right Tile [0][2]
view = UIImageView(image: battleGround.tiles[0][2].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(upperRightTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX + ((frame.width / 3) * 2), y: frame.minY, width: frame.width / 3, height: frame.height / 3)
addSubview(view)



// Middle Left Tile [1][0]
view = UIImageView(image: battleGround.tiles[1][0].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(middleLeftTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX, y: frame.minY + ((frame.height / 3)), width: frame.width / 3, height: frame.height / 3)
addSubview(view)

// Middle Center Tile [1][1]
view = UIImageView(image: battleGround.tiles[1][1].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(middleCenterTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX + ((frame.width / 3)), y: frame.minY + ((frame.height / 3)), width: frame.width / 3, height: frame.height / 3)
addSubview(view)

// Middle Center Tile [1][2]
view = UIImageView(image: battleGround.tiles[1][2].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(middleRightTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX + ((frame.width / 3) * 2), y: frame.minY + ((frame.height / 3)), width: frame.width / 3, height: frame.height / 3)
addSubview(view)

// FIXME: Only clicking the top of the next 3 views works

// Lower Left Tile [2][0]
view = UIImageView(image: battleGround.tiles[2][0].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(lowerLeftTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX, y: frame.minY + ((frame.height / 3) * 2), width: frame.width / 3, height: frame.height / 3)
addSubview(view)

// Lower Center Tile [2][1]
view = UIImageView(image: battleGround.tiles[2][1].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(lowerCenterTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX + ((frame.width / 3)), y: frame.minY + ((frame.height / 3) * 2), width: frame.width / 3, height: frame.height / 3)
addSubview(view)

// Lower Right Tile [2][2]
view = UIImageView(image: battleGround.tiles[2][2].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(lowerRightTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX + ((frame.width / 3) * 2), y: frame.minY + ((frame.height / 3) * 2), width: frame.width / 3, height:frame.height / 3)
addSubview(view)
}

func upperLeftTapped() {
tapped(row: 0, column: 0)
}

func upperCenterTapped(){
tapped(row: 0, column: 1)
}

func upperRightTapped() {
tapped(row: 0, column: 2)
}

func middleLeftTapped() {
tapped(row: 1, column: 0)
}

func middleCenterTapped() {
tapped(row: 1, column: 1)
}
func middleRightTapped() {
tapped(row: 1, column: 2)
}

func lowerLeftTapped() {
tapped(row: 2, column: 0)
}

func lowerCenterTapped() {
tapped(row: 2, column: 1)
}

func lowerRightTapped() {
tapped(row: 2, column: 2)
}

func tapped(row: Int, column: Int) {
if (battleGround.tiles[row][column] == .empty) {
battleGround.tiles[row][column] = currentPlayer

drawTiles()
}
}


Here is the code to pay attention to (what draws the lower row of the grid)

// Lower Left Tile [2][0]
view = UIImageView(image: battleGround.tiles[2][0].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(lowerLeftTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX, y: frame.minY + ((frame.height / 3) * 2), width: frame.width / 3, height: frame.height / 3)
addSubview(view)

// Lower Center Tile [2][1]
view = UIImageView(image: battleGround.tiles[2][1].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(lowerCenterTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX + ((frame.width / 3)), y: frame.minY + ((frame.height / 3) * 2), width: frame.width / 3, height: frame.height / 3)
addSubview(view)

// Lower Right Tile [2][2]
view = UIImageView(image: battleGround.tiles[2][2].image)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(lowerRightTapped)))
view.isUserInteractionEnabled = true
view.frame = CGRect(x: frame.minX + ((frame.width / 3) * 2), y: frame.minY + ((frame.height / 3) * 2), width: frame.width / 3, height:frame.height / 3)
addSubview(view)


EDIT: Here are some screenshots of the app running to give a better idea of what the issue is.

image1

This screenshot shows what an empty grid looks like.

image2

This screenshot shows the grid after I clicked on the top middle and middle left tiles of the grid. As you can see, the image is properly added.

However, if I tap on the bottom row of the grid, nothing happens. Look at this GIF:

gif1

I tapped on the bottom middle multiple times, and nothing happened. Yet tapping in the center worked properly.

Here is the interesting part: tapping on the top of that same bottom middle tile causes the image to appear. Take a look:

enter image description here

Any help would be greatly appreciated!

Answer

Use Debug -> View Debugging -> Capture View Hierarchy, and check that the view you're inserting all these tiles into actually stretches as far as you assume it's doing.

An easy test is also to use self.clipsToBounds = YES; If you can't see all your tiles, your containing view isn't big enough, which means gestures won't be properly forwarded to the gesture recognizers.

Comments