IOS 11 only: Navigation bar label off on top - ios

IOS 11 is causing the main label to move a little bit from the top rather than keeping to the top. The problem only occurs on IOS 11. With different IOS everything looks ok.
Code sample with a setting header:
private func setHeader(agentName: String = "", isTyping: Bool = false) -> Void {
if (agentName.isEmpty) {
self.containerViewController?.navigationItem.titleView = nil
} else {
let headerView: UIView = {
let rect = CGRect(x: 0, y: 0, width: 320, height: 44)
let uiview = UIView(frame: rect)
return uiview
}()
let headerLabel: UILabel = {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: headerView.frame.width, height: 20))
label.font = UIFont.systemFont(ofSize: 18)
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let subheaderLabel: UILabel = {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: headerView.frame.width, height: 12))
label.font = UIFont.systemFont(ofSize: 12)
label.translatesAutoresizingMaskIntoConstraints = false
label.text = self.title
return label
}()
headerView.addSubview(headerLabel)
headerView.addSubview(subheaderLabel)
let viewsDictionary = ["header": headerLabel, "subheader": subheaderLabel]
headerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[header]|", options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
headerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[subheader]|", options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
headerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[header(20)]-[subheader]", options: [], metrics: nil, views: viewsDictionary))
self.containerViewController?.navigationItem.titleView = headerView
}
}

Beginning with iOS 11, views add to toolbars are now laid out using auto layout. You should add sizing constraints on your headerView. For example:
headerView.widthAnchor.constraintEqualToConstant(320.0).isActive = true
headerView.heightAnchor.constraintEqualToConstant(44.0).isActive = true
Otherwise, auto layout will use the intrinsic content size of your header view which is likely not what you expect.
For more information see the WWDC 2017 session Updating your app for iOS 11.

#beyowulf said that you have to add sizing constraints. I think in your case adding height constraint is okay :
headerView.heightAnchor.constraint(equalToConstant: 22.0).isActive = true

Related

Setting height of UiLabel inside UiStackview Using swift

I am dynamically adding views in the UiStackview. Since UiStackview is not a regular view, SO I can not add a bottom border to it. That is why I have planned to add a UILabel at the end of it
The label that I will add at the end of my UIStackview will be dealt as a border. I was thinking to make its height as 1point. and give it a background color. And expand its height to full width of screen.
But its height is not getting controlled. Can anyone tell me what the problem is?
Here is the little code snippet
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 10, height: 1))
label.backgroundColor = UIColor.black
label.text = ""
bottomBorder.addArrangedSubview(label)
I am adding this in the end of the main stackview. it gets added in the main stackview but with the height of I think 30 point. Or may be its the default height of the UiLabel
My questions are:
How to add UiLabel or a border at the end of stackview (vertical alignment)
Is there any way that I can add border to my stackview directly? Four side border or at least bottom border?
You have 2 alternatives to achieve this:
1- put the stackview inside a UIView parentView
2- do not add UILabel directly to the UIStackView, add the UILabel to UIView then add the UIView to the UIStackView, so that you can whatever separators you want to the UIView.
Code to do that:
override func viewDidLoad()
{
super.viewDidLoad()
stackView.distribution = .fillEqually
let v1 = getViewForStackView(lblText: "lbl1")
let v2 = getViewForStackView(lblText: "lbl2")
stackView.addArrangedSubview(v1)
stackView.addArrangedSubview(v2)
}
func getViewForStackView(lblText:String)->UIView
{
let rectView = CGRect(x: 0, y: 0, width: 100, height: 100)
let view = UIView(frame: rectView)
view.backgroundColor = .green
let label = UILabel()
label.text = lblText
label.backgroundColor = .red
addFillingSubview(parentView: view, subview: label, insets: UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8))
return view
}
func addFillingSubview(parentView:UIView, subview: UIView, insets: UIEdgeInsets = .zero)
{
subview.translatesAutoresizingMaskIntoConstraints = false
parentView.addSubview(subview)
let views = ["subview": subview]
let metrics = ["top": insets.top, "left": insets.left, "bottom": insets.bottom, "right": insets.right]
parentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|-(left)-[subview]-(right)-|", options: [], metrics: metrics, views: views))
parentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(top)-[subview]-(bottom)-|", options: [], metrics: metrics, views: views))
}
Output:
Use this class
class BorderedStackView: UIStackView {
let borderWidth : Int = 2
let borderColor : UIColor = UIColor.darkGray;
var borderView : UIView!
required init(coder: NSCoder) {
super.init(coder: coder);
initializeSubviews();
}
required override init(frame: CGRect) {
super.init(frame: frame);
initializeSubviews();
}
func initializeSubviews() {
borderView = UIView.init();
borderView.backgroundColor = UIColor.black;
self.addSubview(borderView);
}
override func layoutSubviews() {
super.layoutSubviews();
var frame = self.bounds;
frame.origin.y = frame.size.height - CGFloat.init(borderWidth)
frame.size.height = CGFloat.init(borderWidth);
self.borderView.frame = frame;
self.bringSubview(toFront: self.borderView)
}
}
Uses:
let stackView = BorderedStackView.init(frame: CGRect.init(x: 50, y: 50, width: 200, height: 200));
stackView.axis = .vertical
stackView.borderWidth = 20;
stackView.borderColor = .red;
self.view.addSubview(stackView);
let subView1 = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 200, height: 100));
subView1.backgroundColor = UIColor.red;
stackView.addSubview(subView1);
let subView2 = UIView.init(frame: CGRect.init(x: 0, y: 100, width: 200, height: 100));
subView2.backgroundColor = UIColor.black;
stackView.addSubview(subView2);

