Reputation: 471
I got question about UIScrollview
.
The story is I have a UIView
named ChartsView which I re-draw it myself by override method drawRect()
. The content of drawing was generated dynamically. So I do not know its size until runtime. The question is how/where can I set its super view's (scrollView) content size dynamically?Any idea about that?
- (void)viewDidLoad
{
[super viewDidLoad];
// not this way. it's fixed size.....
ChartsView *chartsView = [[ChartsView alloc]initWithFrame:CGRectMake(0, 0, 320, 800)];
self.scrollView.contentSize = chartsView.frame.size;
[self.scrollView addSubview:chartsView];
}
Upvotes: 39
Views: 106616
Reputation: 1834
SWIFT 5
You can also programmatically update contentView of the UIScrollView
by adjusting UIScrollView.contentLayoutGuide
, here is the example of constraining contentview to the height of yourView
:
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.heightAnchor.constraint(equalTo: self.view.heightAnchor).isActive = true
self.scrollView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
self.scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.scrollView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.scrollView.contentLayoutGuide.heightAnchor.constraint(equalTo: yourView.heightAnchor, multiplier: 1).isActive = true
self.scrollView.contentLayoutGuide.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 1).isActive = true
self.scrollView.contentLayoutGuide.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.scrollView.contentLayoutGuide.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
Upvotes: 2
Reputation: 2274
It is not 100% guaranteed that the last object in scrollView.subviews array will return the highest y-origin object in your scroll view. The subviews array is arranged by Z-Index (i.e the last object in the subviews array will be the object stacked the highest and will be the top-most subview in the scroll view's subviews. Instead it is more accurate to use a basic sort function to iterate through the subviews and get the object with the highest y-origin.
Swift 4
extension UIScrollView {
func updateContentView() {
contentSize.height = subviews.sorted(by: { $0.frame.maxY < $1.frame.maxY }).last?.frame.maxY ?? contentSize.height
}
}
Usage (in viewDidLayoutSubviews
or whenever your content size updates):
myScrollView.updateContentView()
Upvotes: 25
Reputation: 13236
While using scrollviews in storyboard it’s better to calculate content size according to number of views present in scrollview rather than giving content size programmatically with static value. Here are the steps to get content size dynamically.
Step 1 : Add Scrollview to view in storyboard and add leading, trailing, top and bottom constraints (All values are zero).
Step 2 : Don’t add directly views which you need on directly scrollview, First add one view to scrollview (that will be our content view for all UI elements). Add below constraints to this view. Leading, trailing, top and bottom constraints (All values are zero). Add equal height, equal width to Main view (i.e. which contains scrollview). For equal height set priority to low. (This is the important step for setting content size). Height of this content view will be according to the number of views added to the view. let say if you added last view is one label and his Y position is 420 and height is 20 then your content view will be 440.
Step 3 : Add constraints to all of views which you added within content view as per your requirement.
Upvotes: 3
Reputation: 198
override func viewDidAppear(_ animated: Bool) {
var height: CGFloat
let lastView = self.scrollView.subviews[0].subviews.last!
let lastViewYPos = lastView.convert(lastView.frame.origin, to: nil).y
let lastViewHeight = lastView.frame.size.height
height = lastViewYPos + lastViewHeight
scrollView.contentSize.height = height
}
Upvotes: 3
Reputation: 1248
Calculate UIScrollview contentSize dynamically(depending subviews frame)
Swift:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
DispatchQueue.main.async {
var contentRect = CGRect.zero
for view in self.scrollView.subviews {
contentRect = contentRect.union(view.frame)
}
self.scrollView.contentSize = contentRect.size
}
}
Objective C:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
dispatch_async(dispatch_get_main_queue(), ^ {
CGRect contentRect = CGRectZero;
for(UIView *view in scrollView.subviews)
contentRect = CGRectUnion(contentRect,view.frame);
scrollView.contentSize = contentRect.size;
});
}
Upvotes: 4
Reputation: 5839
This is an extension written on Swift 3
extension UIScrollView {
func updateContentViewSize() {
var newHeight: CGFloat = 0
for view in subviews {
let ref = view.frame.origin.y + view.frame.height
if ref > newHeight {
newHeight = ref
}
}
let oldSize = contentSize
let newSize = CGSize(width: oldSize.width, height: newHeight + 100)
contentSize = newSize
}
}
Upvotes: 0
Reputation: 1655
Try this -
- (void)viewDidLoad
{
[super viewDidLoad];
// not this way. it's fixed size.....
ChartsView *chartsView = [[ChartsView alloc]initWithFrame:CGRectMake(0, 0, 320, 800)];
//self.scrollView.contentSize = chartsView.frame.size;
[self.scrollView setContentSize:CGSizeMake(chartsView.frame.size.width, chartsView.frame.size.height)];
[self.scrollView addSubview:chartsView];
}
Upvotes: 1
Reputation: 563
Thanks to IOS Rocks and CularBytes for the leads on this. I found two problems (for me) with the above solutions:
Also I am using autolayout to pin the sides of the UIScrollView, so I don't need to worry about width (as is CularBytes).
Here's what I came up with and works (in Swift 3.0):
func setScrollViewContentSize() {
var height: CGFloat
let lastView = self.myScrollView.subviews[0].subviews.last!
print(lastView.debugDescription) // should be what you expect
let lastViewYPos = lastView.convert(lastView.frame.origin, to: nil).y // this is absolute positioning, not relative
let lastViewHeight = lastView.frame.size.height
// sanity check on these
print(lastViewYPos)
print(lastViewHeight)
height = lastViewYPos + lastViewHeight
print("setting scroll height: \(height)")
myScrollView.contentSize.height = height
}
I call this in my viewDidAppear()
method.
Upvotes: 4
Reputation: 10321
swift (2.0)
@IBOutlet weak var btnLatestButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let height = btnLatestButton.frame.size.height
let pos = btnLatestButton.frame.origin.y
let sizeOfContent = height + pos + 10
scrollview.contentSize.height = sizeOfContent
}
Above ofcourse is when you just have a fixed amount of views on your scrollview. IOS Rocks method is available but was not good for me, created more space then I needed.
let lastView : UIView! = scrollview.subviews.last
let height = lastView.frame.size.height
let pos = lastView.frame.origin.y
let sizeOfContent = height + pos + 10
scrollview.contentSize.height = sizeOfContent
Upvotes: 7
Reputation: 2127
One more easy way
- (void)viewDidLoad
{
float sizeOfContent = 0;
UIView *lLast = [scrollView.subviews lastObject];
NSInteger wd = lLast.frame.origin.y;
NSInteger ht = lLast.frame.size.height;
sizeOfContent = wd+ht;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, sizeOfContent);
}
Upvotes: 62
Reputation: 1489
try this
- (void)viewDidLoad
{
[super viewDidLoad];
// not this way. it's fixed size.....
ChartsView *chartsView = [[ChartsView alloc]initWithFrame:CGRectMake(0, 0, 320, 800)];
self.scrollView.contentSize = chartsView.frame.size;
[self.scrollView setNeedsDisplay];
[self.scrollView addSubview:chartsView];
}
Upvotes: 1