Scroll View is moving horizontally and not vertically - swift

I have created a scroll view and I have tried many different methods as you can see below, however I always get the same outcome where the scroll view moves horizontally. I want it to move vertically, but it is not. Also, the text view shows the entire message in one line and does not use multiple lines as was happening before I added the scroll view.
import UIKit
import SafariServices
class MainView: UIViewController {
let transition = MainDropMenuAnimation()
let scrollView = UIScrollView()
let scrollViewView = UIView()
let factView = QuickFact()
let socialMediaSV = SocialMediaSV()
let logo = UIImageView()
let aboutUs = UITextView()
let dropMenuButton = UIButton(type: .custom)
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(patternImage: #imageLiteral(resourceName: "HomeBackground"))
addQuickFactView()
setupScrollView()
setupNavBar()
}
func setupNavBar() {
navigationItem.title = "Home"
navigationController?.navigationBar.barTintColor = .clear
dropMenuButton.setImage(#imageLiteral(resourceName: "dropMenuButton"), for: .normal)
dropMenuButton.addTarget(self, action: #selector(handleDropMenu), for: .touchUpInside)
dropMenuButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
dropMenuButton.widthAnchor.constraint(equalTo: dropMenuButton.heightAnchor).isActive = true
let leftButton = UIBarButtonItem(customView: dropMenuButton)
self.navigationItem.leftBarButtonItem = leftButton
}
#objc func handleDropMenu() {
let dropMenu = DropViewContainer()
dropMenu.modalPresentationStyle = .overCurrentContext
dropMenu.transitioningDelegate = self
present(dropMenu, animated: true)
}
func addQuickFactView() {
addChild(factView)
view.addSubview(factView.view)
factView.didMove(toParent: self)
factView.view.translatesAutoresizingMaskIntoConstraints = false
factView.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
factView.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
factView.view.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
factView.view.heightAnchor.constraint(equalToConstant: 200).isActive = true
}
func setupScrollView() {
setupView()
scrollView.contentSize.height = 3000
view.addSubview(scrollView)
positionScrollView()
}
func positionScrollView() {
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: factView.view.bottomAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
func setupView() {
setupLogo()
setupAboutUs()
setupSocialMediaSV()
scrollView.addSubview(scrollViewView)
positionView()
}
func positionView() {
scrollViewView.translatesAutoresizingMaskIntoConstraints = false
scrollViewView.rightAnchor.constraint(equalTo: scrollView.rightAnchor).isActive = true
scrollViewView.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
scrollViewView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
scrollViewView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
}
func setupLogo() {
logo.image = #imageLiteral(resourceName: "Logo")
scrollViewView.addSubview(logo)
positionLogo()
}
func positionLogo() {
logo.translatesAutoresizingMaskIntoConstraints = false
logo.widthAnchor.constraint(equalToConstant: 200).isActive = true
logo.heightAnchor.constraint(equalToConstant: 200).isActive = true
logo.centerXAnchor.constraint(equalTo: scrollViewView.centerXAnchor).isActive = true
logo.topAnchor.constraint(equalTo: scrollViewView.topAnchor, constant: 20).isActive = true
}
func setupAboutUs() {
let aboutUsText = (NSMutableAttributedString(string: "About Us\n", attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 25)]))
aboutUsText.append(NSMutableAttributedString(string: "At Cleaner Together, we are commited to promoting recycling, reusing, and reducing (and of course composting), while spreading sanitization and cleanliness around the world. At the moment, we are just encouraging proper waste disposal, however with proper funding we have many projects we hope to accomplish.", attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17), NSAttributedString.Key.foregroundColor: UIColor.black]))
aboutUs.attributedText = aboutUsText
aboutUs.textColor = .black
aboutUs.textAlignment = .left
aboutUs.isScrollEnabled = false
aboutUs.isEditable = false
aboutUs.backgroundColor = .init(white: 1.0, alpha: 0.5)
aboutUs.layer.cornerRadius = 20
scrollViewView.addSubview(aboutUs)
positionAboutUs()
}
func positionAboutUs() {
aboutUs.translatesAutoresizingMaskIntoConstraints = false
aboutUs.rightAnchor.constraint(equalTo: scrollViewView.rightAnchor, constant: -20).isActive = true
aboutUs.leftAnchor.constraint(equalTo: scrollViewView.leftAnchor, constant: 20).isActive = true
aboutUs.heightAnchor.constraint(equalToConstant: 200).isActive = true
aboutUs.topAnchor.constraint(equalTo: logo.bottomAnchor, constant: 20).isActive = true
}
func setupSocialMediaSV() {
addChild(socialMediaSV)
scrollViewView.addSubview(socialMediaSV.view)
socialMediaSV.didMove(toParent: self)
socialMediaSV.view.translatesAutoresizingMaskIntoConstraints = false
socialMediaSV.view.centerXAnchor.constraint(equalTo: scrollViewView.centerXAnchor).isActive = true
socialMediaSV.view.widthAnchor.constraint(equalToConstant: 240).isActive = true
socialMediaSV.view.topAnchor.constraint(equalTo: aboutUs.bottomAnchor, constant: 20).isActive = true
socialMediaSV.view.heightAnchor.constraint(equalToConstant: 40).isActive = true
}
}
extension MainView: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.isPresenting = true
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.isPresenting = false
return transition
}
}

