Programmatically adding UILabels and UIButtons to UIScrollView in Swift - ios

My goal is to programmatically create multiple UILabels and UIButtons followed by programmatically creating and inserting them into a UIScrollView.
I am operating within the following class.
class ViewController: UIViewController {
I have first created my scrollView shown below.
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
createTitleBar() //Function that creates fixed buttons and labels not included in scrollView
createUpperTabs() //Function that creates fixed buttons and labels not included in scrollView
createLowerNavBars() //Function that creates fixed buttons and labels not included in scrollView
for _ in 0...10 {
createTemplate() //This function consists of nested functions that creates labels and buttons that are then added to the scrollView
}
scrollView = UIScrollView(frame: view.bounds)
scrollView.backgroundColor = UIColor.black
scrollView.contentSize = CGSize(width: screenSize.width, height: currentStackY)
scrollView.autoresizingMask = UIViewAutoresizing.flexibleHeight
view.addSubview(scrollView)
}
After creating the labels and buttons, I add the views individually to the scrollView with following statement
self.scrollView.addSubview(UILabel or UIButton Name).
Here are some snippets showing how I am creating my UILabel and UIButton.
let titleHeader = UILabel()
self.scrollView.addSubview(titleHeader)
currentStackY += 8
titleHeader.backgroundColor = .gray
titleHeader.text = headerTitle
titleHeader.font = UIFont(name: buttonFont, size: buttonFontSize)
let labelWidth = titleHeader.intrinsicContentSize.width + 16
titleHeader.snp.makeConstraints { (make) in
make.height.equalTo(titleHeader)
make.width.equalTo(titleHeader)
make.top.equalTo(currentStackY)
make.left.equalTo(view).offset(8)
}
as well as
for i in 0...chiefComplaintArray.count - 1 {
let btn = UIButton()
chiefComplaintButton.append(btn)
buttonDictionary[chiefComplaintButton[i]] = numberButtonPressed
chiefComplaintButton[i].setTitle(chiefComplaintArray[i], for: .normal)
chiefComplaintButton[i].setTitleColor(UIColor.black, for: .normal)
chiefComplaintButton[i].titleLabel!.font = UIFont(name: buttonFont, size: buttonFontSize)
chiefComplaintButton[i].backgroundColor = UIColor.gray
chiefComplaintButton[i].sizeToFit()
let buttonWidth = chiefComplaintButton[i].frame.width + 10
let buttonHeight = chiefComplaintButton[i].frame.height + 5
if i == 0 {
currentStackX = 20
currentStackY += 5
} else if (currentStackX + buttonWidth) > screenSize.width {
currentStackX = 20
currentStackY += buttonHeight + 5
}
chiefComplaintButton[i].frame = CGRect(x: currentStackX, y: currentStackY, width: buttonWidth, height: buttonHeight)
currentStackX += chiefComplaintButton[i].frame.width + 5
if i == chiefComplaintArray.count - 1 {
currentStackY += chiefComplaintButton[i].frame.height
}
self.scrollView.addSubview(chiefComplaintButton[i])
chiefComplaintButton[i].addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside)
}
I used similar statements to add all of the created content to the scrollView. I have modeled my UIScrollView after other solutions in the stacks as well as other online references. Thanks in advance for your time and consideration.
I have subsequently tried
let titleHeader = UILabel()
scrollView.addSubview(titleHeader)
self.view.addSubview(scrollView)
currentStackY += 8
titleHeader.backgroundColor = .gray
titleHeader.text = headerTitle
titleHeader.font = UIFont(name: buttonFont, size: buttonFontSize)
let labelWidth = titleHeader.intrinsicContentSize.width + 16
Still getting the same error.

The issue with your code is you are initializing your scrollView after you are calling the method in which you are getting crash,because at that time your scrollView is nil.
You need to call your method after
view.addSubview(scrollView)
Edit
override func viewDidLoad() {
super.viewDidLoad()
for _ in 0...10 {
createTemplate() //This function consists of nested functions that creates labels and buttons that are then added to the scrollView
}
scrollView = UIScrollView(frame: view.bounds)
scrollView.backgroundColor = UIColor.black
scrollView.contentSize = CGSize(width: screenSize.width, height: currentStackY)
scrollView.autoresizingMask = UIViewAutoresizing.flexibleHeight
view.addSubview(scrollView)
createTitleBar() //Function that creates fixed buttons and labels not included in scrollView
createUpperTabs() //Function that creates fixed buttons and labels not included in scrollView
createLowerNavBars() //Function that creates fixed buttons and labels not included in scrollView
}
Also from here
let titleHeader = UILabel()
scrollView.addSubview(titleHeader)
self.view.addSubview(scrollView)
currentStackY += 8
titleHeader.backgroundColor = .gray
titleHeader.text = headerTitle
titleHeader.font = UIFont(name: buttonFont, size: buttonFontSize)
let labelWidth = titleHeader.intrinsicContentSize.width + 16
remove this
self.view.addSubview(scrollView)

Related

How do you ignore the safe area in UIKit?

