UIStackView height, content inside in the middle not at top - ios

I have a UIStackview, and when its filled with a bunch of things, its fine, However, when its filled with 1, or 2 (for example), it spreads them out and not keeps them at top. What did I do wrong?
This is what happens. I want them to be at the top and with No right under it.
for i in 0..<self.itemSpecificsArray.count {
let itemSpecificName = UILabel()
let itemSpecificValue = UILabel()
itemSpecificName.textColor = UIColor(red: 236.0 / 255.0, green: 91.0 / 255.0, blue: 110.0 / 255.0, alpha: 1.0)
itemSpecificName.textAlignment = .left
itemSpecificName.font = UIFont.boldSystemFont(ofSize: 18.0)
itemSpecificValue.textColor = UIColor.black
itemSpecificValue.textAlignment = .left
itemSpecificValue.font = UIFont.systemFont(ofSize: 15.0)
print(self.itemSpecificsArray[i].itemSpecificName)
print("** \(self.itemSpecificsArray[i].itemSpecificValue)")
itemSpecificName.text = self.itemSpecificsArray[i].itemSpecificName
itemSpecificValue.text = self.itemSpecificsArray[i].itemSpecificValue
self.mainSectionItemSpecifics.addArrangedSubview(itemSpecificName)
self.mainSectionItemSpecifics.addArrangedSubview(itemSpecificValue)
if (i == self.itemSpecificsArray.count) {
let view = UIView()
self.mainSectionItemSpecifics.addArrangedSubview(view)
}
}
For each index, it has a itemSpecificName and itemSpecificValue. If I need to put these two Labels inside of a View, then I can do that...That will probably fix it, wouldnt it?

UIStackView wants to stretch its arranged subviews to fill its own (the stack view's) bounds. If you don't want the labels stretched, you either need to change the constraints on the UIStackView so that it can shrink to fit its children, or add an arranged subview to the stack view that can absorb the extract space. For example, you can add a plain UIView with no constraints after the two labels. Auto layout will give the extra space of the stack view's bounds to the UIView.

Related

Large titles with UITableView and a UIImage as a background

I am making a financial app. I have a UINavigationController, wrapped around the UIViewController. The large titles in the navigation bar are turned on. I am using a UITableView as a scroller, and a UIImageView with a png image as a background. Now as I scroll the UITableView down, I expect the large titles shift to regular titles. By the way, my navigation bar is somewhat transparent. I am not even trying to ask you guys how to fix the transition from the large titles to regular titles in the navigation bar as I am sure that no one knows how to do that. At least none of the fixes that have been proposed here for 7 years could fix it for me. And as I learned here: https://www.reddit.com/r/iOSProgramming/comments/7k60dl/ios11_large_titles_not_shrinking_tip/, whoever is still trying to figure this problem out, should just abandon all hopes. IT IS IMPOSSIBLE TO PLACE UIIMAGEVIEWE BEHIND THE TABLE VIEW AND EXPECT THE LARGE TITLES TRANSITION TO WORK NORMALLY.
Therefore, I pinned the table to the master view and applied the background to the table directly. The transition started working, but very slowly and with hiccups. But that is not the problem. Since I have a transparent navigation bar, I want the background to occupy the entire screen, and the UITableView to be pinned to a safe area from the top, so nothing goes behind the navigation bar.
This is where the conundrum is. If I pin the background image to the table and make the table pinned to a safe area, whenever I scroll down, the large titles shrink, but the background image is stretching like Stretch Armstrong. It looks stupid. But If I pin the table view to the super view, the scrollable content shows through the navigation bar.
Question: How do I pin the table to a safe area, but make the background extend to super view, and have the large titles/regular titles transition still in place? So that the content doesn’t show behind the transparent navigation bar. Here’s my viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(masterTableViewAsViewScroller)
masterTableViewAsViewScroller.delegate = self
masterTableViewAsViewScroller.dataSource = self
let masterTableViewBackground = UIImageView(image: UIImage(named: "moneyBg"))
masterTableViewBackground.frame = self.masterTableViewAsViewScroller.frame
self.masterTableViewAsViewScroller.backgroundView = masterTableViewBackground;
masterTableViewAsViewScroller.pin(to: view)
title = "Some Title"
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.white]
navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.white]
let image = UIImage.imageFromColor(color: UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.4))
self.navigationController?.navigationBar.setBackgroundImage(image, for: UIBarMetrics.default)
self.navigationController?.navigationBar.barStyle = .default
}
Also, my extensions, which are placed in a separate swift file:
import UIKit
extension UIView {
func pin(to superView: UIView) {
translatesAutoresizingMaskIntoConstraints = false
topAnchor.constraint(equalTo: superView.topAnchor).isActive = true
leadingAnchor.constraint(equalTo: superView.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: superView.trailingAnchor).isActive = true
bottomAnchor.constraint(equalTo: superView.bottomAnchor).isActive = true
}
}
extension UIImage{
static func imageFromColor(color: UIColor) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image! } }
By the way, this is a redo for safe area pinning that I am using:
if #available(iOS 11, *) {
let guide = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
masterTableViewBackground.topAnchor.constraint(equalToSystemSpacingBelow: guide.topAnchor, multiplier: 1.0),
guide.bottomAnchor.constraint(equalToSystemSpacingBelow: masterTableViewBackground.bottomAnchor, multiplier: 1.0)
])
} else {
let standardSpacing: CGFloat = 8.0
NSLayoutConstraint.activate([
masterTableViewBackground.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: standardSpacing),
bottomLayoutGuide.topAnchor.constraint(equalTo: masterTableViewBackground.bottomAnchor, constant: standardSpacing)
])
}
I add gifs so you could see what I'm talking about.