You should check your constraints and make sure that your scroll view has correct contentSize.
Make scrollViewView left and right anchors also equalTo viewController's view. In func positionView()

Related

How to make long text label, which lives in UI View, to multiple lines in Swift?

I'm trying to make a long text label, which lives inside of UI View, to multiple lines. I was searching for a solution for 2 hours, but I couldn't solve it, so I want to ask for some help. Here is the image of what I get now.
This is the View Hierarchy debugger.
In a table view cell, I put the repository name which I got from Github. In the third cell in the image, I want to make the repo name to two lines, since it's long. I saw this StackOverflow question, which sounds similar to my current problem and implemented in my code, but it didn't work.
Here is my code. It's too long but the point is, I have declared label.numberOfLines = 0 and label.adjustsFontSizeToFitWidth = true, and I tried to make my label convertible to multiple lines, but it didn't work. Could anyone please point me at what I'm doing wrong here?
import UIKit
class RepositoryCell: UITableViewCell {
//MARK: - Properties
let userImageView: UIImageView = {
let img = UIImageView()
img.contentMode = .scaleAspectFit
img.clipsToBounds = true
img.backgroundColor = .black
return img
}()
let containerView: UIView = {
let view = UIView()
view.clipsToBounds = true
view.backgroundColor = .systemPink
return view
}()
let userNameLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.font = UIFont.boldSystemFont(ofSize: 20)
label.numberOfLines = 0
label.adjustsFontSizeToFitWidth = true
return label
}()
let repositoryNameLabel: UILabel = {
let label = UILabel()
label.textColor = .gray
label.font = UIFont.systemFont(ofSize: 14)
label.numberOfLines = 0
label.adjustsFontSizeToFitWidth = true
return label
}()
let starNumLabel: UILabel = {
let label = UILabel()
label.textColor = .systemPink
label.font = UIFont.systemFont(ofSize: 14)
label.backgroundColor = .black
return label
}()
//MARK: - Init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(userImageView)
containerView.addSubview(userNameLabel)
containerView.addSubview(repositoryNameLabel)
addSubview(containerView)
addSubview(starNumLabel)
configureUserNameLabel()
configureRepositoryNameLabel()
configureViewConstraints()
}
override func layoutSubviews() {
super.layoutSubviews()
userImageView.layer.cornerRadius = userImageView.frame.height / 2
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//MARK: - Helper functions
func configureCellView(repository: Repository) {
userImageView.image = UIImage(named: "001")
userNameLabel.text = repository.userName
repositoryNameLabel.text = repository.repositoryName
starNumLabel.text = "⭐️\(String(describing: repository.starNum))"
}
func configureUserNameLabel() {
userNameLabel.numberOfLines = 0
userNameLabel.adjustsFontSizeToFitWidth = true
}
func configureRepositoryNameLabel() {
repositoryNameLabel.numberOfLines = 0
repositoryNameLabel.adjustsFontSizeToFitWidth = true
}
func configureViewConstraints() {
setUserImageConstraints()
setContainerViewConstraints()
setUserNameLabelConstraints()
setRepositoryNameLabel()
setStarNumLabel()
}
func setUserImageConstraints() {
userImageView.translatesAutoresizingMaskIntoConstraints = false
userImageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
userImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
userImageView.heightAnchor.constraint(equalToConstant: 70).isActive = true
userImageView.widthAnchor.constraint(equalTo: userImageView.heightAnchor, multiplier: 1).isActive = true
}
func setContainerViewConstraints() {
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
containerView.topAnchor.constraint(equalTo: topAnchor, constant: 10).isActive = true
containerView.leadingAnchor.constraint(equalTo: userImageView.trailingAnchor, constant: 10).isActive = true
containerView.trailingAnchor.constraint(equalTo: starNumLabel.leadingAnchor, constant: -10).isActive = true
}
func setUserNameLabelConstraints() {
userNameLabel.translatesAutoresizingMaskIntoConstraints = false
userNameLabel.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
userNameLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
// userNameLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
}
func setRepositoryNameLabel() {
repositoryNameLabel.translatesAutoresizingMaskIntoConstraints = false
repositoryNameLabel.topAnchor.constraint(equalTo: userNameLabel.bottomAnchor).isActive = true
repositoryNameLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
// repositoryNameLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
// this doesn't work...
// repositoryNameLabel.trailingAnchor.constraint(greaterThanOrEqualTo: containerView.leadingAnchor, constant: 5).isActive = true
}
func setStarNumLabel() {
starNumLabel.translatesAutoresizingMaskIntoConstraints = false
starNumLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
starNumLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
}
}
My guess is the trailing constraints of your label is not set properly.
You can use the View Hierarchy debugger in Xcode to have a clear view of the size your view actually has at runtime.
If the trailing anchor is not set, wrapping text isn't possible as it doesn't reach the "end" of the view.

