UIScrollView doesn't respond to gestures - ios

I have a UIScrollView which is a dashboard basically with relevant information for the user. It has a pager underneath and contains three UIViews at the moment.
I used to set the different views with a CGRect as the frame, setting the x offset to the width of the UIScrollView * the page number. This worked fine, but it was a hassle to make the UIScrollView disappear when scrolling in the UITableView underneath.
I am now using AutoLayout with constraints to display the information, which works fine. However, my UIScrollView is not scrolling anymore. It does not respond to any swipe gestures I perform. However, it does respond to taps in the pager, which shows me the offsets and constraints are right, I am just unable to scroll in it. Take a look:
My UIScrollView is made up like this:
var dashboardView: UIScrollView = {
let dashboardView = UIScrollView()
dashboardView.translatesAutoresizingMaskIntoConstraints = false
dashboardView.backgroundColor = UIColor.clear
dashboardView.layer.masksToBounds = true
dashboardView.layer.cornerRadius = 10
dashboardView.isPagingEnabled = true
dashboardView.showsHorizontalScrollIndicator = false
dashboardView.showsVerticalScrollIndicator = false
dashboardView.alwaysBounceHorizontal = false
dashboardView.alwaysBounceVertical = false
return dashboardView
}()
I then set the different views like so:
for index in 0..<3 {
let currentDash = UIView()
currentDash.backgroundColor = UIColor.lightGray
currentDash.layer.cornerRadius = 10
currentDash.layer.masksToBounds = false
currentDash.translatesAutoresizingMaskIntoConstraints = false
currentDash.isUserInteractionEnabled = false
dashboardView.addSubview(currentDash)
currentDash.topAnchor.constraint(equalTo: dashboardView.topAnchor).isActive = true
currentDash.bottomAnchor.constraint(equalTo: dashboardView.bottomAnchor).isActive = true
currentDash.leadingAnchor.constraint(equalTo: dashboardView.leadingAnchor, constant: dashboardWidth * CGFloat(index)).isActive = true
currentDash.trailingAnchor.constraint(equalTo: dashboardView.leadingAnchor, constant: (dashboardWidth * CGFloat(index)) + dashboardWidth).isActive = true
currentDash.widthAnchor.constraint(equalToConstant: dashboardWidth).isActive = true
currentDash.heightAnchor.constraint(equalToConstant: dashboardHeight).isActive = true
}
When I remove the constraints (AutoLayout) and set it with frames it works perfectly, like this:
dashFrame.origin.x = dashboardWidth * CGFloat(index)
dashFrame.size = CGSize(width: dashboardWidth, height: dashboardHeight)
let currentDash = UIView(frame: dashFrame)
But then, because of setting the frame, the view doesn't scroll up as I would, which is why I want to use AutoLayout.
Any ideas or suggestions as to what I am doing wrong? My ScrollViewDidScroll() method simply gets not called, because a simple print doesn't return anything in my console.
I have tried setting the isUserInteractionEnabled property to false on the currentDash UIView, which was no success. I have also tried removing all constraints separately, some together, etc. - but also this doesn't work - I need a topAnchor, bottomAnchor, heightAnchor and widthAnchor at least.
Oh, and, of course, I set the contentSize property:
dashboardView.contentSize = CGSize(width: dashboardWidth * CGFloat(dashboardPager.numberOfPages), height: dashboardHeight)
Any suggestions or hints towards the right answer would be deeply appreciated. Thanks!
EDIT: I also set constraints for the UIScrollView itself:
dashboardView.topAnchor.constraint(equalTo: dashboardBG.topAnchor).isActive = true
dashboardView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: normalSpacing).isActive = true
dashboardView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -normalSpacing).isActive = true
dashboardView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
dashboardView.heightAnchor.constraint(equalToConstant: dashboardHeight).isActive = true
dashboardView.delegate = self

