iOS UIScrollView's width is different from the width set in constraint - ios

I'm new to iOS auto layout. I want to implement a view pager using UIScrollView with paging enabled. I set the scrollView to (w, h) to (200, 65), but the width of scrollView is always 240 at runtime. As the result, the offset of contents, the label I added into the scrollView, do not match to the scrollView's width. Why are they different? How do I get the right width?
private let contents = ["This is the 1st message",
"This is the 2nd message",
"This is the 3rd message"]
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
print("scrollView.bounds = \(scrollView.bounds)")
print("scrollView.frame = \(scrollView.frame)")
var x: CGFloat = 0
for content in contents {
let label = UILabel(frame: CGRectMake(x, 0, 0, 0))
label.text = content
label.sizeToFit()
print("label.frame = \(label.frame)")
scrollView.addSubview(label)
x += scrollView.bounds.width
}
scrollView.contentSize = CGSizeMake(x, scrollView.bounds.height)
}
Output:
scrollView.bounds = (0.0, 0.0, 240.0, 128.0)
scrollView.frame = (80.0, 156.0, 240.0, 128.0)
label.frame = (0.0, 0.0, 178.0, 20.3333333333333)
label.frame = (240.0, 0.0, 185.666666666667, 20.3333333333333)
label.frame = (480.0, 0.0, 182.333333333333, 20.3333333333333)

Your problem here is that you are using a size class wCompact hRegular for this constraints, so if you have another size class (for example different orientations) you don't have these constraints available.
To set the constraints just for iPhone I suggest doing it programmatically:
if (IDevice.currentDevice().userInterfaceIdiom == .Phone) {
// Set constraints or a different UIVIew here
}
And if you have your app available for iPhone Only you can use the class size Any-Any avoiding the programmatically set.

Related

Auto-sizing a UILabel without setting an explicit height