UIScrollView animate offset change when removing nested view

I have a UIScrollView that contains a stack view - I'm basically replicating a tabs feature.
One tab has a taller view than the other, so when I hide the view in the stack view it resizes.
This causes the scroll view to jump to the offset that fits the shorter view, in the event the user has scrolled to the top.
Is it possible to instead animate this change? Instead of the jump, the view scrolls to the correct offset? I am unsure how to achieve this.
final class ScrollViewController: UIViewController {
private var visibleTab: TabState = .overview {
didSet {
guard oldValue != visibleTab else { return }
switch visibleTab {
case .overview:
self.spacesTab.isHidden = true
self.overviewTab.isHidden = false
case .spaces:
self.spacesTab.isHidden = false
self.overviewTab.isHidden = true
}
}
}
enum TabState {
case overview
case spaces
}
private lazy var scrollView: UIScrollView = {
let view = UIScrollView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.delegate = self
view.alwaysBounceVertical = true
return view
}()
private let contentStackView: UIStackView = {
let view = UIStackView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.axis = .vertical
view.alignment = .fill
view.spacing = 8
view.distribution = .fill
return view
}()
private let tabSelectorView: UIStackView = {
let view = UIStackView(frame: .zero)
view.axis = .horizontal
view.distribution = .fillEqually
return view
}()
private let overviewTab: UIView = {
let view = UIView(frame: .zero)
view.backgroundColor = .darkGray
view.heightAnchor.constraint(equalToConstant: 100).isActive = true
view.isHidden = false
return view
}()
private let spacesTab: UIView = {
let view = UIView(frame: .zero)
view.backgroundColor = .lightGray
view.heightAnchor.constraint(equalToConstant: 780).isActive = true
view.isHidden = true
return view
}()
private let profileHeader = ScrollViewProfileHeaderView(frame: .zero)
private lazy var overviewTabButton = makeButton(title: "Overview")
private lazy var spacesTabButton = makeButton(title: "Spaces")
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
}
}
extension ScrollViewController: UIScrollViewDelegate { }
private extension ScrollViewController {
func configureUI() {
overviewTabButton.addTarget(self, action: #selector(showOverviewTab), for: .touchUpInside)
spacesTabButton.addTarget(self, action: #selector(showSpacesTab), for: .touchUpInside)
[overviewTabButton, spacesTabButton].forEach(tabSelectorView.addArrangedSubview)
profileHeader.translatesAutoresizingMaskIntoConstraints = false
tabSelectorView.translatesAutoresizingMaskIntoConstraints = false
[overviewTab, spacesTab].forEach(contentStackView.addArrangedSubview)
[profileHeader, tabSelectorView, contentStackView].forEach(scrollView.addSubview(_:))
view.addSubview(scrollView)
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
profileHeader.topAnchor.constraint(equalTo: scrollView.topAnchor),
profileHeader.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
profileHeader.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
tabSelectorView.topAnchor.constraint(equalTo: profileHeader.bottomAnchor),
tabSelectorView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
tabSelectorView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
contentStackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
contentStackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
contentStackView.topAnchor.constraint(equalTo: tabSelectorView.bottomAnchor, constant: 8),
contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
contentStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
])
}
func makeButton(title: String) -> UIButton {
let button = UIButton(type: .system)
button.setTitle(title, for: .normal)
button.backgroundColor = .lightGray
return button
}
#objc func showOverviewTab() {
visibleTab = .overview
}
#objc func showSpacesTab() {
visibleTab = .spaces
}
}
final class ScrollViewProfileHeaderView: UIView {
private let headerImage: UIImageView = {
let view = UIImageView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.contentMode = .scaleAspectFill
view.clipsToBounds = true
view.backgroundColor = .systemTeal
return view
}()
private let profileCard: ProfileCardView = {
let view = ProfileCardView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .purple
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
[headerImage, profileCard].forEach(addSubview(_:))
NSLayoutConstraint.activate([
headerImage.topAnchor.constraint(equalTo: topAnchor),
headerImage.leadingAnchor.constraint(equalTo: leadingAnchor),
headerImage.trailingAnchor.constraint(equalTo: trailingAnchor),
headerImage.heightAnchor.constraint(equalToConstant: 180),
profileCard.topAnchor.constraint(equalTo: headerImage.centerYAnchor),
profileCard.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 48),
profileCard.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -32),
profileCard.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -48),
profileCard.heightAnchor.constraint(equalToConstant: 270),
])
}
required init?(coder: NSCoder) {
return nil
}
}
You will probably want to make some additional changes, but this might get you on your way.
In your visibleTab / didSet block, use UIView.animate() when you hide the spacesTab:
private var visibleTab: TabState = .overview {
didSet {
guard oldValue != visibleTab else { return }
switch self.visibleTab {
case .overview:
// set duration longer, such as 1.0, to clearly see the animation...
UIView.animate(withDuration: 0.3) {
self.spacesTab.isHidden = true
self.overviewTab.isHidden = false
}
case .spaces:
self.spacesTab.isHidden = false
self.overviewTab.isHidden = true
}
}
}

