I am implementing a UIScrollView which holds some user data. I am downloading additional data from my server. Once downloaded the contentSize of my UIScrollView should be the original size + the number of cells times their height.
All the UIViews are aligned and set programmatically.
The UITableView is the lowest and is supposed to always stretch to the bottom of the screen.
In my viewDidLayoutSubviews I set the following
groupsCountLbl.frame = CGRectMake(groupsLbl.frame.maxX + 5,
seperatorLbl.frame.maxY + 15 , 40, 20)
groupsCountLbl.sizeToFit()
table.frame = CGRectMake(0, groupsCountLbl.frame.maxY + 10,
bounds.width, bounds.height - groupsCountLbl.frame.maxY + 10)
scrollView.contentSize = CGSizeMake(bounds.width, bounds.height
+ self.table.frame.height)
scrollView.scrollEnabled = true
Rather than filling the whole view it appears like this:
Try setting your UIScrollView bounds to superview
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|-[scrollview]-|",
options: [],
metrics: nil,
views: ["scrollview": scrollview])
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-[scrollview]-|",
options: [],
metrics: nil,
views: ["scrollview": scrollview])
Let me know in the comment if you have some problems
Related
I'm trying to draw a a line at the top of a table view as follows:
func addTopLineToTableView() {
let layer = CALayer()
layer.backgroundColor = UIColor.green.cgColor
layer.frame = CGRect(x: tableView.frame.origin.x, y: tableView.frame.origin.y, width: tableView.frame.size.width, height: 1.0)
view.layer.addSublayer(layer)
}
This works perfectly on every device except an iPhone X.
I've tried getting the safeAreaInsets and the safeAreaLayoutGuide but that didn't seem to do anything.
Any help greatly appreciated!
EDIT
Above is what the end result should look like. Here the table view has a red background and my green CALayer is right at the top as expected. It has been placed there using the function above. This is the way it looks on every device except the iPhone X.
Here is what happens when the same code is run on an iPhone X.
Like friends said before, the line can be set to the parts, you can extend the view
extension UIView {
#IBInspectable var topBorderWidth: CGFloat {
get {
return 0.0 // Just to satisfy property
}
set {
let line = UIView(frame: CGRect(x: 0.0, y: 0.0, width: bounds.width, height: newValue))
line.translatesAutoresizingMaskIntoConstraints = false
line.backgroundColor = UIColor.green
self.addSubview(line)
let views = ["line": line]
let metrics = ["lineWidth": newValue]
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|[line]|", options: [], metrics: nil, views: views))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[line(==lineWidth)]", options: [], metrics: metrics, views: views))
}
}
}
Such use
testView.topBorderWidth = 1
I'm working on a project that has a custom segmented control in a UIScrollView. I want to use auto layout to position the segmented control. I'm using this project as my model: https://github.com/honghaoz/UIScrollView-and-AutoLayout
import UIKit
class ViewController: UIViewController {
let scrollView = UIScrollView()
var screenBounds: CGRect { return UIScreen.main.bounds }
override var preferredStatusBarStyle : UIStatusBarStyle {
return .lightContent
}
override func viewDidLoad() {
super.viewDidLoad()
// Setup scroll view
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = UIColor(red:20/255.0, green:119/255.0, blue:61/255.0, alpha:255/255.0)
view.addSubview(scrollView)
// Setup constraints
var views: [String: UIView] = [
"scrollView": scrollView
]
var constraints = [NSLayoutConstraint]()
// External constraints
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[scrollView]|", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[scrollView]|", options: [], metrics: nil, views: views)
// Internal constraints
// Note: to let scrollView determines its contentSize, four edges should be explicitly specified
let v1 = newLabelWithText("v1 v1 v1 v1 v1 v1 v1 v1 v1 v1")
scrollView.addSubview(v1)
views["v1"] = v1
//Create Segmented Control
let segmentedB = YSSegmentedControl(
//Set Frame in Callback (Required)
frame: CGRect(x: 0, y: 0, width: 400, height: 60),
titles: [
"Yes",
"No"
],
action: {
control, index in
print ("segmented did pressed \(index)")
})
scrollView.addSubview(segmentedB)
views["v4"] = segmentedB
let v2 = newLabelWithText("v2 v2 v2 v2 v2")
views["v2"] = v2
let v3 = newLabelWithText("v3 v3 v3 v3 v3")
views["v3"] = v3
scrollView.addSubview(v3)
// Horizontal, fully specified
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-200-[v4]-300-|", options: .alignAllLastBaseline, metrics: nil, views: views)
// Vertically, fully specified
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-200-[v3]-1000-|", options: .alignAllLeading, metrics: nil, views: views)
NSLayoutConstraint.activate(constraints)
print("scrollView.contentSize: \(scrollView.contentSize)")
}
}
For some reason, auto layout works just fine with the text labels but not with the segmented control. The callback requires that I input parameters for CGRect, so I simply set the width and height and leave the position parameters at zero. This worked when there was no scrollview, but its not working now.
Also, I'm not using storyboards in this project. All constraints and views are created programmatically.
Any Help appreciated. Thanks
I think you should add this line before adding constraints
segmentedB.translatesAutoresizingMaskIntoConstraints = false
Whenever you add constraints programmatically you should set this property to false in order for constraints to take effect on the view
I have a UIViewController with four UIButtons (2 x 2) on it that I laid out in interface builder that worked perfectly. I'm going to have a free and ad-supported version of my app, so I need to redo that scene to load based on whether the app is a paid or ad-supported version. Based on that, I'm attempting to use Visual Formatting Language to lay out the view. I'm getting incorrect values for my UIButton heights despite accounting for them when I calculate them. I can't figure out my mistake (or omission?).
Here's a screenshot of my interface builder.
I do not have constraints on my buttons in interface builder, but I do have IBOutlets wired to MyViewController. MyViewController is in a navigation controller and has a tab bar at the bottom.
I created a method called layoutButtons that I call in viewDidLoad just after super.viewDidLoad(). Here it is:
func layoutButtons() {
// Configure layout constraints
// Remove interface builder constraints from storyboard
view.removeConstraints(view.constraints)
// create array to dump constraints into
var allConstraints = [NSLayoutConstraint]()
// determine screen size
let screenSize: CGRect = UIScreen.mainScreen().bounds
let navBarRect = navigationController!.navigationBar.frame
let navBarHeight = navBarRect.height
let tabBarRect = tabBarController!.tabBar.frame
let tabBarHeight: CGFloat = tabBarRect.height
// calculate button width based on screen size
// padding for left + middle + right = 8.0 + 8.0 + 8.0 = 24.0
let buttonWidth = (screenSize.width - 24.0) / 2
/*
My buttons are extending under the top & bottom layout guides despite accounting
for them when I set the buttonHeight.
*/
// padding for top + middle + bottom = 8.0 + 8.0 + 8.0 = 24.0
let buttonHeight = (screenSize.height - topLayoutGuide.length - bottomLayoutGuide.length - 24.0) / 2
// create dictionary of metrics
let metrics = ["buttonWidth": buttonWidth,
"buttonHeight": buttonHeight,
"navBarHeight": navBarHeight,
"tabBarHeight": tabBarHeight,
"bannerAdWidth": bannerAdWidth,
"bannerAdHeight": bannerAdHeight]
// create dictionary of views
var views: [String : AnyObject] = ["firstButton": firstButton,
"secondButton": secondButton,
"thirdButton": thirdButton,
"fourthButton": fourthButton,
"topLayoutGuide": topLayoutGuide,
"bottomLayoutGuide": bottomLayoutGuide]
let topRowHorizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-[firstButton(buttonWidth)]-[secondButton(buttonWidth)]-|",
options: [.AlignAllCenterY],
metrics: metrics,
views: views)
allConstraints += topRowHorizontalConstraints
let bottomRowHorizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-[thirdButton(buttonWidth)]-[fourthButton(buttonWidth)]-|",
options: [.AlignAllCenterY],
metrics: metrics,
views: views)
allConstraints += bottomRowHorizontalConstraints
let leftColumnVerticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[topLayoutGuide]-[firstButton(buttonHeight)]-[thirdButton(buttonHeight)]-[bottomLayoutGuide]|",
options: [],
metrics: metrics,
views: views)
allConstraints += leftColumnVerticalConstraints
let rightColumnVerticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[topLayoutGuide]-[secondButton(buttonHeight)]-[fourthButton(buttonHeight)]-[bottomLayoutGuide]|",
options: [],
metrics: metrics,
views: views)
allConstraints += rightColumnVerticalConstraints
NSLayoutConstraint.activateConstraints(allConstraints)
}
I've fiddled with my buttonHeight variable, but every iteration I've tried results in the buttons extending under the topLayoutGuide and bottomLayoutGuide. Here's what it looks like at runtime:
I welcome any suggestions where to look for my mistake. Thank you for reading.
I originally followed a Ray Wenderlich's Visual Format Language tutorial and the tutorial's setup was similar to mine in that the subViews were on a storyboard and wired to MyViewController with IBOutlets.
Out of desperation, I nuked the storyboard and created the UIButtons in code. Along the way, I discovered my problem was I was setting the image on the button before it was laid out. The moral of the story is don't set images on UIButtons until they're laid out!
Below is the code called from a method in my viewDidLoad that lays out a 2 x 2 grid of buttons:
// Configure Buttons
firstButton.translatesAutoresizingMaskIntoConstraints = false
// code to customize button
secondButton.translatesAutoresizingMaskIntoConstraints = false
// code to customize button
thirdButton.translatesAutoresizingMaskIntoConstraints = false
// code to customize button
fourthButton.translatesAutoresizingMaskIntoConstraints = false
// code to customize button
// Add buttons to the subview
view.addSubview(firstButton)
view.addSubview(secondButton)
view.addSubview(thirdButton)
view.addSubview(fourthButton)
// create views dictionary
var allConstraints = [NSLayoutConstraint]()
let views: [String : AnyObject] = ["firstButton": firstButton,
"secondButton": secondButton,
"thirdButton": thirdButton,
"fourthButton": fourthButton,
"topLayoutGuide": topLayoutGuide,
"bottomLayoutGuide": bottomLayoutGuide]
// Calculate width and height of buttons
// 24.0 = left padding + middle padding + right padding
let buttonWidth = (screenSize.width - 24.0) / 2
let buttonHeight = (screenSize.height - topLayoutGuide.length - bottomLayoutGuide.length - 24.0) / 2
// Create a dictionary of metrics
let metrics = ["buttonWidth": buttonWidth,
"buttonHeight" : buttonHeight]
let topVerticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[topLayoutGuide]-[firstButton(buttonHeight)]",
options: [],
metrics: metrics,
views: views)
allConstraints += topVerticalConstraints
let topRowHorizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-[firstButton(buttonWidth)]-[secondButton(==firstButton)]-|",
options: NSLayoutFormatOptions.AlignAllCenterY,
metrics: metrics,
views: views)
allConstraints += topRowHorizontalConstraints
let bottomRowHorizontalContraints = NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-[thirdButton(buttonWidth)]-[fourthButton(==thirdButton)]-|",
options: NSLayoutFormatOptions.AlignAllCenterY,
metrics: metrics,
views: views)
allConstraints += bottomRowHorizontalContraints
let leftColumnVerticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:[firstButton]-[thirdButton(==firstButton)]-[bottomLayoutGuide]-|",
options: [],
metrics: metrics,
views: views)
allConstraints += leftColumnVerticalConstraints
let rightColumnVerticalConstriants = NSLayoutConstraint.constraintsWithVisualFormat(
"V:[secondButton(buttonHeight)]-[fourthButton(==secondButton)]-[bottomLayoutGuide]-|",
options: [],
metrics: metrics,
views: views)
allConstraints += rightColumnVerticalConstriants
NSLayoutConstraint.activateConstraints(allConstraints)
// ** DON'T SET IMAGES ON THE BUTTONS UNTIL THE BUTTONS ARE LAID OUT!!!**
firstButton.imageView?.contentMode = UIViewContentMode.ScaleAspectFit
firstButton.setImage(UIImage(named: "first.png"), forState: .Normal)
secondButton.imageView?.contentMode = UIViewContentMode.ScaleAspectFit
secondButton.setImage(UIImage(named: "second.png"), forState: .Normal)
thirdButton.imageView?.contentMode = UIViewContentMode.ScaleAspectFit
thirdButton.setImage(UIImage(named: "third.png"), forState: .Normal)
fourthButton.imageView?.contentMode = UIViewContentMode.ScaleAspectFit
fourthButton.setImage(UIImage(named: "fourth.png"), forState: .Normal)
I made a a custom keyboard toolBar:
To do that I created a toolbar
let keyboardToolbar = UIToolbar(frame: CGRectMake(0, 0, self.view.bounds.size.width, 44))
and a view for the banner
adToolbar = GADBannerView(frame: CGRectMake(0, 44, self.view.bounds.size.width, 44))
then I grouped them in another UIToolbar (I tried UIView too)
let clusterView = UIToolbar(frame: CGRectMake(0, 0, self.view.bounds.size.width, 88))
clusterView.addSubview(adToolbar)
clusterView.addSubview(keyboardToolbar)
and I added the view to the UITextField's keyboard.
Everything ok, but when I rotate the device happens this:
(clusterView UIToolbar resize correctly but not the two contained bars...)
I tried with
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
methodWhichGeneratesToolbar()
}
But it's the same, what can I try to solve this issue?
P.S.: I've made an example project.
Here's a suggestion on how to do it with programmatic autolayout using Visual Format Language (VFL). You'll need to have a look at the VFL docs to understand the VFL string syntax (They are pinning the outer view to the top and sides of the main view, and pinning the two subviews inside and to each other, and setting their height to 44).
I don't have AdMob installed, so I used a regular UIView instead of the banner view, but hopefully it should resize similarly - this code works ok on the 9.2 simulator in a test app
let keyboardToolbar = UIToolbar(frame: CGRectMake(0, 0, self.view.bounds.size.width, 44))
keyboardToolbar.translatesAutoresizingMaskIntoConstraints = false //This is critical for all programmatic autolayout - if you forget it nothing will work
let adToolbar = UIView(frame: CGRectMake(0, 44, self.view.bounds.size.width, 44))
adToolbar.translatesAutoresizingMaskIntoConstraints = false
let clusterView = UIToolbar(frame: CGRectMake(0, 0, self.view.bounds.size.width, 88))
clusterView.translatesAutoresizingMaskIntoConstraints = false
//Map views to keys used in visual format language strings
let views = ["keyboardToolbar":keyboardToolbar,"adToolbar":adToolbar,"clusterView":clusterView]
//Map values to strings used in vfl strings
let metrics = ["barHeight":44]
//In named variables to make it clear what they are
//Syntax is explained in link above
let verticalConstraintsStr = "V:|[keyboardToolbar(barHeight)][adToolbar(barHeight)]|"
let adHorizontalConstraintsStr = "|[adToolbar]|"
let keyboardHorizontalConstraintsStr = "|[keyboardToolbar]|"
let subViewConstraintStrs = [
verticalConstraintsStr,
adHorizontalConstraintsStr,
keyboardHorizontalConstraintsStr
]
//Views must be added to subviews before adding constraints
// if the superview is referenced using
//the | symbol in the VFL strings
clusterView.addSubview(keyboardToolbar)
clusterView.addSubview(adToolbar)
//Converts strings to constraints for subviews and add them
for constraintStr in subViewConstraintStrs {
let allConstraints = NSLayoutConstraint.constraintsWithVisualFormat(constraintStr, options: NSLayoutFormatOptions(rawValue: 0), metrics: metrics, views: views)
clusterView.addConstraints(allConstraints)
}
let clusterVerticalConstraintsStr = "V:|[clusterView]" //Note no | at the end - no bottom pin
let clusterHorizontalConstraintsStr = "|[clusterView]|"
view.addSubview(clusterView)
//Same process for the enclosing view
for constraintStr in [clusterVerticalConstraintsStr,clusterHorizontalConstraintsStr] {
let allConstraints = NSLayoutConstraint.constraintsWithVisualFormat(constraintStr, options: NSLayoutFormatOptions(rawValue: 0), metrics: metrics, views: views)
view.addConstraints(allConstraints)
}
VFL is powerful but annoying to debug, and can't do all types of constraint (e.g. not alignments - you have to use an even more verbose API for those).
i have a custom tableCell that working fine, till i am trying to add an image on the left of each cell.
when placing the image inside the conventView all is working fine, but i don't like when the separator is starting in the middle of the image.
and i want to eliminate the space on the left of each row, i want to place my image there, so the cell seperator will show only under the cell content , and not under part of the image, i am strageling this separator for a couple of hours and now i can't show my content..
also my simulator i taking something like 10 sec to show the table content..
can u help me please, i know its suppose to be s simple thing, but i can't manage this to work...
thank you..
here is my custom TableCell
import UIKit
class TableCellMessages: UITableViewCell {
var labUerName = UILabel();
var labMessage = UILabel();
var labTime = UILabel();
var labRead = UILabel();
override func awakeFromNib() {
super.awakeFromNib()
self.imageView.clipsToBounds = true
self.imageView.layer.cornerRadius = 22;
self.imageView.contentMode = UIViewContentMode.ScaleAspectFill
// self.imageView.autoresizingMask = UIViewAutoresizing.None
var currentFont:UIFont = labUerName.font
labUerName.font = UIFont.boldSystemFontOfSize(currentFont.pointSize);
labUerName.setTranslatesAutoresizingMaskIntoConstraints(false)
labMessage.setTranslatesAutoresizingMaskIntoConstraints(false)
labTime.setTranslatesAutoresizingMaskIntoConstraints(false)
labRead.setTranslatesAutoresizingMaskIntoConstraints(false)
contentView.addSubview(labUerName)
contentView.addSubview(labMessage)
contentView.addSubview(labTime)
contentView.addSubview(labRead)
//Set layout
var viewsDict = Dictionary <String, UIView>()
viewsDict["username"] = labUerName;
viewsDict["message"] = labMessage;
viewsDict["time"] = labTime;
viewsDict["read"] = labRead;
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[username]-1-[message]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[time]-1-[read]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[username]-[time]-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[message]-[read]-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
}
override func layoutSubviews() {
super.layoutSubviews()
var x = self.frame.origin.x;
var y = self.frame.origin.y;
var width = self.frame.width;
var height = self.frame.height;
self.imageView.frame = CGRectMake(x, y, height, height);
self.contentView.frame = CGRectMake(x + height, y, width - height , height)
}
}
To fix the cell separator, you'll need to use the separatorInset property on your UITableView which you most likely have a reference to in your view controller:
self.tableView.separatorInset = UIEdgeInsets(0, 0, 0, 0)
See https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UITableView_Class/index.html#//apple_ref/occ/instp/UITableView/separatorInset
Apart from that, you'll save yourself and your fellow programmers a lot of headache if you design your custom cell as a prototype cell in your storyboard instead of laying out your labels etc programmatically.