Using ScrollView Programmatically in Swift 3 - ios

I have searched other questions and seem to still have some trouble creating my scrollView programmatically with autolayout in swift 3. I am able to get my scrollview to show up as shown in the picture below, but when I scroll to the bottom my other label does not show up and the 'scroll top' label does not disappear.
Hoping someone can help review my code below!
import UIKit
class ViewController: UIViewController {
let labelOne: UILabel = {
let label = UILabel()
label.text = "Scroll Top"
label.backgroundColor = .red
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labelTwo: UILabel = {
let label = UILabel()
label.text = "Scroll Bottom"
label.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
let screensize: CGRect = UIScreen.main.bounds
let screenWidth = screensize.width
let screenHeight = screensize.height
var scrollView: UIScrollView!
scrollView = UIScrollView(frame: CGRect(x: 0, y: 120, width: screenWidth, height: screenHeight))
scrollView.contentSize = CGSize(width: screenWidth, height: 2000)
scrollView.addSubview(labelOne)
scrollView.addSubview(labelTwo)
view.addSubview(labelOne)
view.addSubview(labelTwo)
view.addSubview(scrollView)
// Visual Format Constraints
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": labelOne]))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-100-[v0]", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": labelOne]))
// Using iOS 9 Constraints in order to place the label past the iPhone 7 view
view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .top, relatedBy: .equal, toItem: labelOne, attribute: .bottom, multiplier: 1, constant: screenHeight + 200))
view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .right, relatedBy: .equal, toItem: labelOne, attribute: .right, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .left, relatedBy: .equal, toItem: labelOne, attribute: .left, multiplier: 1, constant: 0)
}
}