Thread 1: signal SIGABRT when my code runs

Total Swift newbie here. I am trying to make two containers that span half of the view.heightAnchor.constraint with one function createhalfcontainer in order to make things neater. Whenever I run my code, I get the signal SIGABRT and despite extensive code review I can't seem to find out why.
class ViewController: UIViewController {
let iv = {() -> UIImageView in
let imageview = UIImageView(image: #imageLiteral(resourceName: "google"))
imageview.contentMode = UIViewContentMode.scaleAspectFit
return imageview
}()
let tv = {() -> UITextView in
let textview = UITextView()
textview.text = "I am going to work here soon."
textview.font = UIFont.boldSystemFont(ofSize: 16)
textview.textAlignment = NSTextAlignment.center
textview.isEditable = false
return textview
}()
func createhalfcontainer(within: UIView, direction:String, view: UIView)-> UIView{
let container = UIView()
container.translatesAutoresizingMaskIntoConstraints = false
within.translatesAutoresizingMaskIntoConstraints = false
container.heightAnchor.constraint(equalTo: view.heightAnchor,multiplier: 0.5).isActive = true
container.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
container.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
if direction == "up" {
container.topAnchor.constraint(equalTo:view.topAnchor).isActive = true
within.topAnchor.constraint(equalTo: container.topAnchor, constant: 100).isActive = true}
else {container.bottomAnchor.constraint(equalTo:view.bottomAnchor).isActive = true
within.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: 100).isActive = true}
within.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
within.trailingAnchor.constraint(equalTo: container.trailingAnchor).isActive = true
container.addSubview(within)
return container
}
override func viewDidLoad() {
super.viewDidLoad() //This instantiates the view object
let uppercontainer = self.createhalfcontainer(within: iv, direction: "up", view:view)
let bottomcontainer = self.createhalfcontainer(within: tv, direction: "bottom",view:view)
view.addSubview(uppercontainer)
view.addSubview(bottomcontainer)
}
}

Label text in center and moving depending on number of text lines in the label below