CGRect X no effect in the TextField

If you see my code I am trying to add 13 px padding from left of the city label in the textfield but it is not working. I am not sure what is going on there.
Here Is my textField
var cityTextField : RightPaddingTextField = {
var textField = RightPaddingTextField()
textField.textAlignment = .right
textField.translatesAutoresizingMaskIntoConstraints = false
var label = UILabel(frame: CGRect(x: 13, y: 0, width: 44, height: 44))
label.text = "City"
textField.leftViewMode = .always
textField.leftView = label
return textField
}()
Here setup constraints:
func cityLabelWithTextFieldSetup(){
addSubview(cityTextField)
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0" : cityTextField]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0" : cityTextField]))
}
I am trying to achieve it like in this image but it's not working.
UPDATE
here is my code output please check only city row ..

UILabel vertical constraints with NSLayoutConstraints (swift 3 xcode)

Just started using NSLayoutConstraints and for some reason I can't get the vertical constraints to work. Whenever I run my application the label sits centered vertically in the view, regardless of what value I give it.
Here is the current code:
override func viewDidLoad() {
super.viewDidLoad()
print(self.view.bounds)
edgesForExtendedLayout = []
setupViews()
}
let headerTitleLabel: UILabel = {
let label = UILabel()
label.text = "Projects"
label.font = UIFont.boldSystemFont(ofSize: 14)
label.translatesAutoresizingMaskIntoConstraints = false
label.sizeToFit()
return label
}()
func setupViews() {
let v1 = UIView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 200))
v1.backgroundColor = UIColor.red
self.view.addSubview(v1)
let v2 = UITableView(frame: CGRect(x: 0, y: v1.frame.height, width: view.bounds.width, height: (view.bounds.height-v1.frame.height)))
v2.backgroundColor = UIColor.blue
self.view.addSubview(v2)
v1.addSubview(headerTitleLabel)
v1.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-50-[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": headerTitleLabel]))
v1.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-8-[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": headerTitleLabel]))
}

Expandable cell, self sizing height

I made expandable view with dynamic amount of UILabels (created progammatically according to the count of results query produces). However I am not able to resize the expanded view height according to how many UILabels I have in the expanded view.
Using for loop I make as many labels as there are results after query.
Now I put them into view like this:
for (index, _) in timesBetween2Stations.enumerated() {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 20))
cell.ExpandableView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.center = CGPoint(x: cell.ExpandableView.frame.width / 2, y: 0)
label.textAlignment = NSTextAlignment.center
var substationTime = timesBetween2Stations[index]?.time
let substation = timesBetween2Stations[index]?.station
substationTime = substationTime!.removeTrainTimeZeros()
label.text = substation! + " " + substationTime!
cell.ExpandableView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-(10)-[label]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["label": label, "expandable": cell.ExpandableView]))
if (previousLabel == nil){
cell.ExpandableView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(10)-[label]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["label": label, "expandable": cell.ExpandableView]))
}
else {
cell.ExpandableView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[previousLabel]-(10)-[label]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["label": label, "previousLabel": previousLabel as Any]))
}
previousLabel = label
}
Question is - why it's not resizing the view height according to the constraints? :)
Try this:
cell.setNeedsLayout()
cell.layoutIfNeeded()
or
cell.setNeedsUpdateConstraints()
cell.updateConstraintsIfNeeded()
after lor.