It is easy to use constraints to define the scroll content size - so you don't have to do any manual calculations.
Just remember:
The content elements of your scroll view must have left / top / width / height values. In the case of objects such as labels, they have intrinsic sizes, so you only have to define the left & top.
The content elements of your scroll view also define the bounds of the scrollable area - the contentSize - but they do so with the bottom & right constraints.
Combining those two concepts, you see that you need a "continuous chain" with at least one element defining the top / left / bottom / right extents.
Here is a simple example, that will run directly in a Playground page:
import UIKit
import PlaygroundSupport
class TestViewController : UIViewController {
let labelOne: UILabel = {
let label = UILabel()
label.text = "Scroll Top"
label.backgroundColor = .red
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labelTwo: UILabel = {
let label = UILabel()
label.text = "Scroll Bottom"
label.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let scrollView: UIScrollView = {
let v = UIScrollView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .cyan
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
// add the scroll view to self.view
self.view.addSubview(scrollView)
// constrain the scroll view to 8-pts on each side
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8.0).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8.0).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -8.0).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8.0).isActive = true
// add labelOne to the scroll view
scrollView.addSubview(labelOne)
// constrain labelOne to left & top with 16-pts padding
// this also defines the left & top of the scroll content
labelOne.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 16.0).isActive = true
labelOne.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 16.0).isActive = true
// add labelTwo to the scroll view
scrollView.addSubview(labelTwo)
// constrain labelTwo at 400-pts from the left
labelTwo.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 400.0).isActive = true
// constrain labelTwo at 1000-pts from the top
labelTwo.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 1000).isActive = true
// constrain labelTwo to right & bottom with 16-pts padding
labelTwo.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: -16.0).isActive = true
labelTwo.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -16.0).isActive = true
}
}
let vc = TestViewController()
vc.view.backgroundColor = .yellow
PlaygroundPage.current.liveView = vc
Edit - since this answer still gets occasional attention, I've updated the code to use more modern syntax, to respect the safe-area, and to use the scroll view's .contentLayoutGuide:
class TestViewController : UIViewController {
let labelOne: UILabel = {
let label = UILabel()
label.text = "Scroll Top"
label.backgroundColor = .yellow
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labelTwo: UILabel = {
let label = UILabel()
label.text = "Scroll Bottom"
label.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let scrollView: UIScrollView = {
let v = UIScrollView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .cyan
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
// add the scroll view to self.view
self.view.addSubview(scrollView)
// add labelOne to the scroll view
scrollView.addSubview(labelOne)
// add labelTwo to the scroll view
scrollView.addSubview(labelTwo)
// always a good idea to respect safe area
let safeG = view.safeAreaLayoutGuide
// we want to constrain subviews to the scroll view's Content Layout Guide
let contentG = scrollView.contentLayoutGuide
NSLayoutConstraint.activate([
// constrain the scroll view to safe area with 8-pts on each side
scrollView.topAnchor.constraint(equalTo: safeG.topAnchor, constant: 8.0),
scrollView.leadingAnchor.constraint(equalTo: safeG.leadingAnchor, constant: 8.0),
scrollView.trailingAnchor.constraint(equalTo: safeG.trailingAnchor, constant: -8.0),
scrollView.bottomAnchor.constraint(equalTo: safeG.bottomAnchor, constant: -8.0),
// constrain labelOne to leading & top of Content Layout Guide with 16-pts padding
// this also defines the left & top of the scroll content
labelOne.topAnchor.constraint(equalTo: contentG.topAnchor, constant: 16.0),
labelOne.leadingAnchor.constraint(equalTo: contentG.leadingAnchor, constant: 16.0),
// constrain labelTwo leading at 400-pts from labelOne trailing
labelTwo.leadingAnchor.constraint(equalTo: labelOne.trailingAnchor, constant: 400.0),
// constrain labelTwo top at 1000-pts from the labelOne bottom
labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor, constant: 1000),
// constrain labelTwo to trailing & bottom of Content Layout Guide with 16-pts padding
// this also defines the right & bottom of the scroll content
labelTwo.trailingAnchor.constraint(equalTo: contentG.trailingAnchor, constant: -16.0),
labelTwo.bottomAnchor.constraint(equalTo: contentG.bottomAnchor, constant: -16.0),
])
}
}

Two things.
1. Add the labels to scroll view, not your view
You want your label to scroll with scroll view, then you should not add it on your view. When running your code, you can scroll but the fixed label there is pinned to your view, not on your scroll view
2. Make sure you added your constraints correctly
Try it on your storyboard about what combination of constraint is enough for a view. At least 4 constraints are needed for a label.
Bottom line
Here is a modified version of your code. For constraint I added padding left, padding top, width and height and it works. My code is
let labelOne: UILabel = {
let label = UILabel()
label.text = "Scroll Top"
label.backgroundColor = .red
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labelTwo: UILabel = {
let label = UILabel()
label.text = "Scroll Bottom"
label.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
let screensize: CGRect = UIScreen.main.bounds
let screenWidth = screensize.width
let screenHeight = screensize.height
var scrollView: UIScrollView!
scrollView = UIScrollView(frame: CGRect(x: 0, y: 120, width: screenWidth, height: screenHeight))
scrollView.addSubview(labelTwo)
NSLayoutConstraint(item: labelTwo, attribute: .leading, relatedBy: .equal, toItem: scrollView, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
NSLayoutConstraint(item: labelTwo, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 200).isActive = true
NSLayoutConstraint(item: labelTwo, attribute: .top, relatedBy: .equal, toItem: scrollView, attribute: .topMargin, multiplier: 1, constant: 10).isActive = true
NSLayoutConstraint(item: labelTwo, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30).isActive = true
scrollView.contentSize = CGSize(width: screenWidth, height: 2000)
view.addSubview(scrollView)
}
And the scroll view looks like this

For me this work like charm
class WithDrawConfirmationViewController: UIViewController, WithDrawConfirmationViewProtocol {
var presenter: WithDrawConfirmationPresenterProtocol?
private let viewbackgroundColor = UIColor(hexString: "#F5F5F8")
// MARK:- View properties
lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
//view.backgroundColor = .red
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
lazy var contentView: UIView = {
let view = UIView()
//view.backgroundColor = .green
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var headerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private lazy var titleLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.themeBlack
label.text = "Withdraw Confirmation"//"Loading.."
label.textAlignment = .center
label.font = UIFont(name: "AvenirNext-DemiBold", size: 20)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
return label
}()
private lazy var descriptionLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.themeGray
label.text = "Please confirm your fixed deposit details before withdrawing your deposit"// "Loading.."
label.textAlignment = .center
label.font = UIFont(name: "AvenirNext-Medium", size: 14)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.setContentHuggingPriority(1000, for: .vertical)
return label
}()
private lazy var instructionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.themeGray
label.text = "Complete fixed deposit will be withdrawn and the money will be debited to your account within 1-2 working days."//"Loading.."
label.textAlignment = .left
label.font = UIFont(name: "AvenirNext-Medium", size: 14)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
return label
}()
private lazy var warningLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.themeBlue
label.text = "Axis bank will levy a penalty of 1% - 2% for premature withdrawal"//"Loading.."
label.textAlignment = .left
label.font = UIFont(name: "AvenirNext-DemiBold", size: 13)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
return label
}()
private lazy var warningView: UIView = {
let view = UIView()
view.cornerradius = 5
view.backgroundColor = UIColor(hexFromString: "#E8F4FD")
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private lazy var fdInformationDetailView: FDPurchaseDetailView = {
let view = FDPurchaseDetailView(isPoweredByViewVisible: false)
//view.backgroundColor = .red
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private lazy var footerCTAView: FooterButtonViewView = {
let view = FooterButtonViewView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
// MARK:- Life cycle
override func viewDidLoad() {
super.viewDidLoad()
contentView.backgroundColor = viewbackgroundColor
scrollView.backgroundColor = viewbackgroundColor
setUpUI()
presenter?.viewDidLoadTriggered()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
private func setUpUI() {
view.addSubview(scrollView)
scrollView.addSubview(contentView)
view.backgroundColor = viewbackgroundColor
addFooter()
setUpHeaderView()
setUpFDDetailView()
setUpInformationAndWarningView()
//scrollView.backgroundColor = .lightGray
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: footerCTAView.topAnchor).isActive = true
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 100, right: 0)
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
//contentView.bottomAnchor.constraint(equalTo: footerCTAView.topAnchor).isActive = true
}
private func setUpHeaderView() {
contentView.addSubview(headerView)
headerView.addSubviews([titleLabel, descriptionLabel])
headerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
headerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
headerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 8).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
titleLabel.topAnchor.constraint(equalTo: headerView.topAnchor, constant: 8).isActive = true
descriptionLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 20).isActive = true
descriptionLabel.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 8).isActive = true
descriptionLabel.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
descriptionLabel.bottomAnchor.constraint(equalTo: headerView.bottomAnchor, constant: -20).isActive = true
}
private func setUpFDDetailView() {
contentView.addSubview(fdInformationDetailView)
fdInformationDetailView.topAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true
fdInformationDetailView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
fdInformationDetailView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
}
private func setUpInformationAndWarningView() {
contentView.addSubview(instructionLabel)
instructionLabel.topAnchor.constraint(equalTo: fdInformationDetailView.bottomAnchor, constant: 20).isActive = true
instructionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
instructionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
warningView.addSubview(warningLabel)
warningLabel.topAnchor.constraint(equalTo: warningView.topAnchor, constant: 8).isActive = true
warningLabel.leadingAnchor.constraint(equalTo: warningView.leadingAnchor, constant: 20).isActive = true
warningLabel.trailingAnchor.constraint(equalTo: warningView.trailingAnchor, constant: -20).isActive = true
warningLabel.bottomAnchor.constraint(equalTo: warningView.bottomAnchor, constant: -8).isActive = true
contentView.addSubview(warningView)
warningView.topAnchor.constraint(equalTo: instructionLabel.bottomAnchor, constant: 8).isActive = true
warningView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
warningView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
//warningView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
warningView.bottomAnchor.constraint(greaterThanOrEqualTo: contentView.bottomAnchor, constant: -20).isActive = true
}
private func addFooter() {
view.addSubview(footerCTAView)
footerCTAView.delegate = self
footerCTAView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
footerCTAView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
footerCTAView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
}