How do I get a multi-line label to size itself? I don't want to set an explicit height for it but I do need to place it in view.
The way my app is built, we explicitly set frames and origins rather than using NSLayoutConstraints. It's a mature app so this isn't up for discussion.
I'd like to be able to give my UILabel an origin and a width and let it figure its own height out.
How can I do this? This is my playground code:
let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 180))
view.backgroundColor = .white
let l = UILabel()
l.text = "this is a really long label that should wrap around and stuff. it should maybe wrap 2 or three times i dunno"
l.textColor = .black
l.lineBreakMode = .byWordWrapping
l.numberOfLines = 0
l.textAlignment = .center
l.sizeToFit()
let margin: CGFloat = 60
view
view.addSubview(l)
l.frame = CGRect(x: margin, y: 0, width: view.bounds.width - (margin * 2), height: 100)
// I don't want to do this ^^
This may do what you want...
As requested, you want to set the .origin and .width of a UILabel and have it set its own .height based on the text.
class ZackLabel: UILabel {
override public func layoutSubviews() {
super.layoutSubviews()
let h = sizeThatFits(CGSize(width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude))
self.frame.size.height = h.height
}
}
class ViewController: UIViewController {
var testLabel: ZackLabel!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellow
// instantiate a 300 x 180 UIView at 20, 80
let myView = UIView(frame: CGRect(x: 20, y: 80, width: 300, height: 180))
myView.backgroundColor = .white
// instantiate a ZackLabel
testLabel = ZackLabel()
testLabel.text = "this is a really long label that should wrap around and stuff. it should maybe wrap 2 or three times i dunno"
testLabel.textColor = .black
testLabel.lineBreakMode = .byWordWrapping
testLabel.numberOfLines = 0
testLabel.textAlignment = .center
// set background color so we can see its frame
testLabel.backgroundColor = .cyan
let margin: CGFloat = 60
// set label's origin
testLabel.frame.origin = CGPoint(x: margin, y: 0)
// set label's width (label will set its own height)
testLabel.frame.size.width = myView.bounds.width - margin * 2
// add the view
view.addSubview(myView)
// add the label to the view
myView.addSubview(testLabel)
// add a tap recognizer so we can change the label's text at run-time
let rec = UITapGestureRecognizer(target: self, action: #selector(tapFunc(_:)))
view.addGestureRecognizer(rec)
}
#objc func tapFunc(_ sender: UITapGestureRecognizer) -> Void {
testLabel.text = "This is dynamic text being set."
}
}
Result (on an iPhone 8):
and, after tapping on the (yellow) view, dynamically changing the text:
label.sizeThatFits(CGSize(width: <your required width>, height: CGFloat.greatestFiniteMagnitude))
This returns the labels needed size, growing infinitely in height, but fitted to your required width. I've occasionally noticed minor inaccuracies with this function (rounding error?), so I tend to bump the width and height by 1 just to be safe.
UILabel comes with an intrinsic size that should be calculated based on the text and the label's .font property. You may need to add a margin to it...
var height = l.intrinsicContentSize.height
height += margin
l.frame = CGRect(x: margin, y: 0, width: view.bounds.width - (margin * 2), height: height)
Failing that, maybe you can try something like:
let size = CGSize(width: view.bounds.width - (margin * 2), height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
var estimatedFrame = CGRect()
if let font = l.font {
estimatedFrame = NSString(string: l.text).boundingRect(with: size, options: options, attributes: [NSAttributedString.Key.font: font], context: nil)
}
//if you need a margin:
estimatedFrame.height += margin
l.frame = estimatedFrame
Give your UILabel as a UIScrollview or UITableView cell subview.
Then you setup UILabel leading, tralling, top, bottom constrain.
If you give UITableview then set table view hight auto dynamic. If you give UIScrollview
just set UILabel bottom constrain priority low

swift 8 iOS data is getting crop in scrollview while adding dynamically data

Hi I am developing small IOS application in which I am using scrollview with auto-layout.Inside scroll I am adding two more views. I am using IB and auto-layout constraints. I am adding two views in side one after another in vertical manner. I have added outer constraints like trailing, leading, top, bottom space. I also added height constraints for both views. Till this everything is working fine.
But my passengerView has some dynamic content. For that reason I want to make height constraint greater than equal to instead of equal to.
my code is:-
#IBOutlet weak var secondView: UIView!
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var passengerView: UIView!
#IBOutlet weak var detailView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let nameLabel = UILabel(frame: CGRect(x: 0, y: (self.name.frame.height * CGFloat(index) + CGFloat(49)), width: 161, height: 32))
let idLabel = UILabel(frame: CGRect(x: 161, y: (self.name.frame.height * CGFloat(index) + CGFloat(49)), width: 161, height: 32))
// nameLabel.text = dsdh?["name"] as? String
nameLabel.layer.borderWidth = 1
nameLabel.layer.borderColor = UIColor.black.cgColor
nameLabel.textAlignment = .center;
self.passengerView.addSubview(nameLabel)
//idLabel.text = dsdh?["document_Type"] as? String
idLabel.layer.borderWidth = 1
idLabel.layer.borderColor = UIColor.black.cgColor
idLabel.textAlignment = .center;
self.passengerView.addSubview(idLabel)
self.secondView.addSubview(self.passengerView)
self.detailView.frame = CGRect(x: self.detailView.frame.origin.x, y: self.detailView.frame.origin.y + CGFloat(32) , width: self.detailView.frame.size.width, height: self.detailView.frame.size.height)
self.secondView.addSubview(self.detailView)
//self.scrollView.addSubview(self.detailView)
self.secondView.frame = CGRect(x: self.secondView.frame.origin.x, y: self.secondView.frame.origin.y , width: self.secondView.frame.size.width, height: self.secondView.frame.size.height + CGFloat(5))
self.scrollView.addSubview(self.secondView)
//self.scrollView.contentSize = CGSize(width: self.scrollView.frame.size.width , height: self.scrollView.frame.size.height + CGFloat(32))
self.scrollView.frame = CGRect(x: self.scrollView.frame.origin.x, y: self.scrollView.frame.origin.y , width: self.scrollView.frame.size.width, height: self.scrollView.frame.size.height + CGFloat(0))
self.view.frame = CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y , width: self.view.frame.size.width, height: self.view.frame.size.height + CGFloat(0))
}
The best way to do this would be as following:
Implement a UIScrollView that is constraint to the Leading, Trailing, Top, Bottom constraints of your main view.
Add a UIView to the UIScrollView and constrain it to the Leading, Trailing, Top, Bottom of your UIScrollView. Important is to also constrain this UIView's width to your main view, not your scroll view! In this way this UIView will serve you as a Content View for the UIScrollView. This step is mandatory if you are doing this in Interface Builder!
Add a UIStackView to your Content View (the view from the previous step) and constrain it to the Leading, Trailing, Top, Bottom of the Content View.
Add all your UILabels, your Detail View, QR Code View and Passenger View as children to this UIStackView.
Since you want only your Passenger View to have a dynamic height you can set your UIStackView's axis to Vertical and Content Mode to Fill. Then you will provide a height for all of the children of the UIStackView except the Passenger View.
In this way the Content Size of your UIScrollView will adapt based on the height of the UIStackView and the height of the UIStackView willl adapt based on the needed height for the Passenger View.

Make UIView fill UIScrollview