I know you use ignoresSafeArea() in SwiftUI to make some UI-element-frame dismiss the safe area. However, when using UIKit I don't know how to do it. I want to place a UILabel right below the actual screen, but I cannot see it because it is hidden behind the safe area.
import UIKit
class SignInViewController: UIViewController {
private let headline = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
addLabel()
view.backgroundColor = .black
}
func addLabel() {
headline.text = "SmashPass"
headline.textColor = UIColor.red
headline.frame = CGRect(x: 0 , y: 0 , width: 243, height: 29)
headline.textAlignment = .center
// alignment
headline.center.x = view.center.x
headline.center.y = view.frame.minY - 10
view.addSubview(headline)
}
}
Learn about how to use Auto-Layout...
Here is your code, with modifications to use Constraints and Auto-Layout to place the label at the bottom of the view:
class SignInViewController: UIViewController {
private let headline = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
addLabel()
view.backgroundColor = .black
}
func addLabel() {
headline.text = "SmashPass"
headline.textColor = UIColor.red
headline.textAlignment = .center
// -- use Auto-Layout!
// alignment
//headline.frame = CGRect(x: 0 , y: 0 , width: 243, height: 29)
//headline.center.x = view.center.x
//headline.center.y = view.frame.minY - 10
view.addSubview(headline)
headline.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// label uses full-width
headline.leadingAnchor.constraint(equalTo: view.leadingAnchor),
headline.trailingAnchor.constraint(equalTo: view.trailingAnchor),
// constrain the label bottom to the view bottom
headline.bottomAnchor.constraint(equalTo: view.bottomAnchor),
// label height is 29-points
headline.heightAnchor.constraint(equalToConstant: 29.0),
])
}
}
Note: in general, we want to constrain elements with awareness of the Safe-Area...
Using that code, we'll get this on an iPhone 12 Pro (for example):
and, if we set headline.backgroundColor = .yellow so we can see the label's frame:

UIScrollView for ViewController horizontally and vertically

My code tutorial works for scrolling horizontally with ScrollView.
I would like to make it with ViewControllers (instead of views) from my Storyboard.
And I would like to add a vertical ScrollView (To move from center to right or left = ScrollView horizontal, and to move from center to top or bottom (vertically).
Is that possible? Could someone give me a hint ?
Thanks
For information, this is my code :
import UIKit
class ViewController: UIViewController {
lazy var view0: UIView = {
let view = UIView()
view.backgroundColor = .systemTeal
let label = UILabel()
label.text = "Page 0"
label.textAlignment = .center
view.addSubview(label)
label.edgeTo(view: view)
return view
}()
lazy var view1: UIView = {
let view = UIView()
view.backgroundColor = .systemPink
let label = UILabel()
label.text = "Page 1"
label.textAlignment = .center
view.addSubview(label)
label.edgeTo(view: view)
return view
}()
lazy var view2: UIView = {
let view = UIView()
view.backgroundColor = .systemYellow
let label = UILabel()
label.text = "Page 2"
label.textAlignment = .center
view.addSubview(label)
label.edgeTo(view: view)
return view
}()
lazy var views = [view0, view1, view2]
lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.showsHorizontalScrollIndicator = false
scrollView.isPagingEnabled = true
scrollView.contentSize = CGSize(width: view.frame.width * CGFloat(views.count), height: view.frame.height)
for i in 0..<views.count {
scrollView.addSubview(views[i])
views[i].frame = CGRect(x: view.frame.width * CGFloat(i), y: 0, width: view.frame.width, height: view.frame.height)
}
scrollView.delegate = self
return scrollView
}()
lazy var pageControl: UIPageControl = {
let pageControl = UIPageControl()
pageControl.numberOfPages = views.count
pageControl.currentPage = 0
pageControl.addTarget(self, action: #selector(pageControlTapHandler(sender:)), for: .touchUpInside)
return pageControl
}()
#objc
func pageControlTapHandler(sender: UIPageControl) {
scrollView.scrollTo(horizontalPage: sender.currentPage, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.addSubview(scrollView)
scrollView.edgeTo(view: view)
view.addSubview(pageControl)
pageControl.pinTo(view)
}
}
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageIndex = round(scrollView.contentOffset.x / view.frame.width)
pageControl.currentPage = Int(pageIndex)
}

Xcode 10 - Scroll View is not allowing scrolling when adding button programmatically

I'm getting data from a database and trying to display the data as buttons, I have got this working but when I run the app on the Xcode simulator the buttons are showing and I can see they are off the screen but I can't scroll.
I have tried adding a "view" inside the "scroll view" with a fixed height and then adding the buttons to that view and still nothing.
// Adding the scroll view and the view
#IBOutlet weak var scroll: UIScrollView!
#IBOutlet weak var content: UIView!
// Button Settings
var buttonX = 100
var buttonY = 0
let buttonWidth = 200
let buttonHeight = 50
let image = UIImage(named: "button") as UIImage?
override func viewDidLoad() {
super.viewDidLoad()
for element in contentsArray {
// Adding a button for each client name
let button = UIButton(type: .system)
button.setTitle(element, for: .normal)
button.tintColor = .white
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 22)
button.setBackgroundImage(image, for: .normal)
button.centerXAnchor.constraint(equalTo: button.centerXAnchor).isActive = true
button.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)
button.frame = CGRect(x: buttonX, y: buttonY, width: buttonWidth, height: buttonHeight)
content.addSubview(button)
buttonY = buttonY + 70
}
}
I am not getting any errors in the console when running the app.
Once your finish adding buttons, set the scrollView.contentSize.height to your calculated buttonY
scrollView.contentSize.width = self.view.frame.size.width // non scrollable content width
for element in contentsArray {
...
buttonY = buttonY + 70
}
scrollView.contentSize.height = buttonY // scrollable content height