Set scrollview images to the wallpaper:
#IBOutlet var scroll_view_img: UIScrollView!
var itemPhotoList = NSMutableArray()
var button = NSMutableArray()
#IBOutlet var imageview_big: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
itemPhotoList = ["grief-and-loss copy.jpg","aaa.jpg","image_4.jpeg"]
// button = ["btn1","btn2"]
let width:CGFloat = 100
let height:CGFloat = 100
var xposition:CGFloat = 10
var scroll_contont:CGFloat = 0
for i in 0 ..< itemPhotoList.count
{
var button_img = UIButton()
button_img = UIButton(frame: CGRect(x: xposition, y: 50, width: width, height: height))
let img = UIImage(named:itemPhotoList[i] as! String)
button_img.setImage(img, for: .normal)
scroll_view_img.addSubview(button_img)
button_img.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button_img.tag = i
view.addSubview(scroll_view_img)
xposition += width+10
scroll_contont += width
scroll_view_img.contentSize = CGSize(width: scroll_contont, height: height)
}
}
func buttonAction(sender: UIButton!)
{
switch sender.tag {
case 0:
imageview_big.image = UIImage(named: "grief-and-loss copy.jpg")
case 1:
imageview_big.image = UIImage(named: "aaa.jpg")
case 2:
imageview_big.image = UIImage(named: "image_4.jpeg")
default:
break
}
}

