My app is has a feature in which you answer questions about people you are following, such as (Option A, B, C, D are where the names for the users will be):
The idea is that you answer a question about a user and it randomly shuffles from the people you are following from the firebase database. The images are the profile pictures that a user uploads when signing up and they are shown in order from the top left, top right, bottom left, bottom right of whoever is in the answer of the question.
I am trying to use UIScrollView but all the images have stacked up on top of each other instead of being able to swipe between them (and the Question label has disappeared)
Is there a way to get the ScrollView to work or is there a better/more efficient way of integrating a UICollectionView (i've seen a few posts that recommend this for similar things)?
I'm new to this so would appreciate any help, thanks in advance :)
UPDATE:
I've edited the constraints so the left and right anchors of the image views should be correct and added a UIView on top of the UIScrollView on storyboard. I've also seen it could be done with UIPageViewController?
@IBOutlet weak var userImageScrollView: UIScrollView!
@IBOutlet weak var contentView: UIView!
@IBOutlet weak var questionLabel: UILabel!
@IBOutlet weak var optionA: UIButton!
@IBOutlet weak var optionB: UIButton!
@IBOutlet weak var optionC: UIButton!
@IBOutlet weak var optionD: UIButton!
func setupLayout() {
userImageScrollView.isPagingEnabled = true
userImageScrollView.isScrollEnabled = true
userImageScrollView.addSubview(imageViewA)
imageViewA.translatesAutoresizingMaskIntoConstraints = false
imageViewA.widthAnchor.constraint(equalTo: userImageScrollView.widthAnchor).isActive = true
imageViewA.heightAnchor.constraint(equalTo: userImageScrollView.heightAnchor).isActive = true
imageViewA.topAnchor.constraint(equalTo: userImageScrollView.topAnchor).isActive = true
imageViewA.bottomAnchor.constraint(equalTo: userImageScrollView.bottomAnchor).isActive = true
imageViewA.leftAnchor.constraint(equalTo: userImageScrollView.leftAnchor)
imageViewA.rightAnchor.constraint(equalTo: imageViewB.leftAnchor)
imageViewA.contentMode = .scaleAspectFit
userImageScrollView.addSubview(imageViewB)
imageViewB.translatesAutoresizingMaskIntoConstraints = false
imageViewB.widthAnchor.constraint(equalTo: userImageScrollView.widthAnchor).isActive = true
imageViewB.heightAnchor.constraint(equalTo: userImageScrollView.heightAnchor).isActive = true
imageViewB.bottomAnchor.constraint(equalTo: userImageScrollView.bottomAnchor).isActive = true
imageViewB.leftAnchor.constraint(equalTo: imageViewA.rightAnchor)
imageViewA.rightAnchor.constraint(equalTo: imageViewC.leftAnchor)
imageViewB.contentMode = .scaleAspectFit
userImageScrollView.addSubview(imageViewC)
imageViewC.translatesAutoresizingMaskIntoConstraints = false
imageViewC.widthAnchor.constraint(equalTo: userImageScrollView.widthAnchor).isActive = true
imageViewC.heightAnchor.constraint(equalTo: userImageScrollView.heightAnchor).isActive = true
imageViewC.bottomAnchor.constraint(equalTo: userImageScrollView.bottomAnchor).isActive = true
imageViewC.leftAnchor.constraint(equalTo: imageViewB.rightAnchor)
imageViewC.rightAnchor.constraint(equalTo: imageViewD.leftAnchor)
imageViewC.contentMode = .scaleAspectFit
userImageScrollView.addSubview(imageViewD)
imageViewD.translatesAutoresizingMaskIntoConstraints = false
imageViewD.widthAnchor.constraint(equalTo: userImageScrollView.widthAnchor).isActive = true
imageViewD.heightAnchor.constraint(equalTo: userImageScrollView.heightAnchor).isActive = true
imageViewD.bottomAnchor.constraint(equalTo: userImageScrollView.bottomAnchor).isActive = true
imageViewD.leftAnchor.constraint(equalTo: imageViewC.rightAnchor)
imageViewD.rightAnchor.constraint(equalTo: userImageScrollView.rightAnchor)
imageViewD.contentMode = .scaleAspectFit
self.userImageScrollView.delegate = self
}
The reason your imageViews are on top of each other is you define:
imageViewB.leftAnchor.constraint(equalTo: imageViewA.leftAnchor)
This translates to: the left of imageView B is the same as the left of imageViewA, essentially saying that their left sides both start at the same place, so they will be on top of each other.
So you need to define each item from left to right:
imageViewB.leftAnchor.constraint(equalTo: imageViewA.rightAnchor)
imageViewBs left side, starts on the right of imageViewA. imageView Cs leftAnchor would be equal to imageViewBs right and so on, until you reach the final imageViewD, where you would need to have the leftAnchor equal to imageViewCs rightAnchor AND imageViewDs rightAnchor equal to the scrollViews rightAnchor to allow scrolling to work correctly.
You could also do what you want easily with a scrollView if you want to scroll Down instead of left to right, instead of left and right anchors, use centerXAnchors
to keep the views centered in the scrollView
You need to imagine you are describing the layout to someone, ImageViewA is at the top, so its top anchor is equal to the scrollViews top anchor.
With imageViewB you do the same describing it from the top down, so:
imageViewB.topAnchor.constraint(equalTo: imageViewA.bottomAnchor).isActive = true
The top of image view B starts at the bottom of imageView A, then you do the same with the rest like so:
imageViewC.topAnchor.constraint(equalTo: imageViewC.bottomAnchor).isActive = true
and
imageViewD.topAnchor.constraint(equalTo: imageViewC.bottomAnchor).isActive = true
Because its a scrollView, and you are scrolling down, the top (imageViewA) needs to be set to the top of the scroll view, and additionally the bottom item needs to define its bottomAnchor to the bottom of the scrollView in order for scrolling to work correctly:
imageViewD.bottomAnchor.constraint(equalTo: userImageScrollView.bottomAnchor).isActive = true
You also are using centerXAnchor
and leftAnchor
together in some of the views, imagine the centerXAnchor
is the same as leftAnchor
but just describing it from a different part of the view, as your centerXAnchor
comes after leftAnchor, it will just use the centerX, meaning that the image will be center in the scrollView.
I'd recommend following this pattern and building your constraints again from scratch, really focusing on what anchors need to be defined and where. You could also use the Interface Builder to add the views, but learning to define constraints like this will prove to be a very valuable skill in future and helps to keep your code very clear and clean.
UPDATE:
This code will do exactly what you want, take note of the height and width of the scroll view and the constants I use to give space. Each imageView is defined the way I stated above, left to right, with the last item having left and right constraints. remove your setupLayout and replace it with this (making sure to change the constraints where necessary to suit your app):
func setupLayout() {
view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50).isActive = true
scrollView.heightAnchor.constraint(equalToConstant: 300).isActive = true
scrollView.widthAnchor.constraint(equalToConstant: 300).isActive = true
scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
scrollView.backgroundColor = .gray
scrollView.isPagingEnabled = true
scrollView.addSubview(imageViewA)
imageViewA.translatesAutoresizingMaskIntoConstraints = false
imageViewA.widthAnchor.constraint(equalToConstant: 250).isActive = true
imageViewA.heightAnchor.constraint(equalToConstant: 250).isActive = true
imageViewA.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 25).isActive = true
imageViewA.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 25).isActive = true
imageViewA.backgroundColor = .cyan
scrollView.addSubview(imageViewB)
imageViewB.translatesAutoresizingMaskIntoConstraints = false
imageViewB.heightAnchor.constraint(equalToConstant: 250).isActive = true
imageViewB.widthAnchor.constraint(equalToConstant: 250).isActive = true
imageViewB.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 25).isActive = true
imageViewB.leftAnchor.constraint(equalTo: imageViewA.rightAnchor, constant: 25).isActive = true
imageViewB.backgroundColor = .red
scrollView.addSubview(imageViewC)
imageViewC.translatesAutoresizingMaskIntoConstraints = false
imageViewC.heightAnchor.constraint(equalToConstant: 250).isActive = true
imageViewC.widthAnchor.constraint(equalToConstant: 250).isActive = true
imageViewC.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 25).isActive = true
imageViewC.leftAnchor.constraint(equalTo: imageViewB.rightAnchor, constant: 25).isActive = true
imageViewC.backgroundColor = .black
scrollView.addSubview(imageViewD)
imageViewD.translatesAutoresizingMaskIntoConstraints = false
imageViewD.heightAnchor.constraint(equalToConstant: 250).isActive = true
imageViewD.widthAnchor.constraint(equalToConstant: 250).isActive = true
imageViewD.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 25).isActive = true
imageViewD.leftAnchor.constraint(equalTo: imageViewC.rightAnchor, constant: 25).isActive = true
imageViewD.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: -25).isActive = true
imageViewD.backgroundColor = .green
}
Here is a gif of what that code does:
Don't forget to call setupLayout
in viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
setupLayout()
// Do any additional setup after loading the view, typically from a nib.
}