Swift: Make vertical scrolling feed with programmatically sized UIViews

I'm building a scrolling feed in my app with data grabbed from a database (firebase). I'm not very experienced in Swift as most of the stuff I do is web design. What I'm looking for is a good way to size the height of my UIViews. Here is what I currently have (fixed height):
Here's my code UIView class:
class eventView: UIView {
let eventDate : UILabel = {
let eventDate = UILabel()
eventDate.translatesAutoresizingMaskIntoConstraints = false
eventDate.numberOfLines = 0
eventDate.textAlignment = .center
return eventDate
}()
let eventTitle : UILabel = {
Same thing as eventDate
}()
let eventDesc : UILabel = {
same thing as eventDate
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.lightGray
self.layer.cornerRadius = 10
self.addSubview(eventDate)
eventDate.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
eventDate.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
self.addSubview(eventTitle)
eventTitle.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
eventTitle.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
eventTitle.topAnchor.constraint(equalTo: eventDate.bottomAnchor).isActive = true
self.addSubview(eventDesc)
eventDesc.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
eventDesc.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
eventDesc.topAnchor.constraint(equalTo: eventTitle.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is my for list that displays these UIViews:
var i = 0
for data in self.eventViewData {
let view = eventView(frame: CGRect(x: 0, y: ( CGFloat(170 * i)), width: self.scrollView.frame.width - 20, height: CGFloat(150)))
view.center.x = self.scrollView.center.x
view.eventDate.text = data.date
view.eventTitle.text = data.title
view.eventDesc.text = data.description
self.scrollView.addSubview(view)
i += 1
}
I usually am using HTML div's and such so I'm having a hard time figuring out how to style these. Any information or links to tutorials on how to programmatically adjust constraints to the UILabels in my eventViews are also appreciated.
How to calculate the desired height seems to be the question. While there are a lot of ways to do this one approach would be something like this:
func heightForView(text:String, font:UIFont, width:CGFloat) -> CGFloat{
let label:UILabel = UILabel(frame: CGRectMake(0, 0, width, CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
let font = UIFont(name: "Helvetica", size: 20.0)
var height = heightForView("This is just a load of text", font: font, width: 100.0)
// apply height to desired view
I prefer to put the heightForView function in an extension myself but it isn't required.
I assumed you're using table view, why not do dynamic height? Good reference https://www.raywenderlich.com/1067-self-sizing-table-view-cells
In essense, you use auto layout to define your top and bottom constraint accordingly for every element in your cell, and your table view delegation/data source method for heightforrow and estimatedheightforrow, return UITableViewAutomaticDimension

UIScrollView doesn't scroll and doesn't appear on the right position

I need a UIScrollView and a containerView to scroll the content I create. My code for that 2 items is:
var scrollView = UIScrollView()
var darkener = UIView()
var container = UIView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.clearColor()
darkener = UIView(frame: self.view.frame)
darkener.backgroundColor = UIColor.blackColor()
darkener.alpha = 0.0
self.view.addSubview(darkener)
scrollView.frame = self.view.bounds
scrollView.backgroundColor = UIColor.clearColor()
scrollView.contentSize = CGSizeMake(scrollView.frame.width, scrollView.frame.height+1)
scrollView.delegate = self
self.view.addSubview(scrollView)
container = UIView(frame: CGRectMake(0, scrollView.frame.height, scrollView.frame.width, scrollView.frame.height))
container.backgroundColor = UIColor.whiteColor()
container.layer.masksToBounds = true
container.layer.cornerRadius = 10
scrollView.addSubview(container)
}
The problem is that the container view once I Set the Y point to scrollView.frame.height doesn't appear on the screen (it's on the bottom), but in a previous VC that code worked perfectly.
Also, it does not let me scroll on the content. What's wrong there?
If necessary I can upload the implementation, but this I think has no problem.
Solved. I just had to put that at the end of the ViewDidLoad to declare de frame:
The textField is the final label I have on the screen, to declare the limit of the screen.
self.container.frame = CGRectMake(0, self.container.frame.origin.y, self.container.frame.width, textField2.frame.origin.y + textField2.frame.height - 60)
self.scrollView.contentSize = CGSizeMake(self.container.frame.width, self.container.frame.height + 20.0)
And works Ok now.

Resources