Copy and paste this controller in your project
class BaseScrollViewController: UIViewController {
lazy var contentViewSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 100)
lazy var scrollView: UIScrollView = {
let view = UIScrollView(frame: .zero)
view.backgroundColor = .white
view.frame = self.view.bounds
view.contentSize = contentViewSize
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var containerView: UIView = {
let v = UIView()
v.backgroundColor = .white
v.frame.size = contentViewSize
return v
}()
override func viewDidLoad() {
view.backgroundColor = .white
view.addSubview(scrollView)
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView.addSubview(containerView)
setupContainer(containerView)
super.viewDidLoad()
}
public func setupContainer(_ container: UIView) {
}
}
Usage for above code:
class ClientViewController: BaseScrollViewController {
override func viewDidLoad() {
super.viewDidLoad()
// do your stuff here
}
override func setupContainer(_ container: UIView) {
// add views here
}
}

These answers do not work with large titles in the navigation bar. Make sure you have the code below in your viewDidLoad() method of your view controller:
self.navigationController?.navigationBar.prefersLargeTitles = false

Related

Using ScrollView with StackView as subview and UIViews as children of StackView

I'm struggling to get my scroll view to work programatically.
I have a view controller that instantiates a UIScrollView with the following constraints
class HomeTabBarController: ViewController {
let homePageView = HomePageView()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
homePageView.setupHomePage()
view.addSubview(homePageView)
///constraints
homePageView.stackView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
homePageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
homePageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true;
homePageView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true;
homePageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -83).isActive = true;
}
}
The HomePageView (UIScrollView) has a UIStackView and instantiates 3 more UIViews which are the UIStackView's children. The code and constraints are as follows
class HomePageView: UIScrollView {
var homePageCarrousel: HomePageCarrousel?
var homePageSocial: HomePageSocialUp?
var homePageAboutUs: HomePageAboutUs?
var stackView = UIStackView()
func setupHomePage() {
translatesAutoresizingMaskIntoConstraints = false
homePageCarrousel = HomePageCarrousel()
homePageSocial = HomePageSocialUp()
homePageAboutUs = HomePageAboutUs()
guard let homePageSocial = homePageSocial, let homePageCarrousel = homePageCarrousel, let homePageAboutUs = homePageAboutUs else { return }
homePageSocial.setupSocialHeader()
stackView.addArrangedSubview(homePageSocial)
homePageCarrousel.setupCarrousel()
stackView.addArrangedSubview(homePageCarrousel)
homePageAboutUs.setup()
stackView.addArrangedSubview(homePageAboutUs)
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 10
setLayout()
}
func setLayout(){
guard let homePageSocial = homePageSocial, let homePageCarrousel = homePageCarrousel, let homePageAboutUs = homePageAboutUs else { return }
///header
homePageSocial.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true
homePageSocial.topAnchor.constraint(equalTo: stackView.topAnchor).isActive = true
homePageSocial.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true
///carrousel
homePageCarrousel.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 20).isActive = true
homePageCarrousel.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 20).isActive = true
homePageCarrousel.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -20).isActive = true
///about us
homePageAboutUs.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true
homePageAboutUs.topAnchor.constraint(equalTo: homePageCarrousel.bottomAnchor, constant: 10).isActive = true
homePageAboutUs.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true
homePageAboutUs.bottomAnchor.constraint(equalTo: stackView.bottomAnchor).isActive = true
///stackview
self.stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true;
self.stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true;
self.stackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true;
self.stackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true;
}
func dispose() {
homePageSocial = nil
homePageCarrousel = nil
homePageAboutUs = nil
subviews.forEach{$0.removeFromSuperview()}
}
}
Each of the children UIView (homePageSocial, homePageCarrousel and homePageAboutUs) have constraints have also got constraints:
HomePageCarrousel
Loads UIImageView and after adding as subview sets the constraint as
heightAnchor.constraint(equalToConstant: imageView.frame.height).isActive = true
HomePageAboutUs
Has 3 UITextviews (headerText, bodyTextLeft, bodyTextRight)
Header on top, bodytextLeft below it being 50% width of screen x = 0 and bodyTextRight x = width of bodyTextLeft.
Constraints are as follows
heightAnchor.constraint(equalToConstant: headerText.frame.height + bodyTextLeft.frame.height).isActive = true
bodyTextLeft.topAnchor.constraint(equalTo: headerText.bottomAnchor, constant: 15).isActive = true
bodyTextLeft.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width / 2).isActive = true
bodyTextRight.topAnchor.constraint(equalTo: headerText.bottomAnchor, constant: 15).isActive = true
bodyTextRight.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width / 2).isActive = true
bodyTextRight.leadingAnchor.constraint(equalTo: bodyTextLeft.trailingAnchor).isActive = true
HomePageSocialUp
A basic header with an icon and a constraint of
heightAnchor.constraint(equalToConstant: 325).isActive = true
After all that set I still can't get my ui scroll view to scroll on the y axis leaving my text of homePageAboutUs below the tabBar and off screen.
What am I doing wrong here?
Thanks in advance
First - the main purpose of a UIStackView is to arrange its subviews, so it is wrong to add position constraints to those subviews.
Next, when adding subviews to the "root" view of a controller (such as your scroll view), be sure to constrain them to the Safe Area Layout Guide.
Third, constrain the content of your scroll view to its Content Layout Guide.
I'm kind of taking your descriptions and hoping I'm close to what you're going for here:
and after scrolling down:
Here is your code, modified to produce that result:
class HomeTabBarController: UIViewController {
let homePageView = HomePageView()
override func viewDidLoad() {
super.viewDidLoad()
homePageView.setupHomePage()
view.addSubview(homePageView)
// respect safe area
let g = view.safeAreaLayoutGuide
///constraints
NSLayoutConstraint.activate([
homePageView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
homePageView.topAnchor.constraint(equalTo: g.topAnchor),
homePageView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
homePageView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
}
}
class HomePageSocialUp: UIView {
func setupSocialHeader() -> Void {
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .red
let imgView = UIImageView()
imgView.translatesAutoresizingMaskIntoConstraints = false
addSubview(imgView)
NSLayoutConstraint.activate([
// constrain image view 20-pts on each side
imgView.topAnchor.constraint(equalTo: topAnchor, constant: 20.0),
imgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20.0),
imgView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20.0),
imgView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20.0),
// Height = 325
imgView.heightAnchor.constraint(equalToConstant: 325.0),
])
if let img = UIImage(named: "myHeaderImage") {
imgView.image = img
}
}
}
class HomePageCarrousel: UIView {
func setupCarrousel() -> Void {
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .green
let imgView = UIImageView()
imgView.translatesAutoresizingMaskIntoConstraints = false
addSubview(imgView)
NSLayoutConstraint.activate([
// constrain image view 20-pts on each side
imgView.topAnchor.constraint(equalTo: topAnchor, constant: 20.0),
imgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20.0),
imgView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20.0),
imgView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20.0),
// let's make it 3:2 ratio
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 2.0 / 3.0)
])
if let img = UIImage(named: "myCarouselImage") {
imgView.image = img
}
}
}
class HomePageAboutUs: UIView {
let headerText = UILabel()
let bodyTextLeft = UILabel()
let bodyTextRight = UILabel()
func setup() -> Void {
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .blue
[headerText, bodyTextLeft, bodyTextRight].forEach {
// keep label height to text content
$0.setContentHuggingPriority(.required, for: .vertical)
$0.setContentCompressionResistancePriority(.required, for: .vertical)
// allow word-wrap
$0.numberOfLines = 0
// yellow background
$0.backgroundColor = .yellow
$0.translatesAutoresizingMaskIntoConstraints = false
addSubview($0)
}
NSLayoutConstraint.activate([
// header text 8-pts from Top / Leading / Trailing
headerText.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
headerText.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
headerText.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),
// left text 8-pts from Bottom of header text, 8-pts Leading
bodyTextLeft.topAnchor.constraint(equalTo: headerText.bottomAnchor, constant: 8.0),
bodyTextLeft.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
// right text 8-pts from Bottom of header text, 8-pts Trailing
bodyTextRight.topAnchor.constraint(equalTo: headerText.bottomAnchor, constant: 8.0),
bodyTextRight.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),
// 8-pts between left and right text
bodyTextRight.leadingAnchor.constraint(equalTo: bodyTextLeft.trailingAnchor, constant: 8.0),
// left and right text equal width
bodyTextLeft.widthAnchor.constraint(equalTo: bodyTextRight.widthAnchor),
// constrain Bottom of both to <= 8 (at least 8-pts
bodyTextLeft.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -8.0),
bodyTextRight.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -8.0),
])
headerText.font = .systemFont(ofSize: 20, weight: .regular)
bodyTextLeft.font = .systemFont(ofSize: 16, weight: .regular)
bodyTextRight.font = .systemFont(ofSize: 16, weight: .regular)
// texxt alignment
headerText.textAlignment = .center
bodyTextLeft.textAlignment = .left
bodyTextRight.textAlignment = .right
// some sample text
headerText.text = "This is the text for the About Us Header label. It will, of course, wrap onto multiple lines when needed, and auto-size it's height to fit the text."
bodyTextLeft.text = "Left label with\nembedded newlines\nso we can see it grow\nto fit the text.\nLine 5\nLine 6\nLine 7"
bodyTextRight.text = "Right label will wrap if needed. The one with the most lines will determine the bottom."
}
}
class HomePageView: UIScrollView {
var homePageCarrousel: HomePageCarrousel?
var homePageSocial: HomePageSocialUp?
var homePageAboutUs: HomePageAboutUs?
var stackView = UIStackView()
func setupHomePage() {
translatesAutoresizingMaskIntoConstraints = false
homePageCarrousel = HomePageCarrousel()
homePageSocial = HomePageSocialUp()
homePageAboutUs = HomePageAboutUs()
guard let homePageSocial = homePageSocial, let homePageCarrousel = homePageCarrousel, let homePageAboutUs = homePageAboutUs else { return }
homePageSocial.setupSocialHeader()
stackView.addArrangedSubview(homePageSocial)
homePageCarrousel.setupCarrousel()
stackView.addArrangedSubview(homePageCarrousel)
homePageAboutUs.setup()
stackView.addArrangedSubview(homePageAboutUs)
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 10
setLayout()
}
func setLayout(){
// constrain stackView to scroll view's Content Layout Guide
let g = self.contentLayoutGuide
///stackview
NSLayoutConstraint.activate([
self.stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
self.stackView.topAnchor.constraint(equalTo: g.topAnchor),
self.stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
self.stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
// stack view width is scroll view's frame layout guide width
stackView.widthAnchor.constraint(equalTo: self.frameLayoutGuide.widthAnchor),
])
}
}

