Thread 1: signal SIGABRT when my code runs - swift

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)
}
}

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.

Can't figure out why my stackviews are not arranging vertically

I have done this many times but this time for some reason won't work the way it usually does. Am I doing something wrong here? I am just trying to get two views into my UIstackView and distribute them vertically. It seems to keep overlapping and going all over the place. At one point it was only even showing one view.
My viewdidload():
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
view.addSubview(headerView)
view.addSubview(contentView)
contentView.addSubview(contentStack)
headerView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.30).isActive = true
headerView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
headerView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
headerView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
contentView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.70).isActive = true
contentView.widthAnchor.constraint(equalTo: self.view.widthAnchor,multiplier: 0.90).isActive = true
contentView.topAnchor.constraint(equalTo: headerView.bottomAnchor,constant: 20).isActive = true
contentView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
}
My Views and Labels:
fileprivate lazy var headerView : UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .black
return view
}()
fileprivate lazy var contentView : UIView = {
var view = UIView()
view.backgroundColor = .lightGray
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
fileprivate lazy var contentStack : UIStackView = {
var stack = UIStackView(arrangedSubviews: [EarningsView,ListingsView,])
stack.translatesAutoresizingMaskIntoConstraints = true
stack.distribution = .fillEqually
stack.alignment = .fill
stack.axis = .vertical
// stack.spacing = 5
return stack
}()
fileprivate lazy var EarningsView : UIView = {
let EarningsView = UIView()
EarningsView.translatesAutoresizingMaskIntoConstraints = false
EarningsView.backgroundColor = .blue
EarningsView.addSubview(EarningsLabel)
EarningsView.addViewBorder(borderColor: UIColor.black.cgColor, borderWith: 0.5, borderCornerRadius: 0.0)
return EarningsView
}()
fileprivate lazy var EarningsLabel : UILabel = {
let EarningsLabel = UILabel()
EarningsLabel.translatesAutoresizingMaskIntoConstraints = false
let earningsText = NSAttributedString(string: "My Earnings", attributes: self.stringAttrib)
EarningsLabel.attributedText = earningsText
EarningsLabel.textColor = .black
EarningsLabel.backgroundColor = .white
EarningsLabel.textAlignment = .center
return EarningsLabel
}()
fileprivate lazy var ListingsView : UIView = {
let ListingsView = UIView()
ListingsView.translatesAutoresizingMaskIntoConstraints = false
ListingsView.addSubview(ListingLabel)
ListingsView.addViewBorder(borderColor: UIColor.black.cgColor, borderWith: 0.5, borderCornerRadius: 0.0)
ListingsView.backgroundColor = .red
return ListingsView
}()
fileprivate lazy var ListingLabel : UILabel = {
let ListingLabel = UILabel()
ListingLabel.translatesAutoresizingMaskIntoConstraints = false
let listingText = NSAttributedString(string: "My Listing", attributes: self.stringAttrib)
ListingLabel.attributedText = listingText
return ListingLabel
}()
This is driving me crazy because I have done it so many times before and now I have spent atleast 6 hours on this little part trying to figure it out. Of course I could just create a whole new viewcontroller but I just want to figure this out.
EarningsView, EarningsLabel, ListingsView, ListingLabel have
translatesAutoresizingMaskIntoConstraints = false
But they have no constraints added to replace the Autoresizing constraints, so they just act goofy and go up to the top left. I've seen stuff like this before. On my stuff. Either autosize or put in constraints.

Scroll View is moving horizontally and not vertically

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()

UIImage in UIImageView that is in UIScrollView is automatically zoomed in - how to initially view whole UIImage?

I have a UIViewController that has a UIScrollView. Within the latter view there is a UIImageView. I have implemented the UIImageView so that the user can zoom in/out. However, at the beginning I would like the user to see the entire UIImage before deciding to zoom in/out. Currently the UIImage is just automatically enlarged. A lot of the answers on stackOverflow suggested setting the UIImageView.contentMode to scaleAspectFit - i have done this but it has not worked.
class ViewImageViewController: UIViewController, UIScrollViewDelegate {
var imageToPresent: UIImage!
let scrollView: UIScrollView = {
let view = UIScrollView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let imageView: UIImageView = {
let imgView = UIImageView()
imgView.backgroundColor = UIColor.black
imgView.contentMode = .scaleAspectFit
imgView.isUserInteractionEnabled = true
imgView.translatesAutoresizingMaskIntoConstraints = false
return imgView
}()
override func viewDidLoad() {
super.viewDidLoad()
print("ViewImageViewController.viewDidLoad")
view.backgroundColor = UIColor.appGrayForLabels
setupViews()
scrollView.delegate = self
}
private func setupViews(){
view.addSubview(scrollView)
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 4.0
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
guard let img = imageToPresent else{return}
imageView.image = img
scrollView.addSubview(imageView)
imageView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
scrollView.isScrollEnabled = true
return imageView
}
You want to set the .minimumZoomScale to the ratio between your image size and the scroll view size, and then set the .zoomScale to that value to start with.
Here's your code, with a few modifications:
class ViewImageViewController: UIViewController, UIScrollViewDelegate {
var imageToPresent: UIImage!
let scrollView: UIScrollView = {
let view = UIScrollView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let imageView: UIImageView = {
let imgView = UIImageView()
imgView.backgroundColor = UIColor.black
imgView.contentMode = .scaleAspectFit
imgView.isUserInteractionEnabled = true
imgView.translatesAutoresizingMaskIntoConstraints = false
return imgView
}()
override func viewDidLoad() {
super.viewDidLoad()
print("ViewImageViewController.viewDidLoad")
if let img = UIImage(named: "background") {
imageToPresent = img
}
view.backgroundColor = .gray // UIColor.appGrayForLabels
setupViews()
scrollView.delegate = self
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let scrollViewFrame = scrollView.frame
let scaleWidth = scrollViewFrame.size.width / imageToPresent.size.width
let scaleHeight = scrollViewFrame.size.height / imageToPresent.size.height
let minScale = min(scaleWidth, scaleHeight)
scrollView.minimumZoomScale = minScale
scrollView.maximumZoomScale = 4.0
scrollView.zoomScale = minScale
}
private func setupViews(){
scrollView.isScrollEnabled = true
view.addSubview(scrollView)
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
guard let img = imageToPresent else{return}
imageView.image = img
scrollView.addSubview(imageView)
let g = scrollView.contentLayoutGuide
imageView.topAnchor.constraint(equalTo: g.topAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: g.bottomAnchor).isActive = true
imageView.leadingAnchor.constraint(equalTo: g.leadingAnchor).isActive = true
imageView.trailingAnchor.constraint(equalTo: g.trailingAnchor).isActive = true
imageView.widthAnchor.constraint(equalToConstant: imageToPresent.size.width).isActive = true
imageView.heightAnchor.constraint(equalToConstant: imageToPresent.size.height).isActive = true
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
}

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)
}