How to add shadow to UICollectionViewCell with clear background color?

I am populating the collection view cells. But only for a single cell, I want a different background color with shadow to the cell. So far I am able to give background color but can't find any idea, how to give shadow. Any suggestions...
cell.backgroundColor = UIColor.init(alpha: 1, red: 255, green: 221, blue: 126)
let view = UIView()
view.frame = CGRect(x: 20, y: cell.frame.origin.y + 62, width: cell.frame.size.width, height: 50)
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 3, height: 3)
view.layer.shadowOpacity = 0.9
view.backgroundColor = UIColor.clear
self.addSubview(view)
return cell
This is what I did
This is what I want
I can give you a way of doing it.
Give your cell a clear color
Add a child view in it make sure to give proper constrains (make sure to keep the child view inside the cell).
Add all labels or any other views inside the new child view
Give shadows and background color to the child view.
(make sure to toggle the color when needed just as selected color)
I am sure this will definitely look the image you have sown

Dynamically size UIStackView to the width of the UIViews inside

I am trying to create a UIStackView with three UIViews inside. The UIViews will have a circle with text over / in it.
I would like not to set the StackView to a static number, i would like it to be able to get smaller/grow based on the device the user is using.
Right now, the StackView is being added to the view, and the UIViews are being added to that. The colors are being displayed, but the rounded circles are not and the StackView height is not equal to the leftui's width.
Basically, I need three circles of equal height and width....is there a better way for this?
Here is my code.
#IBOutlet var stack: UIStackView!
override func viewWillLayoutSubviews() {
//let stack = UIStackView()
let leftui = UIView()
let middleui = UIView()
let rightui = UIView()
stack.addArrangedSubview(leftui)
stack.addArrangedSubview(middleui)
stack.addArrangedSubview(rightui)
leftui.backgroundColor = UIColor.red
middleui.backgroundColor = UIColor.blue
rightui.backgroundColor = UIColor.brown
leftui.bounds.size.height = leftui.bounds.width //needs these to new equal
middleui.bounds.size.height = middleui.bounds.width //needs these to new equal
rightui.bounds.size.height = rightui.bounds.width //needs these to new equal
leftui.layer.cornerRadius = leftui.bounds.size.width / 2
middleui.layer.cornerRadius = middleui.bounds.size.width / 2
rightui.layer.cornerRadius = rightui.bounds.size.width / 2
print(leftui.bounds.size.width) //prints 0.0
leftui.clipsToBounds = true
middleui.clipsToBounds = true
rightui.clipsToBounds = true
stack.sizeToFit()
stack.layoutIfNeeded()
view.addSubview(stack)
}
Here is what I was looking for.
This is from the android version of the application.
I think that in order for UIStackView to work its arrangedSubviews have to use autolayout - Check first answer here: Is it necessary to use autolayout to use stackview
This is how you could solve this:
Add a new class for your circular views, these do not do much other than set its layer.cornerRadius to half of their width, so that if height and width are the same they will be circular.
class CircularView: UIView {
override func layoutSubviews() {
super.layoutSubviews()
clipsToBounds = true
layer.cornerRadius = bounds.midX
}
}
You add a widthConstraint with which you will be able to size the elements in the stack view
var widthConstraint: NSLayoutConstraint!
You can then create the UIStackView, I used your code mostly to do this:
override func viewDidLoad() {
super.viewDidLoad()
let leftui = CircularView()
let middleui = CircularView()
let rightui = CircularView()
leftui.translatesAutoresizingMaskIntoConstraints = false
middleui.translatesAutoresizingMaskIntoConstraints = false
rightui.translatesAutoresizingMaskIntoConstraints = false
leftui.backgroundColor = UIColor.red
middleui.backgroundColor = UIColor.blue
rightui.backgroundColor = UIColor.brown
let stack = UIStackView(arrangedSubviews: [leftui, middleui, rightui])
stack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stack)
widthConstraint = leftui.widthAnchor.constraint(equalToConstant: 100)
NSLayoutConstraint.activate([
widthConstraint,
stack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
leftui.heightAnchor.constraint(equalTo: leftui.widthAnchor, multiplier: 1.0),
middleui.widthAnchor.constraint(equalTo: leftui.widthAnchor, multiplier: 1.0),
middleui.heightAnchor.constraint(equalTo: leftui.widthAnchor, multiplier: 1.0),
rightui.widthAnchor.constraint(equalTo: leftui.widthAnchor, multiplier: 1.0),
rightui.heightAnchor.constraint(equalTo: leftui.widthAnchor, multiplier: 1.0)
])
}
Given the constraints set here, circles will have a width/height of 100 and stack view is centred in the view.
Next if you want to do something when view rotates you could implement something like this in your viewController
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animate(alongsideTransition: { _ in
if size.width > size.height {
self.widthConstraint.constant = 150
} else {
self.widthConstraint.constant = 100
}
}, completion: nil)
}
It would animate to circles of width/height of 150 in landscape. You can then play with these values to get desired outcome.
To design this, follow the below steps.
create a custom view. in the custom view put all the subviews like
cost title label, price label and the color UIImageView
Now create three object of the custom view with proper data.
Get the device screen width divide by 3 gives each custom view
width, also set the view height as per your requirement and provide
frame for the created custom view
Now add the three views to the StackView.
Hope this will help to design, if you need any more help please comment.