how to make three views the same size and resized depending on the container view?

I have three views on the bottom of the view. I'm trying to make them have the same size and the size can be resized automatically depending on the size of the container view. I'd like to do it programmatically.
Here's how I want them to look
Here's how it looks by my current code
import UIKit
class ViewController: UIViewController {
var view1 = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
var view2 = UIView(frame: CGRect(x: 300, y: 300, width: 50, height: 50))
var view3 = UIView(frame: CGRect(x: 400, y: 500, width: 70, height: 70))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(view1)
view.addSubview(view2)
view.addSubview(view3)
view1.backgroundColor = .orange
view2.backgroundColor = .black
view3.backgroundColor = .gray
view1.translatesAutoresizingMaskIntoConstraints = false
view2.translatesAutoresizingMaskIntoConstraints = false
view3.translatesAutoresizingMaskIntoConstraints = false
// view1.topAnchor.constraint(equalTo: view.topAnchor, constant: 10).isActive = true
view1.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10).isActive = true
view1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: -10).isActive = true
view1.widthAnchor.constraint(equalTo: view.widthAnchor, constant: view.frame.size.width*0.01).isActive = true
view2.heightAnchor.constraint(equalToConstant: view.frame.size.height*0.1).isActive = true
//view2.topAnchor.constraint(equalTo: view.topAnchor, constant: 10).isActive = true
view2.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10).isActive = true
view2.leadingAnchor.constraint(equalTo: view1.leadingAnchor, constant: view.frame.size.width*(2/3)).isActive = true
view2.widthAnchor.constraint(equalTo: view1.widthAnchor, constant: 0).isActive = true
view2.heightAnchor.constraint(equalTo: view1.heightAnchor, constant: 0).isActive = true
// view3.topAnchor.constraint(equalTo: view.topAnchor, constant: 10).isActive = true
view3.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10).isActive = true
view3.leadingAnchor.constraint(equalTo: view2.leadingAnchor, constant: 10).isActive = true
view3.widthAnchor.constraint(equalTo: view2.widthAnchor, constant: 0).isActive = true
view3.heightAnchor.constraint(equalTo: view2.heightAnchor, constant: 0).isActive = true
}
}
Here, you can use the concept of UIStackView. Here, you can get your desired output using the following code(sample demo)
import UIKit
class ViewController: UIViewController {
var view1 = UIView()
var view2 = UIView()
var view3 = UIView()
override func viewDidLoad() {
super.viewDidLoad()
let stackView = UIStackView(arrangedSubviews: [view1, view2, view3])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .fillEqually
stackView.spacing = 50 //add amount of space between your views
view.addSubview(stackView)
view1.backgroundColor = .orange
view2.backgroundColor = .black
view3.backgroundColor = .gray
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true
stackView.heightAnchor.constraint(equalToConstant: view.frame.size.height*0.1).isActive = true
}
}
For more about UIStackView with example check this: https://nshipster.com/uistackview
Output:
Welcome to Stackoverflow. Please read on UIStackView. And by using that stackView, you can easily achieve what you want to achieve in your screenshot.
import UIKit
class ViewController: UIViewController {
var view1 = UIView()
var view2 = UIView()
var view3 = UIView()
lazy var stackView: UIStackView = {
return UIStackView(arrangedSubviews: [
self.view1,
self.view2,
self.view3
])
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(stackView)
view1.backgroundColor = .orange
view2.backgroundColor = .black
view3.backgroundColor = .gray
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.alignment = .center
view1.translatesAutoresizingMaskIntoConstraints = false
view2.translatesAutoresizingMaskIntoConstraints = false
view3.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.stackView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 16),
self.stackView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -16),
self.stackView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -16),
self.view1.heightAnchor.constraint(equalToConstant: view.frame.size.height*0.1),
self.view1.widthAnchor.constraint(equalToConstant: view.frame.size.height*0.1),
self.view2.heightAnchor.constraint(equalToConstant: view.frame.size.height*0.1),
self.view2.widthAnchor.constraint(equalToConstant: view.frame.size.height*0.1),
self.view3.heightAnchor.constraint(equalToConstant: view.frame.size.height*0.1),
self.view3.widthAnchor.constraint(equalToConstant: view.frame.size.height*0.1)
])
}
}
RESULT:

