UIScrollView inside of UIViewController not scrolling horizontally - ios

I have created a UIScrollView inside of a UIViewController. I have added a UIView inside of my UIScrollView with one label inside of the UIView. The plan is to eventually add 3 very large UIButtons that is why I need a scroll view. I have not used storyboard and it is done all programmatically. I cannot get the UIScrollView to work.
class ChooseCategoryPostViewController: UIViewController, UIScrollViewDelegate {
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.backgroundColor = .green
return sv
}()
let myView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
let test: UILabel = {
let label = UILabel()
label.text = "test label"
label.font = UIFont.boldSystemFont(ofSize: 15)
label.textAlignment = .center
return label
}()
override func viewDidLoad() {
view.addSubview(scrollView)
scrollView.addSubview(myView)
scrollView.anchor(top: topTitle.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 200, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: view.frame.width, height: 200)
myView.anchor(top: scrollView.topAnchor, left: scrollView.leftAnchor, bottom: scrollView.bottomAnchor, right: nil, paddingTop: 0, paddingLeft: 10, paddingBottom: 0, paddingRight: 0, width: 1700, height: 200)
myView.addSubview(test)
test.anchor(top: myView.topAnchor, left: nil, bottom: nil, right: myView.rightAnchor, paddingTop: 20, paddingLeft: 0, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
}
override func viewDidLayoutSubviews() {
scrollView.isScrollEnabled = true
// Do any additional setup after loading the view
scrollView.contentSize = CGSize(width: view.frame.width, height: 200)
}

You need to make sure that the width of the scroll view contentSize is greater than the width of the scroll view itself. Something as simple as the following should cause horizontal scrolling to happen:
override func viewDidLayoutSubviews() {
scrollView.isScrollEnabled = true
// Do any additional setup after loading the view
scrollView.contentSize = CGSize(width: view.frame.width * 2, height: 200)
}

Related

How to mask a View with many sub views in Swift?

Masks in Swift have caused me to pull out all my hair, I'm now in the #baldclub.
I have a menu view that contains many sub views which also have many subviews. I need to mask the entire menu view so that the menu view can be animated to slide out under a button.
I have tried to simplify the problem with one container view and one subview and still have not been able to mask the views! Perhaps masks are afraid of auto-layout?
Here are the views:
let curtainMask: UIView = {
let view = UIView()
view.backgroundColor = .green
view.isUserInteractionEnabled = false
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let curtainContainer: UIView = {
let view = UIView()
view.backgroundColor = .clear
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let locationImage: UIImageView = {
let view = UIImageView()
//view.layer.cornerRadius = 100
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .clear
view.alpha = 1
view.image = UIImage(systemName: "mappin")?.withRenderingMode(.alwaysOriginal)
view.contentMode = .scaleAspectFit
return view
}()
Here is the code being run on the main thread:
view.addSubview(curtainContainer)
curtainContainer.backgroundColor = .lightGray
curtainContainer.anchor(top: nil, left: nil, bottom: nil, right: nil, centerX: view.centerXAnchor, centerY: view.centerYAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 300, height: 200)
curtainContainer.addSubview(locationImage)
locationImage.anchor(top: curtainContainer.topAnchor, left: curtainContainer.leftAnchor, bottom: curtainContainer.bottomAnchor, right: curtainContainer.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
view.addSubview(curtainMask)
curtainMask.backgroundColor = .green
curtainMask.anchor(top: nil, left: nil, bottom: nil, right: nil, centerX: view.centerXAnchor, centerY: view.centerYAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 200, height: 100)
view.layoutSubviews()
view.setNeedsLayout()
curtainContainer.mask = curtainMask
This code crates a large gray rectangle with an image as a subview. The mask is a smaller rectangle inside the large gray rectangle. I would expect the curtainContainer view to be masked along with the image subview but the result is that nothing is drawn. Any ideas?

Custom buttons can't be tapped Swift

I have created 5 custom buttons all in code, and they all show up perfectly; however, for some reason, there is no response when tapped like they are inactive. I have added the subviews in my ViewDidLoad and coded the constraints in a separate function that's not important I don't believe to this so let me know if that's needed to better your understanding.
The code:
let backgroundImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.masksToBounds = true
return imageView
}()
let transparentView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.black
view.backgroundColor = view.backgroundColor?.withAlphaComponent(0.5)
return view
}()
let button1: UIButton = {
let button = UIButton(type: .custom)
button.addTarget(self, action: #selector(button1Pressed), for: .touchUpInside)
button.setImage(UIImage(named: "button"), for: .normal)
button.isUserInteractionEnabled = true
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
addSubviews()
setupViewConstraints()
}
func addSubviews() {
self.view.addSubview(backgroundImageView)
backgroundImageView.addSubview(transparentView)
transparentView.addSubview(button1)
}
func setupViewConstraints() {
backgroundImageView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
transparentView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
button1.anchor(top: nil, left: nil, bottom: view.bottomAnchor, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 20, paddingRight: 0, width: 80, height: 80)
button1.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
#objc func button1Pressed() {
print("Button1 pressed")
}
By default, user interaction is disabled in UIImageView instances. And when a superview's user interaction is disabled, its subviews can't receive the user interaction, even if you have set button.isUserInteractionEnabled = true
So add imageView.isUserInteractionEnabled = true in backgroundImageView closure
let backgroundImageView: UIImageView = {
let imageView = UIImageView()
imageView.isUserInteractionEnabled = true
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.masksToBounds = true
return imageView
}()

UIScrollView not scrolling - ambiguous content size

I am trying to create a Scroll View. The view itself is correctly
displayed but it does not scroll, even though I added a view to it and
set the content size.
NOTE: I am not using storyboards but Constraints (AutoLayout) only.
Code:
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.isScrollEnabled = true
sv.backgroundColor = .brown
return sv
}()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
func setupViews() {
self.view.addSubview(scrollView)
scrollView.anchor(top: self.view.topAnchor, left: self.view.leftAnchor, bottom: self.view.bottomAnchor, right: self.view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
scrollView.contentSize = CGSize(width: self.view.frame.width, height: 2000)
print(scrollView.contentSize)
let view = UIView()
view.backgroundColor = .blue
scrollView.addSubview(view)
view.anchor(top: scrollView.topAnchor, left: scrollView.leftAnchor, bottom: scrollView.bottomAnchor, right: scrollView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
}
.anchor() is a custom extension I made in order to make constraining views easier.
Any help would be appreciated.
It's better not to mix autolayout with frame layout do
1- Comment
scrollView.contentSize = CGSize(width: self.view.frame.width, height: 2000)
2- set equal width constraint between the view inside the scrollview to the vc's view and a height constraint of 2000
For no confuse with vc's view change
let view = UIView()
to
let contentView = UIView()
then add these 2 constarints
contentView.widthAnchor.constraint(equalTo:self.view.widthAnchor).isActive = true
contentView.heightAnchor.constraint(equalToConstant:2000).isActive = true

View's Frame get called twice and causes clipping bug

I have a base UIView that has a visualEffectView as it's subview. I set the visualEffectView's view frame.height as 80. When presenting to ViewController, I want to reset its height to 120. Currently, when presented, the height is initially 120 but then quickly clips back down to 80. Shouldn't my frame in my ViewController override the frame set in the UIView? My frame in the ViewController gets called first but later it gets called from the UIView, causing the clipping bug.
class CardView: UIView {
let visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
override init(frame: CGRect) {
super.init(frame: frame)
clipsToBounds = true
addSubview(visualEffectView)
}
override func layoutSubviews() {
super.layoutSubviews()
visualEffectView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 80)
}
}
class ViewController: ViewController {
let cardView: CardView = {
let cv = CardView()
cv.imageView.clipsToBounds = true
cv.layer.cornerRadius = 18
return cv
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let superviewFrame = view.frame.height
cardView.frame = CGRect(x: 10, y: superviewFrame / 8, width: view.frame.width - 20, height: superviewFrame - (superviewFrame / 5))
cardView.visualEffectView.anchor(top: cardView.topAnchor, left: cardView.leftAnchor, bottom: nil, right: cardView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 120)
}
}
try moving this code
visualEffectView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 80)
from override func layoutSubviews() { to override init(frame: CGRect) {
after super.init()
You may need a guard condition and if there is a constraint, just pass.
override func layoutSubviews() {
super.layoutSubviews()
guard visualEffectView.constraintsAffectingLayout(for: .vertical).count == 0 else {return}. // Here
visualEffectView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 80)
}

Have autosizing text in a viewcontroller

Can someone tell my why this code does not autosize the text in the descLabel? I tried to set the numberOfLines to 0 and I set a lineBreakMode but this pretty much does nothing to solve my problem. Is this approach not possible in a ViewController or do I do something completely wrong?
class SingleEventViewController: UIViewController {
var thisEvent: Event
var eventDescription: String?
let descLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 16)
label.text = "Beschreibung"
label.textColor = .black
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
return label
}()
let participateButton: UIButton = {
let button = UIButton()
button.backgroundColor = CalendarSettings.Colors.buttonBG
button.setTitle("Teilnehmen", for: .normal)
button.setTitleColor(CalendarSettings.Colors.darkRed, for: .normal)
return button
}()
//MARK: - Init & View Loading
init(event: Event) {
thisEvent = event
super.init(nibName: nil, bundle: nil)
setupDefaultValues()
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
applyDefaultValues()
setupViews()
confBounds()
}
//MARK: - Setup
func setupDefaultValues() {
eventDescription = thisEvent.eventDescription
}
func applyDefaultValues() {
descLabel.text = eventDescription
}
func setupViews() {
view.addSubview(descLabel)
view.addSubview(participateButton)
let tabbarHeight = self.tabBarController?.tabBar.frame.height ?? 0
descLabel.anchor(top: titleLabel.bottomAnchor, left: view.leftAnchor, bottom: nil, right: nil, paddingTop: 5, paddingLeft: 10, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
participateButton.anchor(top: nil, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: tabbarHeight, paddingRight: 0, width: 0, height: 50)
}
To make it autoresize you must give it a left/leading and right/trailing constraints , or a fixed width
descLabel.anchor(top: titleLabel.bottomAnchor, left: view.leftAnchor, bottom: nil, rightview.rightAnchor, paddingTop: 5, paddingLeft: 10, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
If it is the font size you are trying to change so that it fits in the label then try adjustFontSizeToWidth, but if it is the label you wish to resize try sizeToFit().

Resources