The issue is here
currentDash.leadingAnchor.constraint(equalTo: dashboardView.leadingAnchor, constant: dashboardWidth * CGFloat(index)).isActive = true
currentDash.trailingAnchor.constraint(equalTo: dashboardView.leadingAnchor, constant: (dashboardWidth * CGFloat(index)) + dashboardWidth).isActive = true
1- Regarding leading of the views , the leading of the first view should be pinned to the scrollView , the leading of the second view pinned to the trailing of the previous and so on , so
if index == 0{
// make leading with scrolview leading
else
{
// make leading with prevView trailing
}
So make a var initiated with scrollView and change end of for loop
2- Regarding the trailing , only the last view trailing be pinned to the trailing of the scrollview
if index == 2 { // last
// make trailing with scrollView
}

I eventually figured it out thanks to Sh_Khan’s answer. I held on to the original code, but removed the trailingAnchor for all indexes and added it only to the last index.
for index in 0..<3 {
let currentDash = UIView()
currentDash.backgroundColor = UIColor.lightGray
currentDash.layer.cornerRadius = 10
currentDash.layer.masksToBounds = false
currentDash.translatesAutoresizingMaskIntoConstraints = false
currentDash.isUserInteractionEnabled = false
dashboardView.addSubview(currentDash)
currentDash.topAnchor.constraint(equalTo: dashboardView.topAnchor).isActive = true
currentDash.bottomAnchor.constraint(equalTo: dashboardView.bottomAnchor).isActive = true
currentDash.leadingAnchor.constraint(equalTo: dashboardView.leadingAnchor, constant: dashboardWidth * CGFloat(index)).isActive = true
currentDash.widthAnchor.constraint(equalToConstant: dashboardWidth).isActive = true
currentDash.heightAnchor.constraint(equalToConstant: dashboardHeight).isActive = true
if index == 2 {
currentDash.trailingAnchor.constraint(equalTo: dashboardView.trailingAnchor).isActive = true
}
}
Thanks for the help!

Related

Why I cannot modify a UIScrollview leading edge programmatically?

I'm fairly new to iOS app development. I'm trying to make an iOS app which will have an horizontal scroll view with labels added as subviews which exhibit a snapping behaviour. I'm using UIKit and auto layouts (Swift 5, Xcode 12.01) for a programmatically build UI. I have three labels to be added to the scroll view, one is centred, one is offset 500 pt from the scrollview centre (right) and one is at -500 from the centre (left)
When I try to set the scrollview trailing anchor referring to the label on the right I have no problems. However, when I try to set the leading edge referring to the label on the left (leading edge), I encounter the error: [LayoutConstraints] Unable to simultaneously satisfy constraints.
Am I missing something?
I attach my code below:
// Create scroll view on top of bottom view
func createScrollView() -> UIScrollView {
let myScrollView = UIScrollView()
myScrollView.isScrollEnabled = true
myScrollView.backgroundColor = .red
myScrollView.showsHorizontalScrollIndicator = true
view.addSubview(myScrollView)
// Constraints
myScrollView.translatesAutoresizingMaskIntoConstraints = false
myScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
myScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
myScrollView.bottomAnchor.constraint(equalTo: bottomView.firstBaselineAnchor).isActive = true
myScrollView.heightAnchor.constraint(equalToConstant: 50).isActive = true
// Add some elements to the view
let label1 = UILabel()
let label2 = UILabel()
let label3 = UILabel()
label1.text = "EV 0."
label1.font = .systemFont(ofSize: 20)
label1.translatesAutoresizingMaskIntoConstraints = false
label2.text = "ISO"
label2.font = .systemFont(ofSize: 20)
label2.translatesAutoresizingMaskIntoConstraints = false
label3.text = "SNAP"
label3.font = .systemFont(ofSize: 20)
label3.translatesAutoresizingMaskIntoConstraints = false
// Add labels to scroll view
myScrollView.addSubview(label1)
myScrollView.addSubview(label2)
myScrollView.addSubview(label3)
// Add constraint
label1.centerYAnchor.constraint(equalTo: myScrollView.centerYAnchor).isActive = true
label2.centerYAnchor.constraint(equalTo: myScrollView.centerYAnchor).isActive = true
label3.centerYAnchor.constraint(equalTo: myScrollView.centerYAnchor).isActive = true
label1.centerXAnchor.constraint(equalTo: myScrollView.centerXAnchor, constant: -300).isActive = true
label2.centerXAnchor.constraint(equalTo: myScrollView.centerXAnchor, constant: 300).isActive = true
label3.centerXAnchor.constraint(equalTo: myScrollView.centerXAnchor, constant: 0).isActive = true
// Add last constraint in the scroll view
myScrollView.leadingAnchor.constraint(equalTo: label1.leadingAnchor, constant: -500).isActive = true
myScrollView.trailingAnchor.constraint(equalTo: label2.trailingAnchor, constant: 500).isActive = true
return myScrollView
Thanks a lot for your help!!!

View height wrap child views content

I want to add a image left of the super view and a custom label center of the superview. Also superview's height must wrap children. Also I am adding superview in to a stack(fill,fill distribution and alignment ). Code is below but imageview not showing. What is problem here?
let topView = UIView()
topView.translatesAutoresizingMaskIntoConstraints = false
topView.heightAnchor.constraint(equalToConstant: 30).isActive = true
topView.widthAnchor.constraint(equalToConstant: self.view.frame.size.width).isActive = true
let backImageView: UIImageView = UIImageView()
backImageView.isUserInteractionEnabled = true
backImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(backButtonClicked)))
backImageView.image = UIImage(named: "backButton")
backImageView.contentMode = .scaleAspectFit
backImageView.clipsToBounds = true
topView.addSubview(backImageView)
backImageView.leftAnchor.constraint(equalTo: topView.leftAnchor).isActive = true
backImageView.topAnchor.constraint(equalTo: topView.topAnchor).isActive = true
backImageView.bottomAnchor.constraint(equalTo: topView.bottomAnchor).isActive = true
backImageView.centerYAnchor.constraint(equalTo: topView.centerYAnchor).isActive = true
topView.addSubview(titleText)
titleText.centerXAnchor.constraint(equalTo: topView.centerXAnchor).isActive = true
titleText.centerYAnchor.constraint(equalTo: topView.centerYAnchor).isActive = true
I think your problem is in the backImageView Y constraint
backImageView.centerYAnchor.constraint(equalTo: topView.centerYAnchor).isActive = true
should be
backImageView.centerXAnchor.constraint(equalTo: topView.centerXAnchor).isActive = true
the Y is for vertical axix, so in your 4 contraints you have 3 vertical constrains, and 1 horizontal constraint. Replace it with a centerXAnchor and you should be good.