Why ImageView has was wrong location when I use anchors programmatically?

everyone. I have problems with the collection view cell which I build programmatically using anchors.
Cell
private lazy var iconView: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
return image
}()
private lazy var label: UILabel = {
let label = UILabel()
label.numberOfLines = 2
label.font = NeoSansPro.regular.of(size: 12)
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
label.sizeToFit()
return label
}()
Config anchor
override func layoutSubviews() {
super.layoutSubviews()
setupUI()
}
func setupUI() {
NSLayoutConstraint.activate( [
iconView.heightAnchor.constraint(equalToConstant: 32),
iconView.widthAnchor.constraint(equalToConstant: 32),
iconView.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0),
iconView.centerYAnchor.constraint(equalTo: centerYAnchor, constant: -16),
label.topAnchor.constraint(equalTo: iconView.bottomAnchor, constant: 8),
label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 4),
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4),
label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 4),
])
}
as the result 1 and 9 cells has wrong image location and label width biggest that cell width.
Add views first then add constraints it will work.
private lazy var iconView: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.image = UIImage(named: "1")
return image
}()
override func awakeFromNib() {
super.awakeFromNib()
backView.layer.cornerRadius = 5
self.contentView.addSubview(iconView)
setUpView()
}
func setUpView() {
NSLayoutConstraint.activate( [
iconView.heightAnchor.constraint(equalToConstant: 40),
iconView.widthAnchor.constraint(equalToConstant: 40),
iconView.centerXAnchor.constraint(equalTo: self.contentView.centerXAnchor, constant: 0),
iconView.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor, constant: -16)
])
}

