How to layout a horizontal image into a UIScrollView? - ios

How to layout a horizontal image in a scrolling view with the storyboard, so that the horizontal image can scroll in different directions ?
Tried all the options that were on the internet. Solutions with a vertical image work, but not with a horizontal.

The basics for centering the scroll content on start...
Assuming:
constraints are setup correctly
imageView is the view returned by viewForZooming()
Implement:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let x = (imageView.frame.width - scrollView.frame.width) * 0.5
let y = (imageView.frame.height - scrollView.frame.height) * 0.5
scrollView.contentOffset = CGPoint(x: x, y: y)
}
Note that this is only the basics... depending on what else you may be doing, viewDidLayoutSubviews() can be called multiple times throughout the life-cycle. So you'd want to account for that (unless you want the view centered every time).

Related

Horizontal scroll view scrolls but only partially and bounces back to original position

I've got a horizontal scroll view with content as follows:
When I run it scrolls horizontally partially - I can scroll until about half of the red view is visible but then it bounces back.
How can I get it so that it can scroll all the way so all the red view is visible and then stays there?
I have this in the view controller, but it makes no difference if there or not.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("Content view bounds: \(contentView.bounds)")
scrollView.layoutIfNeeded()
scrollView.contentSize = contentView.bounds.size
}
In the screenshot of the storyboard there is no trailing edge constraint for the red view, however if I add one between the red view and the content view then when I run on the device it stops scrolling and looks like this:
You shouldn't need to be explicitly setting the .contentSize to begin with -- you can let auto-layout handle it all for you.
First, delete your Content View.width = width constraint:
Having that constraint told auto-layout to make your contentView only as wide as the scroll view, so you wouldn't get any horizontal scrolling. By explicitly setting the .contentSize you got some scrolling, but as you found it didn't give you what you wanted.
After deleting that constraint, add a 20-pt trailing constraint from Red View to the trailing edge of the content view:
Now, you have a complete chain of horizontal constraints...
- Blue View.leading = leading + 20
- Blue View Width
- Red View.leading = Blue View.trailing + 40
- Red View Width
- trailing = Red View.trailing + 20
This satisfies auto-layout and properly defines the width of Content View... and since Content View is constrained to leading and trailing of your Scroll View, you get correct horizontal scrolling.
No need for any code.
I got it to work by adding the following:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
scrollView.contentSize = CGSize(width: 750,height: 812)
}
But why does the above work but not the following?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("Content view bounds: \(contentView.bounds)")
scrollView.layoutIfNeeded()
scrollView.contentSize = contentView.bounds.size
}
At runtime contentView.bounds is (0.0, 0.0, 750.0, 812.0)

Implement twitter Profile like view with parallax scroll

I want to implement the following sort of view where the view can be completely scrolled and houses 2 different scrollview (Main and the secondary) with infinite scrollable content. This represents the exact thing I want.
The red view is superview - should scroll vertically
The green view is of the height of the current view and is just static. That doesnt scroll
The blue view is the horizontal scrollview where for each label there is a yellow vertically scrolling infinity collection view
the labels scroll as in the given video. under each label there is the collection view I mentioned in point 3
The blue box is the scroll view and I want the scrolling to happen horizontally in a parallax way such as this.
I am able to implement the above parallax in the correct fashion but each title contains their own collectionview. When I implement this I am not able to have an infinite scroll. Below is the code for that :
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == containerScrollView {
for i in 0..<shotsData.count {
let label = scrollView.viewWithTag(i + tagValueL) as! UILabel
let view = scrollView.viewWithTag(i + tagValueV) as! ShotsMediaView
let scrollContentOffset = scrollView.contentOffset.x + scrollView.frame.width
let viewOffset = (view.center.x - scrollView.bounds.width/4) - scrollContentOffset
label.center.x = scrollContentOffset - ((scrollView.bounds.width/4 - viewOffset)/2)
}
}
}
How can I exactly achieve the same behavior with an infinite scroll vertically? I want each of these titles to have collectionview that have the dynamic height each.
I did a crude implementation of this.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == colorsCollectionView {
let newContentOffSetX = scrollView.contentOffset.x
let distance = contentOffSetX + newContentOffSetX
// Scroll the text collection view proportinately
let titleScrollDistance = (distance/colorsCollectionView.frame.width * 75.0)
titlesCollectionView.contentOffset = CGPoint(x: titleScrollDistance, y: titlesCollectionView.contentOffset.y)
contentOffSetX = newContentOffSetX
}
}
contentOffSetX is a property of the class(ViewController) that I use to keep track of the scrolling distance of the bottom collection view. Initially that is set to 0. When the user scrolls the collection view at the bottom, the above delegate method is called. Then I use the contentOffSet to get the distance that was scrolled along the X-axis. I map that to the width of the title labels(hardcoded as 75.0) to calculate the distance that collection has to be scrolled. Hope this can be refined to serve your purpose, but I am sure that there are better methods out there :)

UITableViewController layout not working

I am trying to position UITableView to the left side of the app, whole height but taking just 1 / 3 of the available width with the code like this:
class ViewController: UIViewController {
var tableController: UITableViewController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableController = UITableViewController();
addChildViewController(tableController!)
self.view.addSubview(tableController!.view)
tableController!.didMove(toParentViewController: self)
}
override func viewDidLayoutSubviews() {
tableController!.view.backgroundColor = UIColor.red
tableController!.view.frame = CGRect(
x: 0,
y: 0,
width: view.bounds.width / 3,
height: view.bounds.height);
//tableController!.view.frame = view.bounds
}
}
And it looks like this:
And I don't get why the lines are not correctly aligned horizontally and it looks like being cut on the right.
If I give the view controller full width / height by uncommenting the last line, it looks better:
Question 1
Are you properly constraining the TableView in the parent view? You can set proportional constraints. Please see this post on the topic: AutoLayout to keep view sizes proportional
Side Note
For iOS 11, constraining views to layout guides will trigger warnings in your IDE. Consider embedding UI components inside another view that takes up the entire width of the parent view using Auto-resizing Masks. See the topic here: Xcode Auto Layout Resizing Issue

How to make subviews resize to UIScrollView ContentSize?

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.

How to make swipe back in left side in single view

I made more than one view without taking new Controller class , I used UIgestureRecognizer but it not look better.
I want to swipe same like ios device, when we start to swipe from left side corner.
You can implement this with scrollview.
Steps:
1)First place a scrollview that fills the full screen.
2)Next create a container view which is as big as to hold your two views.
let containerSize = CGSize(width: 640.0, height: 640.0)
containerView = UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0),size:containerSize))
scrollView.addSubview(containerView)
3) Now you create your view 1 , view2 and add them to container view.
4) Set the scrollview content size to container size
scrollView.contentSize = containerSize;
5) Then set the scrollview delegate method
func viewForZoomingInScrollView(scrollView: UIScrollView!) -> UIView!{
return containerView
}

Resources