Creating view with overlapping circle

I'm trying to create a simple UIView line and then a circle on top if it. i'm however not sure how i'm suppose to draw the circle without making hierarchy problems? so far i have below, which jut creates the line
line
self.stepLine = UIView()
self.stepLine.translatesAutoresizingMaskIntoConstraints = false
stepView.addSubview(self.stepLine)
self.stepLine.backgroundColor = Color.theme.value
self.stepLine.bottomAnchor.constraint(equalTo: stepView.bottomAnchor, constant: -4).isActive = true
self.stepLine.heightAnchor.constraint(equalToConstant: 6).isActive = true
self.stepLine.leftAnchor.constraint(equalTo: stepView.leftAnchor).isActive = true
override func layoutSubviews() {
self.stepLine.widthAnchor.constraint(equalToConstant: self.frame.width/2).isActive = true
}
Illustration
let circleView = UIView()
circleView.layer.cornerRadius = 10 // half of the width/height dimension
circleView.translatesAutoresizingMaskIntoConstraints = false
stepView.addSubview(circleView)
circleView.centerYAnchor.constraint(equalTo: stepLine.centerYAnchor).isActive = true
circleView.trailingAnchor.constraint(equalTo: stepLine.trailingAnchor).isActive = true
circleView.widthAnchor.constraint(equalToConstant: 20).isActive = true
circleView.heightAnchor.constraint(equalToConstant: 20).isActive = true
Or some variation thereof. But this is the general concept I would suggest given the nature of the code you presented.

Set X and Y values of UILabel using NSLayout

I am adding a UI Label and I am trying out programmatic UI code. I set up my label using this code:
let titleLabel: UILabel = {
let lb = UILabel()
lb.translatesAutoresizingMaskIntoConstraints = false
lb.textAlignment = .center
lb.numberOfLines = 1
lb.textColor = UIColor.black
lb.font=UIFont.systemFont(ofSize: 22)
lb.backgroundColor = UIColor.white
return lb
and then added constraints using this code:
func setupTitleLabel() {
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
titleLabel.widthAnchor.constraint(equalToConstant: 200).isActive = true
titleLabel.centerXAnchor.constraint(equalTo: titleLabel.superview!.centerXAnchor).isActive = true
titleLabel.centerYAnchor.constraint(equalTo: titleLabel.superview!.centerYAnchor).isActive = true
}
I call this function later on. However, I am not sure how to change to X and Y values. Currently, it is set to the middle of the page for both X and Y, but how would I move it up to the top.
As I said in my comment, you are using titleLabel.superview!.centerYAnchor wrongly if you need pin your label to top of your label superview you need use titleLabel.superview!.topAnchor instead
replace this
titleLabel.centerYAnchor.constraint(equalTo: titleLabel.superview!.centerYAnchor).isActive = true
by this
titleLabel.centerYAnchor.constraint(equalTo: titleLabel.superview!.topAnchor).isActive = true
What I understand the case is that the label is in the centerX and centerY, and you want it to be at the top of the superView later.If so,I think you can use var centerYAnchor: NSLayoutConstraint?
centerYAnchor = titleLabel.centerYAnchor.constraint(equalTo: titleLabel.superview!.centerYAnchor)
centerYAnchor.isActive = true
And when you want to change the label at the top later,you can set centerYAnchor.isActive = false and set the label's topAnchor.
titleLabel.topAnchor.constraint(equalTo: titleLabel.superview!.topAnchor).isActive = true

Swift, constraint, UILabel, anchor right

Desired...
For some reason the following doesn't work:
extension UIView {
func addWordOnRight() {
let l = UILabel()
//l.frame = self.bounds ..?
l.textAlignment = .right
l.text = "blah"
self.addSubview(l)
let m = self.layoutMarginsGuide
l.trailingAnchor.constraint(equalTo: m.trailingAnchor, constant: 0).isActive = true
l.centerYAnchor.constraint(equalTo: m.centerYAnchor, constant: 0).isActive = true
}
}
And if you l.frame = self.bounds, it mysteriously anchors to the left, not right.
By default the label has translatesAutoresizingMaskIntoConstraints set to true, so the view automatically adds constraints. When you add your own constraints these conflict with those already added. (You should be seeing lots of warnings in your console.)
If you want to use autolayout set l.translatesAutoresizingMaskIntoConstraints = false. Your constraints should now work correctly.
In case you also want to set the width and height of the label, set those using constraints as well (leave the frame alone).

Resources