UIScrollview does not show all subviews and is not scrollable

I have some problems with scrollview. I added a ScrollView to my ViewController with simple UIViews. But the ScrollView does not scroll and it does not show all my subviews.
I followed this example IOS swift scrollview programmatically but somehow my code does not work. Here is my example
import UIKit
class StatisticsViewController: UIViewController{
let scrollView: UIScrollView = {
let view = UIScrollView()
view.backgroundColor = UIColor.lightGray.adjust(by: 28)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let topstatsView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .red
return view
}()
let resultsView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
let blue: UIView = {
let view = UIView()
view.backgroundColor = .blue
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let yellow: UIView = {
let view = UIView()
view.backgroundColor = .yellow
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(scrollView)
// constraints of scroll view
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.addSubview(topstatsView)
scrollView.addSubview(resultsView)
scrollView.addSubview(blue)
scrollView.addSubview(yellow)
NSLayoutConstraint.activate([
topstatsView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 40),
topstatsView.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 30),
topstatsView.heightAnchor.constraint(equalToConstant: 250),
topstatsView.rightAnchor.constraint(equalTo: scrollView.rightAnchor)
])
NSLayoutConstraint.activate([
resultsView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 330),
resultsView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 30),
resultsView.heightAnchor.constraint(equalToConstant: 400),
resultsView.widthAnchor.constraint(equalToConstant: 450)
])
NSLayoutConstraint.activate([
blue.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 330),
blue.leftAnchor.constraint(equalTo: resultsView.rightAnchor, constant: 20),
blue.heightAnchor.constraint(equalToConstant: 400),
blue.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: -30)
])
NSLayoutConstraint.activate([
yellow.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 800),
yellow.leadingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 20),
yellow.heightAnchor.constraint(equalToConstant: 400),
yellow.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -30)
])
}
Here is a screenshot of my example.
As you can see the red view (topstatsView) does not confirm the right anchor and you cannot see the yellow and blue ones. And it is not scrollable. I am not able to see my mistakes. Thanks in advance!
Here you define wrong constraint.
1) Always add constraints related to each views top, bottom, leading and trailing, instead of define top constraint of all views to a scrollview.
2) Its not a good practise to add both leading and trailing anchor when you already define a width anchor.
3) Add bottom constraint related to a scrollview bottom to make it scrollable.
4) Add leading and trailing constraint related to outerView instead of adding it related to a scrollview.
Here is the updated code:-
class StatisticsViewController: UIViewController{
let scrollView: UIScrollView = {
let view = UIScrollView()
view.backgroundColor = UIColor.lightGray
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let topstatsView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .red
return view
}()
let resultsView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
let blue: UIView = {
let view = UIView()
view.backgroundColor = .blue
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let yellow: UIView = {
let view = UIView()
view.backgroundColor = .yellow
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(scrollView)
// constraints of scroll view
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.addSubview(topstatsView)
scrollView.addSubview(resultsView)
scrollView.addSubview(blue)
scrollView.addSubview(yellow)
NSLayoutConstraint.activate([
topstatsView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 40),
topstatsView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 30),
topstatsView.heightAnchor.constraint(equalToConstant: 250),
topstatsView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -30)
])
NSLayoutConstraint.activate([
resultsView.topAnchor.constraint(equalTo: topstatsView.bottomAnchor, constant: 30),
resultsView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 30),
resultsView.heightAnchor.constraint(equalToConstant: 400),
resultsView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -30)
])
NSLayoutConstraint.activate([
blue.topAnchor.constraint(equalTo: resultsView.bottomAnchor, constant: 30),
blue.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 20),
blue.heightAnchor.constraint(equalToConstant: 400),
blue.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -30)
])
NSLayoutConstraint.activate([
yellow.topAnchor.constraint(equalTo: blue.bottomAnchor, constant: 30),
yellow.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20),
yellow.heightAnchor.constraint(equalToConstant: 400),
yellow.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -30),
yellow.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor, constant: -20)
])
}
}

