I have custom view with some labels, a text field and a button. When I add it to a view in a VC, the custom view appears nicely, but I can't tap on the text field or on the button, they are not responding. Can someone tell my what is the problem with the code? Here is the simplified version:
import UIKit
class CustomView: UIView {
let title: UILabel = {
let title = UILabel()
title.font = UIFont.systemFont(ofSize: 24)
title.text = "Title"
title.numberOfLines = 0
title.textAlignment = .center
title.translatesAutoresizingMaskIntoConstraints = false
return title
}()
let textView: UITextField = {
let textView = UITextField()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.placeholder = "Placeholder text"
textView.backgroundColor = UIColor.lightGray
textView.layer.cornerRadius = 10
return textView
}()
let searchButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.blue
button.setTitle("Tap me", for: UIControl.State.normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
return button
}()
private func setupView() {
addSubview(title)
addSubview(textView)
addSubview(searchButton)
NSLayoutConstraint.activate([
title.topAnchor.constraint(equalTo: self.topAnchor, constant: 100),
title.rightAnchor.constraint(equalTo: self.rightAnchor),
title.leftAnchor.constraint(equalTo: self.leftAnchor),
])
NSLayoutConstraint.activate([
textView.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 20),
textView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
textView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
textView.heightAnchor.constraint(equalToConstant: 60)
])
NSLayoutConstraint.activate([
searchButton.topAnchor.constraint(equalTo: textView.bottomAnchor, constant: 20),
searchButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
searchButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
searchButton.heightAnchor.constraint(equalToConstant: 60)
])
}
#objc func buttonAction(_ sender:UIButton!)
{
print("Button tapped")
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setupView()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
import UIKit
class ViewController: UIViewController {
private let customView = CustomView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
customView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
customView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
customView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
}
Basically I want to have a seperate file with the view that has all the design and I just want to drop that into the VC without doing anything special with it. Is this even a good approach? Where should I set up the button action? I mean once its tappable...
Thanks!
Try this first - at the end of viewDidLoad(), add this line:
customView.backgroundColor = .red
When you run the app, you'll notice there is no red box.
Now, add this line after that one:
customView.clipsToBounds = true
Run it again, and... we see nothing!
The problem is, you haven't given your customView any height.
To fix it, constrain the bottom of searchButton in your custom view class:
NSLayoutConstraint.activate([
searchButton.topAnchor.constraint(equalTo: textView.bottomAnchor, constant: 20),
searchButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
searchButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
searchButton.heightAnchor.constraint(equalToConstant: 60),
// add this line!
searchButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20),
])
You need to set userInteractionEnabled to true on your CustomView instance so that it passes through taps.
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
customView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
customView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
customView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
customView.userInteractionEnabled = true
}
You can use a delegation pattern or a closure property to pass the button tap event back to the containing view controller.
For example,
class CustomView: UIView {
var searchTappedHandler: ((CustomView)->Void)?
#objc func buttonAction(_ sender:UIButton!)
{
print("Button tapped")
self.searchTappedHandler?(self)
}
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
customView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
customView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
customView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
customView.userInteractionEnabled = true
customView.searchTappedHandler = { _ in
print("Search button was tapped")
}
}
Or, if you want to use a function rather than an inline closure
func handleTap(_ customView: CustomView) {
print("tap")
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
customView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
customView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
customView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
customView.userInteractionEnabled = true
customView.searchTappedHandler = handleTap
}
Related
I have an iOS application where i have a textfield and a button and on tap of button i have to hide the textfield.
I am setting heightAnchor to 0 on tap of button. Everything is working fine on iOS 14(14.5) but does not work(does not hide the text field) on iOS 15. Also, I have tried setting up the isHidden property on UITextField but it does not work.
Can you please help tell if something changed or i am doing something wrong. Thank you.
Code reference:
import UIKit
class ViewController: UIViewController {
private lazy var mytextFeild: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.text = "Hello world"
textField.backgroundColor = .green
return textField
}()
private lazy var testView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
private lazy var button: UIButton = {
let view = UIButton()
view.backgroundColor = .blue
view.setTitle("hide it", for: .normal)
view.translatesAutoresizingMaskIntoConstraints = false
view.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
return view
}()
var heightConstraint: NSLayoutConstraint?
#objc func buttonTapped() {
heightConstraint?.isActive = false
heightConstraint = mytextFeild.heightAnchor.constraint(equalToConstant: 0)
heightConstraint?.isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mytextFeild)
view.addSubview(testView)
view.addSubview(button)
mytextFeild.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32).isActive = true
mytextFeild.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32.0).isActive = true
mytextFeild.topAnchor.constraint(equalTo: view.topAnchor, constant: 64).isActive = true
heightConstraint = mytextFeild.heightAnchor.constraint(equalToConstant: 32.0)
heightConstraint?.isActive = true
button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32.0).isActive = true
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32.0).isActive = true
button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -64.0).isActive = true
testView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
testView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
testView.topAnchor.constraint(equalTo: mytextFeild.bottomAnchor).isActive = true
testView.bottomAnchor.constraint(equalTo: button.topAnchor).isActive = true
}
}
Add them to a stack then add stack to the viewController. at the end try to hide it easily without changing the height.
class ViewController: UIViewController {
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .center
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
private lazy var myTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.text = "Hello world"
textField.backgroundColor = .green
return textField
}()
private lazy var testView: UIView = {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
private lazy var button: UIButton = {
let view = UIButton()
view.backgroundColor = .blue
view.setTitle("hide it", for: .normal)
view.translatesAutoresizingMaskIntoConstraints = false
view.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
return view
}()
#objc func buttonTapped() {
myTextField.isHidden = !myTextField.isHidden
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
stackView.addArrangedSubview(myTextField)
stackView.addArrangedSubview(testView)
stackView.addArrangedSubview(button)
view.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -64.0).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
myTextField.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
myTextField.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64.0).isActive = true
testView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
button.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64.0).isActive = true
}
}
I have a ViewController and another UIView called MySubview which contains a simple button.
If I add MySubview into ViewController the button is unclickable. However if I put the button directly inside ViewController everything works as expected.
Example:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = MySubview()
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
}
}
class MySubview: UIView {
let button: UIButton = {
let button = UIButton()
button.setTitle("MyButton", for: .normal)
button.setTitleColor(.label, for: .normal)
button.addTarget(self, action: #selector(myfunc), for: .touchUpInside)
return button
}()
#objc func myfunc() {
print("clicked")
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
button.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
When I click on the button I do not see printed message.
Your code is confused, if I understand well take a look to my code below, declare your button and your view under your controller class:
class ViewController: UIViewController {
let button: UIButton = {
let button = UIButton()
button.setTitle("MyButton", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .black
button.addTarget(self, action: #selector(myfunc), for: .touchUpInside)
return button
}()
let mysubview = UIView()
...
now in viewDidLoad set constraints like this:
override func viewDidLoad() {
super.viewDidLoad()
mysubview.backgroundColor = .red
view.addSubview(mysubview)
mysubview.translatesAutoresizingMaskIntoConstraints = false
mysubview.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
mysubview.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
mysubview.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
mysubview.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
mysubview.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.topAnchor.constraint(equalTo: mysubview.topAnchor, constant: 20).isActive = true
button.leadingAnchor.constraint(equalTo: mysubview.leadingAnchor, constant: 20).isActive = true
button.trailingAnchor.constraint(equalTo: mysubview.trailingAnchor, constant: -20).isActive = true
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
now add your func
#objc func myfunc() {
print("clicked")
}
complete code:
import UIKit
class ViewController: UIViewController {
let button: UIButton = {
let button = UIButton()
button.setTitle("MyButton", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .black
button.addTarget(self, action: #selector(myfunc), for: .touchUpInside)
return button
}()
let mysubview = UIView()
override func viewDidLoad() {
super.viewDidLoad()
mysubview.backgroundColor = .red
view.addSubview(mysubview)
mysubview.translatesAutoresizingMaskIntoConstraints = false
mysubview.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
mysubview.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
mysubview.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
mysubview.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
mysubview.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.topAnchor.constraint(equalTo: mysubview.topAnchor, constant: 20).isActive = true
button.leadingAnchor.constraint(equalTo: mysubview.leadingAnchor, constant: 20).isActive = true
button.trailingAnchor.constraint(equalTo: mysubview.trailingAnchor, constant: -20).isActive = true
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
#objc func myfunc() {
print("clicked")
}
}
If your subview has a frame of size zero, you won't be able to tap the button even if you can see it. Using a view debugger from Xcode will show you if this is the issue.
Also, check if the subview has user interaction enabled when adding it.
I think , the problem is in layers (I mean, subviews). Try this instead of view.addSubview(button) :
view.insertSubview(your_button_view, at: view.subviews.count)
I declare TestClass contain button and textField and constraint it and then add this class to view controller
import Foundation
import UIKit
class TestClass : UIView {
let testButton : UIButton = {
let button = UIButton()
button.setTitle("Test", for: .normal)
button.setTitleColor(.red ,for:.normal)
return button
}()
let testInput : UITextField = {
let input = UITextField()
input.placeholder = "type here"
return input
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
func setupView(){
addSubview(testButton)
addSubview(testInput)
testButton.translatesAutoresizingMaskIntoConstraints = false
testInput.translatesAutoresizingMaskIntoConstraints = false
testButton.topAnchor.constraint(equalTo: self.topAnchor, constant: 20).isActive = true
testButton.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true
testButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
testButton.widthAnchor.constraint(equalToConstant: 50).isActive = true
testInput.topAnchor.constraint(equalTo: self.topAnchor, constant: 70).isActive = true
testInput.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true
testInput.heightAnchor.constraint(equalToConstant: 30).isActive = true
testInput.widthAnchor.constraint(equalToConstant: 100).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
in the viewDidLoad I add this class and it appeared .
but when I tap on textField or button it doesn't respond
class ViewControll : UIViewController {
let newClass : TestClass = {
let view = TestClass()
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(newClass)
newClass.frame.size = CGSize(width: 200, height: 200)
newClass.translatesAutoresizingMaskIntoConstraints = false
newClass.topAnchor.constraint(equalTo: contView.topAnchor, constant: 300).isActive = true
newClass.leadingAnchor.constraint(equalTo: contView.leadingAnchor, constant: 30).isActive = true
newClass.testButton.addTarget(self, action: #selector(prnt), for: .touchUpInside)
}
#objc func prnt(){
print("bla bla bla")
}
}
when I click on button or tap on textField nothing happened
can anyone help me please
Constrains don't define height and width of your TestClass - set frame doesn't work. You need to add constraints for this as well, for example, in viewDidLoad method:
newClass.heightAnchor.constraint(equalToConstant: 200).isActive = true
newClass.widthAnchor.constraint(equalToConstant: 200).isActive = true
Any idea why the Tap gesture on a UI Label will not fire?
I've tried using a delegate also but in its simplest form, for some reason it will just not hit the action method.
Is the UIView layer restricting this interaction?
class TestTextViewLabel : UIView {
weak var testTextView: UITextView!
weak var testLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(testLabelTapped(_:)))
let testUITextView: UITextView = {
let textView = UITextView()
textView.textColor = UIColor(hex: "#000")
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
let testUILabel: UILabel = {
let label = UILabel()
label.textColor = UIColor(hex: "#666666")!
label.translatesAutoresizingMaskIntoConstraints = false
label.addGestureRecognizer(tapGesture)
label.isUserInteractionEnabled = true
return label
}()
self.addSubview(testUITextView)
self.addSubview(testUILabel)
self.testTextView = testUITextView
self.testLabel = testUILabel
testUITextView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
testUITextView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUITextView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
testUILabel.topAnchor.constraint(equalTo: testUITextView.bottomAnchor, constant: 50).isActive = true
testUILabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUILabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func testLabelTapped(_ sender: UITapGestureRecognizer) {
print("testLabelTapped")
}
}
I tried running your class and I was able to get the UILabel to fire after putting text into it. The tap gesture is only recognized within the view's bounds and since you didn't have any text in the UILabel, the bounds were zero giving you no place to click it. By default, if you put in text, the UILabel will automatically match those bounds. Here is my working code below:
class TestTextViewLabel : UIView {
weak var testTextView: UITextView!
weak var testLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(testLabelTapped(_:)))
let testUITextView: UITextView = {
let textView = UITextView()
textView.textColor = UIColor.black
textView.translatesAutoresizingMaskIntoConstraints = false
textView.text = "This is a text view"
textView.backgroundColor = .clear
return textView
}()
let testUILabel: UILabel = {
let label = UILabel()
label.textColor = UIColor(red:0.40, green:0.40, blue:0.40, alpha:1.0)
label.translatesAutoresizingMaskIntoConstraints = false
label.addGestureRecognizer(tapGesture)
label.isUserInteractionEnabled = true
label.text = "This is a UILabel view"
return label
}()
self.addSubview(testUITextView)
self.addSubview(testUILabel)
self.testTextView = testUITextView
self.testLabel = testUILabel
testUITextView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
testUITextView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUITextView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
testUILabel.topAnchor.constraint(equalTo: testUITextView.bottomAnchor, constant: 50).isActive = true
testUILabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUILabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func testLabelTapped(_ sender: UITapGestureRecognizer) {
print("testLabelTapped")
}
}
I followed this programmatic scrollview-pageview tutorial I keep getting a crash inside the Scrollview Delegate's ScrollViewDidScroll(...)
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1030b0d98)
The crash appears on this line: pageControl.currentPage = Int(round(pageFraction))
Why does this crash keeps happening
ViewController Class:
class ViewController: UIViewController, UIScrollViewDelegate {
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
scrollView.isPagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.backgroundColor = UIColor.white
return scrollView
}()
let stackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.distribution = .equalSpacing
return stackView
}()
let pageControl: UIPageControl = {
let pageControl = UIPageControl()
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.currentPage = 0
pageControl.tintColor = UIColor.white
pageControl.pageIndicatorTintColor = UIColor.gray
pageControl.currentPageIndicatorTintColor = UIColor.red
pageControl.addTarget(self, action: #selector(pageControlTapped(sender:)), for: .valueChanged)
return pageControl
}()
var views = [UIView]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
scrollView.delegate = self
configureViewsForViewArray()
setScrollViewAnchors()
setStackViewAnchors()
setPageViewsInsideStackView()
setPageControlAnchors()
}
func configureViewsForViewArray(){
let pageView1 = PageView(headerText: "Header 1", paragraphText: "Bla bla bla", backgroundColor: .red)
views.append(pageView1)
let pageView2 = PageView(headerText: "Header 2", paragraphText: "Bla bla bla", backgroundColor: .orange)
views.append(pageView2)
let pageView3 = PageView(headerText: "Header 3", paragraphText: "Bla bla bla", backgroundColor: .blue)
views.append(pageView3)
let pageView4 = PageView(headerText: "Header 4", paragraphText: "Bla bla bla", backgroundColor: .green)
views.append(pageView4)
}
func setScrollViewAnchors(){
view.addSubview(scrollView)
if #available(iOS 11.0, *) {
scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
} else {
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
}
if #available(iOS 11.0, *) {
scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
} else {
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
}
if #available(iOS 11.0, *) {
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
} else {
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
}
if #available(iOS 11.0, *) {
scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
} else {
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
}
}
func setStackViewAnchors(){
scrollView.addSubview(stackView)
stackView.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
stackView.rightAnchor.constraint(equalTo: scrollView.rightAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
}
func setPageViewsInsideStackView(){
for eachView in self.views{
eachView.translatesAutoresizingMaskIntoConstraints = false
stackView.addArrangedSubview(eachView)
eachView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
eachView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
}
}
func setPageControlAnchors(){
view.addSubview(pageControl)
pageControl.numberOfPages = views.count
if #available(iOS 11.0, *) {
pageControl.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
} else {
// Fallback on earlier versions
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
if #available(iOS 11.0, *) {
pageControl.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20).isActive = true
} else {
// Fallback on earlier versions
pageControl.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20).isActive = true
}
}
#objc func pageControlTapped(sender: UIPageControl) {
let pageWidth = scrollView.bounds.width
let offset = sender.currentPage * Int(pageWidth)
UIView.animate(withDuration: 0.33, animations: { [weak self] in
self?.scrollView.contentOffset.x = CGFloat(offset)
})
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageWidth = scrollView.bounds.width
let pageFraction = scrollView.contentOffset.x/pageWidth
//I get a crash here?
pageControl.currentPage = Int(round(pageFraction))
}
}
// this is the model for the views that get added inside the [UIView] array above
PageView:
import Foundation
import UIKit
class PageView: UIView {
// Private so that it can only be modified from within the class
private var headerTextField = UITextField()
// When this property is set it will update the headerTextField text
var headerText: String = "" {
didSet {
headerTextField.text = headerText
}
}
// Private so that you can only change from within the class
private var paragraphTextView = UITextView()
// When this property is set it will update the paragraphTextView text
var paragraphText: String = "" {
didSet {
paragraphTextView.text = paragraphText
}
}
// Designated Init method
init(headerText: String, paragraphText: String, backgroundColor: UIColor) {
super.init(frame: .zero)
setup()
self.headerTextField.text = headerText
self.paragraphTextView.text = paragraphText
self.backgroundColor = backgroundColor
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
// Basic text and view setup
headerTextField.isUserInteractionEnabled = false
headerTextField.textColor = .black
headerTextField.textAlignment = .center
headerTextField.sizeToFit()
paragraphTextView.isUserInteractionEnabled = false
paragraphTextView.textColor = .black
paragraphTextView.textAlignment = .center
paragraphTextView.sizeToFit()
paragraphTextView.isScrollEnabled = false
paragraphTextView.backgroundColor = .clear
// Configuring the textfield/view for autoLayout
headerTextField.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(headerTextField)
paragraphTextView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(paragraphTextView)
// Creating and activating the constraints
NSLayoutConstraint.activate([
headerTextField.centerXAnchor.constraint(equalTo: self.centerXAnchor),
headerTextField.centerYAnchor.constraint(equalTo: self.centerYAnchor),
paragraphTextView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
paragraphTextView.topAnchor.constraint(equalTo: headerTextField.bottomAnchor, constant: 20),
paragraphTextView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: (2/3))
])
}
}