I have a paging scrollview with two views, and the first one is to fill the UIScrollview and the second to stay in the middle. I don't know what's going wrong, but it wouldn't fill on iPhone6 but, it fits fine on iPhone5. Below is my code:
View Hierarchy
UIView
--StackView (Properties -- Axis(Vertical) Alignment(Fill) Distribution(Fill) Spacing(0))
--UIView (Fixed height. 60)
--UIView (Main Views Holder)
--ScrollView(Content Size: Screen Width * 2, Height: Height left between it margin to the MainView and BottomView) and I notice, that the scrollview keeps remaining size 433 on both iPhone5 and 6. And when I check my storyboard, that's the exact same height there
--UIView(Height: 102, Width: Screen Width)BottomView
#IBOutlet weak var scrollView: UIScrollView!
var rectanglePhot: UIView!
var squarePhoto: UIView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.contentSize = CGSize(width: self.view.frame.size.width * 2, height: scrollView.frame.size.height)
scrollView.autoresizingMask = .FlexibleWidth
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = UIColor.greenColor()
iniiCameraView()
}
func iniiCameraView() {
rectanglePhot = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.scrollView.frame.size.height)) <-- This is the problematic one
//rectanglePhot = UIView()
let specifyY = self.scrollView.frame.size.height - self.view.frame.size.width
squarePhoto = UIView(frame: CGRect(x: self.view.frame.size.width, y: specifyY, width: self.view.frame.size.width, height: self.view.frame.size.width))
scrollView.addSubview(rectanglePhot)
scrollView.addSubview(squarePhoto)
rectanglePhot.backgroundColor = UIColor.blueColor()
squarePhoto.backgroundColor = UIColor.brownColor()
print("Rect Height ", rectanglePhot.frame.size.height)
print("Scroll Height ", scrollView.frame.size.height)
print("Specify Y ", specifyY)
}
And the image Image Added to Scrollview a index[0]
Add the code in viewdidappear. The autolayout will be applied after viewdidload
Replace
UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.scrollView.frame.size.height))
With :
UIView(frame: CGRect(x: 0, y: 0, width: self.scrollView.bounds.size.width, height: self.scrollView.bounds.size.height))
With this the UIView will take the exact size of you scrollview
if you don't want to use autolayout: add subviews in viewDidLoad, that is fine but you also need to adjust their frames in viewDidLayoutSubviews method of UIViewController since your scrollView will have a different frame after autolayout passes.
if you are ok with using autolayout: don't rely on scrollView for sizing your subviews, relate size of your subviews to the size of self.view

UIScrollView didn't scroll

I've some problem with ScrollView.
I'm trying to create a scrollview and add to it dynamically some buttons.
So i create a scrollView in my main storyboard with some constraints. (0 to left, 0 to right, 0 to botton and 1/10 height).
Now, i want to add some button.
#IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
for index in 0..<12 {
let frame1 = CGRect(x: 0 + (index * 60), y: 0, width: 45, height: 45 )
let button = UIButton(frame: frame1)
button.setTitle("toto", forState: .Normal)
button.backgroundColor = UIColor.green()
scrollView.addSubview(button)
}
}
So now the problem is : my buttons are present but i can't scroll. The scroll is enabled in the storyBoard. I tried to enabled it in the code but nothing changed..
set the contentSize of your scrollView
self. scrollView.contentSize = CGSizeMake(required_width, required_height)
for example
self. scrollView.contentSize = CGSizeMake(500, 74) // custtomize ur self
You have to set the contentSize of the scrollView
The height to set is probably the origin.y + the size.height of the last button.
use this code to set scrollview height
scrollview.contentInset = UIEdgeInsetsMake(0, 0, 50, 0)
//50 is the height of scroll view you can change it to the height you want
scrollview.bounces = false
or you can also change content inset from here
by changing the values of content inset (height or width)

Adjust UIScrollView's contentSize with an embedded UIView?

I have a UIScroll view which contains only one subview. The subview called contentViewis an UIView. Here is what I did in viewDidLoad():
self.scrollView = UIScrollView()
self.view.addSubview(self.scrollView)
self.scrollView.backgroundColor = UIColor.yellowColor()
// pin all edges to the edges of the superview (self.view)
self.scrollView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.view)
}
// create contentView
self.contentView = UIView()
self.contentView.backgroundColor = UIColor.redColor()
self.scrollView.addSubview(self.contentView)
// pin the edges of the contentView to the scrollView
self.contentView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.scrollView)
}
let myView = UIView(frame: CGRectMake(100, 100, 20, 300))
myView.backgroundColor = UIColor.greenColor()
self.contentView.addSubview(myView)
The result is this:
There is ne red contentView as shown in the previous screen shot.
Next, I tried to adjust the size of the contenView in viewDidLayoutSubviews():
let newSize: CGSize = self.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
println("newSize: \(newSize)")
The result is:
newSize: (0.0, 0.0)
How can I either setup the correct size of the contentView or set the correct size of the UIScrollView's contentSize?
have you tried setting the var contentSize: CGSize of the UIScrollView? The documentation says
Discussion The unit of size is points. The default size is CGSizeZero.
If you don't know the size of the content view, then I would recommend setting
scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth| UIViewAutoresizingFlexibleHeight);
and
scrollView.autoresizesSubviews = YES;
for good measure.

Resources