My UILabel is pinned to the top of the cell, just like UIImage but if the text below has f.e. 1 line (its also in UILabel) my top UILabel is in other place.
Here are my constraints and labels declaration. Thanks to anyone who will try to solve my problem :) I tried using sizeToFit method on my UILabels
var albumImage: UIImageView = {
let view = UIImageView()
view.translatesAutoresizingMaskIntoConstraints = false
view.contentMode = .scaleAspectFit
return view
}()
var albumName: UILabel = {
let view = UILabel()
view.translatesAutoresizingMaskIntoConstraints = false
view.numberOfLines = 0
view.sizeToFit()
return view
}()
var bandName: UILabel = {
let view = UILabel()
view.translatesAutoresizingMaskIntoConstraints = false
view.sizeToFit()
return view
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
drawLayout()
}
required init?(coder aDecoder: NSCoder) {
fatalError("ERROR")
}
func drawLayout(){
let guide = self.safeAreaLayoutGuide
//albumImage
addSubview(albumImage)
addSubview(albumName)
addSubview(bandName)
albumImage.heightAnchor.constraint(equalToConstant: 50).isActive = true
albumImage.widthAnchor.constraint(equalToConstant: 50).isActive = true
albumImage.leadingAnchor.constraint(equalTo: guide.leadingAnchor, constant: 8).isActive = true
albumImage.topAnchor.constraint(equalTo: self.topAnchor, constant: 8).isActive = true
albumImage.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -8).isActive = true
//albumTitle
albumName.topAnchor.constraint(equalTo: bandName.bottomAnchor).isActive = true
albumName.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -8).isActive = true
albumName.leadingAnchor.constraint(equalTo: albumImage.trailingAnchor, constant: 8).isActive = true
albumName.trailingAnchor.constraint(equalTo: guide.trailingAnchor, constant: -8).isActive = true
//bandName
bandName.topAnchor.constraint(equalTo: albumImage.topAnchor).isActive = true
bandName.leadingAnchor.constraint(equalTo: albumImage.trailingAnchor, constant: 8).isActive = true
}

Trying to make sense of UILabel behavior.

