I have been trying this for quite sometime now and went through a lot of posts on SO and some video tutorials. What I want to create is a horizontal scroll view with some buttons in it, like this:
My view hierarchy is as follows:
View Controller
View
Scroll View
View
Buttons
I have set top, leading, trailing, bottom constraints to the scroll view. I have set it's width equal to it's superview, and have set it's height to 200. So far so good, for the view inside the scroll view, I have set it's constraints leading, trailing, top and bottom to zero with respect to it's superview i.e. scroll view. I have made it's width equals to the View controllers view, since that was the solution here on SO to the ambiguous width issue. It solved the issue. Now I added all the buttons and set up their constraints to their parent view. Now when I run the app, a screen like the above added screenshot appears, however I cant scroll to the last element.Any help is greatly appretiated.
It's so simple.
Take a scrollView(Draw top left bottom right and hight constraint)
Take a UIView which will act like as a container View.(Draw top left bottom right constraint with scrollView).
Make your View Controller 1000px so that you can make your scrollView bigger and easy to watch.later on you can minimize it.
Now keep your button inside of it. keep in mind that, Every button will have top leading width , height and trailing if necessary. Width & height is important for scrollView because how big it will be depends on it.
Pictures worth a thousand word.
here is my hierarchy
And here is the Storyboard layout design.White background is basically containerView.
And here is the output.I have given some color for better understanding.
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var btnBack: UIButton!
#IBOutlet weak var btnForward: UIButton!
let headerView = UIView()
headerView.backgroundColor = UIColor.white
for i in 0..<selectedRestaurant.multipleImages.count {
let url = selectedRestaurant.multipleImages[i]
let imgView = UIImageView()
imgView.frame = CGRect(x: CGFloat(i) * self.view.frame.size.width, y: 0, width: self.view.frame.size.width, height: scrollView.frame.size.height)
let task = URLSession.shared.dataTask(with: URL(string: url as! String)!, completionHandler: { (data, response, error) in
if error == nil {
if data != nil {
imgView.contentMode = UIViewContentMode.scaleAspectFill
imgView.image = UIImage(data: data!)
}
}
})
scrollView.addSubview(imgView)
scrollView.contentSize = CGSize(width: self.view.frame.size.width * CGFloat(selectedRestaurant.multipleImages.count), height: scrollView.frame.size.height)
write code in btn back clicked <<<<
let index = Int(scrollView.contentOffset.x/self.view.frame.size.width) - 1
print("\(index)")
if index >= 0 {
scrollView.setContentOffset(CGPoint(x: CGFloat(index) * self.view.frame.size.width, y: 0), animated: true)
}
write code in btn next clicked >>>>>
let index = Int(scrollView.contentOffset.x/self.view.frame.size.width) + 1
print("\(index)")
if index < (selectedRestaurant.multipleImages.count) {
scrollView.setContentOffset(CGPoint(x: CGFloat(index) * self.view.frame.size.width, y: 0), animated: true)
}
For swift 5.1
firstly put scrollView give constraint to superView. Make them all what you desire.
Then put view on scroll view like I showed in pic. Give constraint shown below then to avoid error follow 3. step.
https://i.stack.imgur.com/MwzVN.png
Push control button and drag to the view then select Equal Height.
https://i.stack.imgur.com/91yzl.png
I give name to view over scrollview which name is ContentView . Be sure view size form is freeform and make width 1200 same as ContentView.
https://i.stack.imgur.com/jqo0v.png
https://i.stack.imgur.com/ntW4p.png
5)Then put TableView over the ContentView and give all the constraint zero.
https://i.stack.imgur.com/JPbXH.png
This is my TableviewCell
https://i.stack.imgur.com/4Fxwk.png
And this one is the result.
https://i.stack.imgur.com/zxBAT.gif
Related
I have the following layout:
UIImageView
UIView (with UILabel)
UITableView
As the tableView is scrolled up, the height of the imageView is decreased before actually scrolling the tableView. The following code is used for that:
let headerImageViewMaxHeight: CGFloat = 200
let headerImageViewMinHeight: CGFloat = UIApplication.shared.statusBarFrame.height
#IBOutlet var headerImageViewHeightConstraint: NSLayoutConstraint!
#IBOutlet var headerImageView: UIImageView!
#IBOutlet var subtitleView: UIView!
#IBOutlet var subtitleLabel: TextLabel!
private var contentOffsetDictionary: NSMutableDictionary!
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.isKind(of: UICollectionView.self) {
let horizontalOffset: CGFloat = scrollView.contentOffset.x
let collectionView: UICollectionView = scrollView as! UICollectionView
contentOffsetDictionary.setValue(horizontalOffset, forKey: collectionView.tag.description)
} else if scrollView == tableView {
let y: CGFloat = scrollView.contentOffset.y
let newHeaderImageViewHeight: CGFloat = headerImageViewHeightConstraint.constant - y
if newHeaderImageViewHeight > headerImageViewMaxHeight {
headerImageViewHeightConstraint.constant = headerImageViewMaxHeight
} else if newHeaderImageViewHeight < headerImageViewMinHeight {
headerImageViewHeightConstraint.constant = headerImageViewMinHeight
} else {
headerImageViewHeightConstraint.constant = newHeaderImageViewHeight
scrollView.contentOffset.y = 0
}
}
}
The following two images shown the current states of scrolled down or up:
Scrolled down (initial state).
Scrolled up.
As you can see, initially the grey bar saying To Table -> Table 1 is scrolled down and is about 60 high.
When it's scrolled up, it scrolles until the safeArea. What I want to achieve is that once it reaches the safe area, it continues scrolling up into the safeArea (top notch), while keeping the text in exactly the same place (topSpace to safeArea of the text should stay the same). So, basically growing the UIView to go into the safeArea.
This image shows the ViewController layout.
Header View can be ignored, as that is aligned to the top of the viewController, but no constraints to anything else than superView.
TopSpace of the view in question, is 0 to the UIImageView.
Changing headerImageViewMinHeight to 0, makes it scroll up to the actual top of the screen, without expanding it. So this seems like a good start, but needs some extra logic or constraints.
As the UIView doesn't have a specific height constraint, changing its UILabel's top space from = 16 to => 16, there's the warning of missing Y constraint or height.
Otherwise that, together with UILabel's top space to the viewController's safeArea of => 8 sounds like it could work.
Any ideas are welcome.
EDIT: The following picture basically shows what I want, except that here the UILabel's top is not aligned to the safe area.
What this picture shows is achieved by changing headerImageViewMinHeight to 0.
Add constriant
Uncheck constraint to margin
double click on Align bottom/ Align top
click on last baseline (for bottom)/ first baseline (for top constrraint)
DONE
I am trying to add two custom interfaces that are UIViews to a UIScrollView in my mainStoryBoard.
both UIViews have an image inside them, the image's dimensions are equal to the view's dimensions.
This is the code I am using to add these views to my scrollView
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myScrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.myScrollView.frame = view.frame
for i in 0..<2{
let uiImage = UIImage(named: "\(i)")!
let view = Bundle.main.loadNibNamed("TestView", owner: self, options: nil)?.first as! UIView
view.setImage(uiImage)
let xpos = self.myScrollView.frame.width * CGFloat(i)
view.frame = CGRect(x: xpos, y: 0, width: self.myScrollView.frame.width, height: self.myScrollView.frame.height)
myScrollView.contentSize.width = self.myScrollView.frame.width * CGFloat((i + 1))
myScrollView.layer.cornerRadius = 20
myScrollView.layer.masksToBounds = true
myScrollView.addSubview(view)
}
}
}
In the TestView.swift, I have
func setImage(_ name:UIImage) -> Void{
testUIImage.image = name
testUIImage.contentMode = .scaleAspectFit
testUIImage.clipsToBounds = true
}
But when I run the app, the first View gets added fine, but when i scroll to the right, I can see that the second View is not in the Scrollview properly and there is a white space between the first and second View, almost like the space is an added view, that is pushing the second View to the right. I have paging enabled in the scrollViewer.
First Image In ScrollView
Second Image In ScrollView
As you can see, the second image is cut off on the leading edge.
This is the blank space between them:
enter image description here
I checked the width of both UIView frames, they are equal. I also checked the content size after the second iteration, it is equal to the size of both frames*2 , as it should be. But for some reason, a white black space is being added. And it only shows up after adding 2 or more subviews.
I am adding an OverlayView to a Controller. I have set all the constraints to my OverlayView except the bottom constraint. I have a button at the end of my view. I am setting the size of the OverlayView as button.frame.maxY + margin. But it's not setting up the right height.
override func layoutSubviews() {
sizeToFit()
var sizeThatFits: CGSize = bounds.size
sizeThatFits.width = UIScreen.main.bounds.size.width - popupSizeHorizontalMargin
sizeThatFits.height = getStartedButton.frame.maxY + popupBottomMargin
let newFrame = CGRect(x: popupOriginX, y: bounds.midY - sizeThatFits.height/2, width: sizeThatFits.width, height: sizeThatFits.height)
frame = newFrame
}
This can be accomplished according to this [inner elements are example]
OverlayView
-> label ------> top to overlayView ,left,right
-> button -----> top to label , left , right
-> label -----> top to button , left , right , bottom to overlayView
The overlay view or outer view can calculate its height based on the size of elements inside it and their intrinsic size. Instead of specifying height constraint for overlay view or bottom constraint, add bottom constraint of overlay view to the last element inside the overlay view. This will satisfy the constraints and your overlay view will expand based on its contents.
Simply ctrl drag from your overlay view to the last inner view and select Bottom Space to Container.
You need to override intrinsicContentSize in UIView and return your custom size:
class YourCustomView: UIView {
override var intrinsicContentSize: CGSize {
var someYourSize = CGSize()
// ... calculate your size
return someYourSize
}
}
It will automatically update YourCustomView size to your custom calculated size using autolayout or when you want to update size manually via code - call invalidateIntrinsicContentSize() on YourCustomView object
I have a UIViewController that acts as a Container View Controller. It has a UIScrollView that has the same width as the screen, but it's height is smaller than the height of the screen.
The UIScrollView contains the views of two other UIViewControllers and those views can be horizontally scrolled through.
I set my contentSize like this:
scrollView.contentSize.width = self.view.bounds.width * 2
This works and allows me to scroll through my UIViewController views horizontally.
The following is how I add the UIViewController views to my scrollView:
private func addPanel(viewController: UIViewController, panel: Panel) {
let xOffset: CGFloat!
switch panel {
case .left:
xOffset = 0.0
case .right:
xOffset = self.view.bounds.width
}
let panelView = UIView(frame: CGRect(x: xOffset, y: 0, width: self.view.bounds.width, height: self.scrollView.bounds.height))
scrollView.addSubview(panelView)
let panelViewController: UIViewController! = viewController
var viewBounds = view.bounds
viewBounds.height = panelView.bounds.height
panelViewController.view.frame = view.bounds
panelView.addSubview(panelViewController.view)
addChildViewController(panelViewController)
panelViewController.didMove(toParentViewController: self)
}
For some reason, the UIViewController views don't resize to fit the height of the UIScrollView.
Should I be doing it constraint based? How would I do this. I've been struggling with this for a few days and am at a loss.
Essentially, the UIViewController views will just like like full screen views offset and so you can't see the bottom of the views because the bottom of the screen cuts them off.
I'm just taking a guess without knowing all the code, but one reason could be if you're adding the child view controllers before the scrollview has been layouted.
After you add and set the child views sizes, the scrollview adjusts its size to the phone size but you never update the child views.
My suggestion here would be to add the child view controllers to the scrollview, but move the frame setting code into a layouting method where you know your views have the correct(visible) frames/bounds.
Given you are writing this in a view controller, one method could be -viewDidLayoutSubviews
Hope this makes sense, feel free to ask more questions if it doesn't.
I have this situation :
When I tap on "add" button I reduce the pink view's(first view) height and I execute this code:
#IBOutlet weak var viewPink: UIView!
#IBAction func add(_ sender: AnyObject) {
viewPink.frame = CGRect(x: viewPink.frame.origin.x, y: viewPink.frame.origin.y, width: viewPink.frame.size.width, height: viewPink.frame.size.height - 50)
}
but I want that the last view remains to the same distance from the pink view , essentially you have to climb on why the pink view reduces its height , instead the second view remains where it was before.
Can you help me about it?
P.S I set the vertical spacing constraint between the two views but It doesn't work
You should add an Height constraint on your pink view, create an IBOutlet to this constraint in your ViewController, and set the "constant" property to change the height.
Example:
heightConstraint.constant = 150
This will change the height with Autolayout, you shouldn't change the height by setting a new frame because it doesn't use Autolayout.