addTarget and addGestureRecognizer not working, no crash/error

I a have an overlay with a table in and I'd like to add a Tap gesture recogniser to the background to dismiss the view and also addTarget to a button within the overlay which does the same thing.
The overlay displays fine as expected, however whenever I tap the black background or the cancel button, nothing happens. I've searched for an answer here but nothing found has worked. My code is as follows, followed by a screenshot of the overlay:
class importedFileView: NSObject {
let blackView = UIView()
let importedFileContainerView: UIView = {
let importedFileContainerView = UIView(frame: .zero)
importedFileContainerView.backgroundColor = .white
importedFileContainerView.layer.cornerRadius = 10
importedFileContainerView.layer.masksToBounds = true
return importedFileContainerView
}()
let headerLabel: UILabel = {
let headerLabel = UILabel()
headerLabel.translatesAutoresizingMaskIntoConstraints = false
headerLabel.font = UIFont(name: "HelveticaNeue-Thin" , size: 24)
headerLabel.text = "Attach file"
headerLabel.textColor = .darkGray
headerLabel.adjustsFontSizeToFitWidth = true
return headerLabel
}()
let fileTableView: UITableView = {
let fileTableView = UITableView()
return fileTableView
}()
let updateDetailsButton: UIButton = {
let updateDetailsButton = UIButton()
updateDetailsButton.translatesAutoresizingMaskIntoConstraints = false
updateDetailsButton.backgroundColor = UIColor(r:40, g:86, b:131)
updateDetailsButton.setTitleColor(UIColor.white, for: .normal)
updateDetailsButton.setTitle("Attach selected files", for: .normal)
updateDetailsButton.titleLabel!.font = UIFont(name: "HelveticaNeue-Light" , size: 18)
updateDetailsButton.layer.cornerRadius = 2
return updateDetailsButton
}()
let cancelButton: UIButton = {
let cancelButton = UIButton()
cancelButton.translatesAutoresizingMaskIntoConstraints = false
cancelButton.backgroundColor = UIColor.white
cancelButton.setTitleColor(UIColor(r:40, g:86, b:131), for: .normal)
cancelButton.setTitle("Cancel", for: .normal)
cancelButton.titleLabel!.font = UIFont(name: "HelveticaNeue-Light" , size: 18)
cancelButton.layer.cornerRadius = 2
return cancelButton
}()
let frameHeight: CGFloat = 450
func showFormView(){
if let window = UIApplication.shared.keyWindow {
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
window.addSubview(blackView)
window.addSubview(importedFileContainerView)
importedFileContainerView.addSubview(headerLabel)
importedFileContainerView.addSubview(fileTableView)
importedFileContainerView.addSubview(updateDetailsButton)
importedFileContainerView.addSubview(cancelButton)
cancelButton.addTarget(self, action: #selector(handleDismiss), for: .touchUpInside)
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
layoutViews()
fileTableView.frame = CGRect(x: 30, y: window.frame.height, width: window.frame.width - 60, height: 230)
let frameY = (window.frame.height - frameHeight) / 2
importedFileContainerView.frame = CGRect(x: 20, y: window.frame.height, width: window.frame.width - 40, height: self.frameHeight)
blackView.frame = window.frame
blackView.alpha = 0
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.blackView.alpha = 1
self.importedFileContainerView.frame = CGRect(x: 20, y: frameY, width: window.frame.width - 40, height: self.frameHeight)
}, completion: nil
)
}
}
func layoutViews(){
let views = ["v0" : headerLabel, "v1": fileTableView, "v2": updateDetailsButton, "v3": cancelButton]
let leftSpace = NSLayoutConstraint.constraints(withVisualFormat: "H:|-20.0-[v0]-20.0-|", options: NSLayoutFormatOptions(), metrics: nil, views: views)
let leftSpace1 = NSLayoutConstraint.constraints(withVisualFormat: "H:|-20.0-[v1]-20.0-|", options: NSLayoutFormatOptions(), metrics: nil, views: views)
let leftSpace2 = NSLayoutConstraint.constraints(withVisualFormat: "H:|-20.0-[v2]-20.0-|", options: NSLayoutFormatOptions(), metrics: nil, views: views)
let leftSpace3 = NSLayoutConstraint.constraints(withVisualFormat: "H:|-20.0-[v3]-20.0-|", options: NSLayoutFormatOptions(), metrics: nil, views: views)
let topSpacing = NSLayoutConstraint.constraints(withVisualFormat: "V:|-20.0-[v0(40)]-20.0-[v1(230)]-20.0-[v2(50)]-10.0-[v3(50)]-10.0-|", options: NSLayoutFormatOptions(), metrics: nil, views: views)
importedFileContainerView.addConstraints(topSpacing)
importedFileContainerView.addConstraints(leftSpace)
importedFileContainerView.addConstraints(leftSpace1)
importedFileContainerView.addConstraints(leftSpace2)
importedFileContainerView.addConstraints(leftSpace3)
}
func handleDismiss() {
UIView.animate(withDuration: 0.5,
delay: 0.0,
options: .curveEaseInOut,
animations: {
self.blackView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.importedFileContainerView.frame = CGRect(x: 20, y: window.frame.height, width: window.frame.width - 40, height: self.frameHeight)
}
},
completion: { [weak self] finished in
self?.blackView.removeFromSuperview()
self?.importedFileContainerView.removeFromSuperview()
})
}
override init() {
super.init()
}
}
self.blackView.isUserInteractionEnabled = true;
is all you need to add to the blackView (UIView).
Without that, the view doesn't have any interactions enabled and so the gesture recognizer's target/action isn't triggered.
Events are ignored.
https://developer.apple.com/reference/uikit/uiview/1622577-isuserinteractionenabled
You might also want to disable it during animations.
Are you keeping a strong reference to the instance of your importedFileView while your overlays are visible? As far as I tested, all actions are silently ignored when the target is lost.
For example, this does not work:
#IBAction func someAction(_ sender: Any) {
let ifv = importedFileView()
ifv.showFormView()
//`ifv` is released at the end of this method, then your overlays are shown...
}
This works:
let ifv = importedFileView() //keep the instance as a property of your ViewController.
#IBAction func someAction(_ sender: Any) {
ifv.showFormView()
}
Programmatically generated UIViews isUserInteractionEnabled is default to true. You have no need to explicitly set it to true.
By the way, you'd better not name your non-UIView class as ...View, and better make your type names start with capital letter, which makes your code more readable to experienced Swift programmers.

Resources