Swift textview growing direction

so i have a textview inside a uiview. And my question is how to make my textivew grow in up direction when textview goes to the next line as well as my uiview.
var textheightcontraint : NSLayoutConstraint!
var viewheightconstraint : NSLayoutConstraint!
func setup4(){
view.addSubview(colorview)
colorview.topAnchor.constraint(equalTo: customtableview.bottomAnchor).isActive = true
colorview.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
colorview.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
viewheightconstraint = colorview.heightAnchor.constraint(equalToConstant: 44)
viewheightconstraint.isActive = true
colorview.backgroundColor = UIColor.lightGray
colorview.addSubview(customtextview2)
customtextview2.backgroundColor = .white
customtextview2.leftAnchor.constraint(equalTo: colorview.leftAnchor).isActive = true
customtextview2.rightAnchor.constraint(equalTo: colorview.rightAnchor, constant: -20).isActive = true
customtextview2.bottomAnchor.constraint(equalTo: colorview.bottomAnchor).isActive = true
textheightcontraint = customtextview2.heightAnchor.constraint(equalToConstant: 39)
textheightcontraint.isActive = true
customtextview2.delegate = self
}
func setuptextview(){
let fixedWidth = customtextview2.frame.size.width
let newSize = customtextview2.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
self.textheightcontraint.constant = newSize.height
self.viewheightconstraint.constant = newSize.height
self.view.layoutIfNeeded()
}
func textViewDidChange(_ textView: UITextView) {
setuptextview()
}
If I understand your question, you want the text view (and the view it's contained in) to keep its bottom at the same position, and expand upward as you type?
To do that, disable scrolling in the text view, and set up your constraints so the bottom of the containing view is constrained to a y-position:
//
// ViewController.swift
//
// Created by Don Mag on 8/9/18.
//
import UIKit
class ViewController: UIViewController {
var theContainingView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
var theTextView: UITextView = {
let v = UITextView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .yellow
theContainingView.backgroundColor = .cyan
theContainingView.addSubview(theTextView)
view.addSubview(theContainingView)
NSLayoutConstraint.activate([
theTextView.topAnchor.constraint(equalTo: theContainingView.topAnchor, constant: 8.0),
theTextView.bottomAnchor.constraint(equalTo: theContainingView.bottomAnchor, constant: -8.0),
theTextView.leadingAnchor.constraint(equalTo: theContainingView.leadingAnchor, constant: 8.0),
theTextView.trailingAnchor.constraint(equalTo: theContainingView.trailingAnchor, constant: -8.0),
theContainingView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40.0),
theContainingView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40.0),
// Constrain the Bottom of the containing view. As the textView grows (or shrinks) with input,
// the TOP of the view will move up or down
theContainingView.bottomAnchor.constraint(equalTo: view.topAnchor, constant: 300.0),
])
theTextView.isScrollEnabled = false
theTextView.text = "This is the starting text."
}
}
Results:

Resources