I am trying to replicate Apple's calculator UI layout.
Here is a gif of what I have so far.
The problems that I am encountering mostly have to do with the UILables.
As seen in the gif above, I am experiencing the following problems:
On device rotation, the labels "L1" and "L2" pop, instead of transitioning smoothly.
The labels on the brown colored buttons disappear when transitioning back to portrait.
For the labels "L1" and "L2" I have tried experimenting with the content mode and constraints, however, I still get clunky transitions.
As for the disappearing labels, instead of hiding/unhiding the stack view to make the layout appear and disappear via it's is hidden property, I instead tried using constraints on the stack view to handle the transition, however, the results remain the same.
I have also looked online and tried some suggestions, however, most answers were outdated or simply did not work.
The code is very straight forward, it primarily consists of setting up the views and its constraints.
extension UIStackView {
convenience init(axis: UILayoutConstraintAxis, distribution: UIStackViewDistribution = .fill) {
self.init()
self.axis = axis
self.distribution = distribution
self.translatesAutoresizingMaskIntoConstraints = false
}
}
class Example: UIView {
let mainStackView = UIStackView(axis: .vertical, distribution: .fill)
let subStackView = UIStackView(axis: .horizontal, distribution: .fillProportionally)
let portraitStackView = UIStackView(axis: .vertical, distribution: .fillEqually)
let landscapeStackView = UIStackView(axis: .vertical, distribution: .fillEqually)
var containerView: UIView = {
$0.backgroundColor = .darkGray
$0.translatesAutoresizingMaskIntoConstraints = false
return $0
}(UIView(frame: .zero))
let mainView: UIView = {
$0.backgroundColor = .blue
$0.translatesAutoresizingMaskIntoConstraints = false
return $0
}(UIView(frame: .zero))
let labelView: UIView = {
$0.backgroundColor = .red
$0.translatesAutoresizingMaskIntoConstraints = false
return $0
}(UIView(frame: .zero))
var labelOne: UILabel!
var labelTwo: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
autoresizingMask = [.flexibleWidth, .flexibleHeight]
labelOne = createLabel(text: "L1")
labelOne.translatesAutoresizingMaskIntoConstraints = false
labelOne.backgroundColor = .darkGray
labelTwo = createLabel(text: "L2")
labelTwo.translatesAutoresizingMaskIntoConstraints = false
labelTwo.backgroundColor = .black
landscapeStackView.isHidden = true
mainView.addSubview(labelView)
labelView.addSubview(labelOne)
labelView.addSubview(labelTwo)
addSubview(mainStackView)
mainStackView.addArrangedSubview(mainView)
setButtonStackView()
setStackViewConstriants()
setDisplayViewConstriants()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setStackViewConstriants() {
mainStackView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
func setDisplayViewConstriants() {
mainView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 288/667).isActive = true
labelView.heightAnchor.constraint(equalTo: mainView.heightAnchor, multiplier: 128/288).isActive = true
labelView.centerYAnchor.constraint(equalTo: mainView.centerYAnchor).isActive = true
labelView.leadingAnchor.constraint(equalTo: mainView.leadingAnchor, constant: 24).isActive = true
labelView.trailingAnchor.constraint(equalTo: mainView.trailingAnchor, constant: -24).isActive = true
labelOne.heightAnchor.constraint(equalTo: labelTwo.heightAnchor, multiplier: 88/32).isActive = true
labelOne.trailingAnchor.constraint(equalTo: labelView.trailingAnchor).isActive = true
labelOne.leadingAnchor.constraint(equalTo: labelView.leadingAnchor).isActive = true
labelOne.topAnchor.constraint(equalTo: labelView.topAnchor).isActive = true
labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor).isActive = true
labelTwo.trailingAnchor.constraint(equalTo: labelView.trailingAnchor).isActive = true
labelTwo.leadingAnchor.constraint(equalTo: labelOne.leadingAnchor).isActive = true
labelTwo.bottomAnchor.constraint(equalTo: labelView.bottomAnchor).isActive = true
}
func createLabel(text: String) -> UILabel {
let label = UILabel(frame: .zero)
label.text = text
label.font = UIFont.init(name: "Arial-BoldMT", size: 60)
label.textColor = .white
label.textAlignment = .right
label.contentMode = .right
label.minimumScaleFactor = 0.1
label.adjustsFontSizeToFitWidth = true
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}
func createButton(text: String) -> UIButton {
let button = UIButton(type: .custom)
button.setTitle(text, for: .normal)
button.setTitleColor(.white, for: .normal)
button.layer.borderColor = UIColor.white.cgColor
button.layer.borderWidth = 1
button.titleLabel?.font = UIFont.init(name: "Arial-BoldMT", size: 60)
button.titleLabel?.minimumScaleFactor = 0.1
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.translatesAutoresizingMaskIntoConstraints = false
button.titleLabel?.leadingAnchor.constraint(equalTo: button.leadingAnchor).isActive = true
button.titleLabel?.trailingAnchor.constraint(equalTo: button.trailingAnchor).isActive = true
button.titleLabel?.topAnchor.constraint(equalTo: button.topAnchor).isActive = true
button.titleLabel?.bottomAnchor.constraint(equalTo: button.bottomAnchor).isActive = true
button.titleLabel?.textAlignment = .center
button.titleLabel?.contentMode = .scaleAspectFill
button.titleLabel?.numberOfLines = 0
return button
}
func setButtonStackView() {
for _ in 1...5 {
let stackView = UIStackView(axis: .horizontal, distribution: .fillEqually)
for _ in 1...4 {
let button = createButton(text: "0")
button.backgroundColor = .brown
stackView.addArrangedSubview(button)
}
landscapeStackView.addArrangedSubview(stackView)
}
for _ in 1...5 {
let stackView = UIStackView(axis: .horizontal, distribution: .fillEqually)
for _ in 1...4 {
let button = createButton(text: "0")
button.backgroundColor = .purple
stackView.addArrangedSubview(button)
}
portraitStackView.addArrangedSubview(stackView)
}
subStackView.addArrangedSubview(landscapeStackView)
subStackView.addArrangedSubview(portraitStackView)
mainStackView.addArrangedSubview(subStackView)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
if UIDevice.current.orientation.isLandscape && landscapeStackView.isHidden == true {
self.landscapeStackView.isHidden = false
}
if UIDevice.current.orientation.isPortrait && landscapeStackView.isHidden == false {
self.landscapeStackView.isHidden = true
}
self.layoutIfNeeded()
}
}
Overview:
Do things incrementally with separate components / view controllers (easier to debug)
The below solution is only for labels L1 and L2.
For the calculator buttons, it would be best to use a UICollectionViewController. (I haven't implemented it, add as a child view controller)
Code:
private func setupLabels() {
view.backgroundColor = .red
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .fill
stackView.distribution = .fill
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
let label1 = UILabel()
label1.text = "L1"
label1.textColor = .white
label1.backgroundColor = .darkGray
label1.textAlignment = .right
label1.font = UIFont.preferredFont(forTextStyle: .title1)
let label2 = UILabel()
label2.text = "L2"
label2.textColor = .white
label2.backgroundColor = .black
label2.textAlignment = .right
label2.font = UIFont.preferredFont(forTextStyle: .caption1)
stackView.addArrangedSubview(label1)
stackView.addArrangedSubview(label2)
}