Why is image view stretching to fill half the width in a UIStackView?

I am taking an UIImageView on the left of the horizontal stack view and a label with number of lines set to 0. The issue is that both the UIImageView and the label are filling equal space even though I have set the stack view distribution property to fill. I don't want my image view to expand. Changing content hugging isn't helping.
Problem is: When telling the stack view to fill the content area it try to do it as best as possible. When setting the distribution type to fill it do not know how to fill. You have to provide this information by setting a width constraint via autolayout for the image. The label will fill the remaining area then.
Another way of doing this would be to add an invisible spacer object at the right side of the image + label stack.
private func spacer() -> UIView {
let stretchingView = UIView()
stretchingView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
stretchingView.backgroundColor = UIColor.clear
stretchingView.translatesAutoresizingMaskIntoConstraints = false
return stretchingView
}
private func labelWithImage() -> UIView {
let image = UIImage(systemName: "map")
let imageView = UIImageView(image: image)
let label = UILabel()
label.text = "3 Miles Away"
label.font = UIFont.systemFont(ofSize: 17, weight: .regular)
label.textColor = UIColor.gray
let stackView = UIStackView(arrangedSubviews: [imageView, label, spacer()])
stackView.alignment = .leading
stackView.axis = .horizontal
return stackView
}

Button in the footer of tableview section Swift3

I have a footer in the section of my tableView.
I try to add a button, in the center, and after to center my button.
I didn't find how I can center my button, I try to use view.center, or give him the width of all the parent view and after use a text align for center my button. (Personally I prefer use the second method .)
Actually my code is this one
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = UIView()
let labelMore = UIButton()
footerView.addSubview(labelMore)
labelMore.sizeToFit()
labelMore.setTitle(footerText(status: "Test"), for: .normal)
labelMore.backgroundColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
footerView.backgroundColor = #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1)
//labelMore.addTarget(self, action:#selector(footerMore(sender:)), for: .touchUpInside)
return footerView
}
And I got, the green square is actually my button, I erase from my code labelMore.frame = footerView.frame, because that don't work
edit: If my question is not clear I want center my button.
None of the code you showed "centers" the button, so it is not surprising that it is not centered. You do not set the frame.origin of labelMore at all, so its origin is zero — the top left corner, exactly as shown in the screen shot.
I erase from my code labelMore.frame = footerView.frame
Yes, well that was never going to work. It's just another case of confusing frame and bounds. Remember, the frame of the subview is in terms of the bounds of the superview. So you would change that code to
labelMore.frame = footerView.bounds
However, that isn't going to work either because footerView has no bounds — you haven't given it any size!
The real solution here is to position labelMore using autolayout. That way, no matter how footerView ends up being sized, labelMore will have the correct position within it. For example, let's assume that you want labelMore to be the same size as footerView:
let footerView = UIView()
let labelMore = UIButton()
footerView.addSubview(labelMore)
labelMore.topAnchor.constraint(equalTo: footerView.topAnchor).isActive = true
labelMore.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true
labelMore.leadingAnchor.constraint(equalTo: footerView.leadingAnchor).isActive = true
labelMore.trailingAnchor.constraint(equalTo: footerView.trailingAnchor).isActive = true
labelMore.translatesAutoresizingMaskIntoConstraints = false
// ... and remove the `sizeToFit` ...

Resources