ScrollView on an iOS app in Swift - ios

I have a ViewController and my View is too large, so I'm trying to implement a simple ScrollView but I don't know how to do. I Tried to do Embed in >> ScrollView but it doesn't work.

Add Scrollview in the xib or storyboard file of your VC. Create the view seperately and add it as a subview inside the scrollview. Or you could simply drag it inside the scrollview. Set content size of scrollview to be equal to that view's size. If you still need help, use this tutorial. http://www.raywenderlich.com/76436/use-uiscrollview-scroll-zoom-content-swift

If you need wide screen so here is example how you can make three times wider that usual one.
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let view1: View1 = View1(nibName: "View1", bundle: nil)
addChildViewController(view1)
scrollView.addSubview(view1.view)
view1.didMoveToParentViewController(self)
let view2: View2 = View2(nibName: "View2", bundle: nil)
addChildViewController(view2)
scrollView.addSubview(view2.view)
view2.didMoveToParentViewController(self)
var view2Frame: CGRect = view2.view.frame
view2Frame.origin.x = view.frame.width
view2.view.frame = view2Frame
let view3: View3 = View3(nibName: "View3", bundle: nil)
addChildViewController(view3)
scrollView.addSubview(view3.view)
view3.didMoveToParentViewController(self)
var view3Frame: CGRect = view3.view.frame
view3Frame.origin.x = view.frame.width * 2
view3.view.frame = view3Frame
self.scrollView.contentSize.width = view.frame.width * 3
}
So basically you create scrollView and add here three views and each view has it's new origin point. You can enable or disable paging in storyboard. And that is it!

Related

UIScrollView with glitch content on iPhone 11 Pro Max

I’m new in Swift and I tried making UIScrollView that shows view controllers.
Every thing perfect just at iPhone 11 Pro Max the next screen show a little bit on the side:
the orange strip is the next screen
My Code:
//MARK: - outlets
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var scrollView: UIScrollView!
//MARK: - properties
var viewControllers: [String] = ["ComputerViewController", "AttactViewController", "DefenceViewController", "OfflineViewController"]
var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
//MARK: - life cyrcles
override func viewDidLoad() {
super.viewDidLoad()
for index in 0..<viewControllers.count {
frame.origin.x = scrollView.frame.size.width * CGFloat(index)
frame.size = scrollView.frame.size
let view = UIView(frame: frame)
let storyboard = UIStoryboard(name: "Menu", bundle: nil)
var controller: UIViewController = storyboard.instantiateViewController(withIdentifier: viewControllers[index]) as UIViewController
view.addSubview(controller.view)
self.scrollView.addSubview(view)
}
scrollView.contentSize = CGSize(width: scrollView.frame.size.width * CGFloat(viewControllers.count), height: scrollView.frame.size.height)
scrollView.delegate = self
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
pageControl.currentPage = Int(pageNumber)
}
thanks for helping...
A couple of observations:
You should avoid referencing frame in viewDidLoad. At this point, the frame is not known.
You should avoid hard-coding the size and placement of the subviews at all. There can be a variety of events that change the view’s size later on (e.g. rotations, split view multitasking, etc.). Use constraints.
With scroll views, there are two layout guides, one for its frame and another for its contentSize. So set the size of the subviews using the frameLayoutGuide and the placement of these subviews relative to the contentLayoutGuide.
When you add a view controller’s view as a subview, make sure you call addChild(_:) and didMove(toParent:) calls for view controller containment. See “Implementing a Container View Controller” section of the view controller documentation.
If you want to add paging behavior, just set isPagingEnabled of the scroll view.
Pulling that all together:
override func viewDidLoad() {
super.viewDidLoad()
addChildViews()
}
override func viewDidLoad() {
super.viewDidLoad()
var anchor = scrollView.contentLayoutGuide.leadingAnchor
for identifier in viewControllers {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let child = storyboard.instantiateViewController(withIdentifier: identifier)
addChild(child) // containment call
child.view.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(child.view)
child.didMove(toParent: self) // containment call
NSLayoutConstraint.activate([
// define size of child view (relative to `frameLayoutGuide`)
child.view.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor),
child.view.heightAnchor.constraint(equalTo: scrollView.frameLayoutGuide.heightAnchor),
// define placement of child view (relative to `contentLayoutGuide`)
child.view.leadingAnchor.constraint(equalTo: anchor),
child.view.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
child.view.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
])
anchor = child.view.trailingAnchor
}
anchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor).isActive = true
scrollView.isPagingEnabled = true
}
I’ve eliminated the property frame as that’s not needed anymore (and is just a source of confusion with the view controller’s view’s property of the same name). I’ve also eliminated the container view as it didn’t add much to the overall solution (and only adds a layer of constraints to add).
But the key is to use constraints to dictate the size and position of the subviews within the scroll view and to use view controller containment API.

How to have a shared background on a UIScrollView handling multiple ViewControllers?

I want to make 1 UIScrollView that has inside of it 1 background which can be used by multiple ViewControllers. When the user scrolls to the next ViewController, the background should scroll with it. This is my code:
override func viewDidLoad() {
super.viewDidLoad()
let leftViewController: StartLeftViewController = StartLeftViewController(nibName: "StartLeftViewController", bundle: nil)
self.addChildViewController(leftViewController)
self.theScrollView.addSubview(leftViewController.view)
leftViewController.didMove(toParentViewController: self)
let middleViewController: StartMiddleViewController = StartMiddleViewController(nibName: "StartMiddleViewController", bundle: nil)
self.addChildViewController(middleViewController)
self.theScrollView.addSubview(middleViewController.view)
middleViewController.didMove(toParentViewController: self)
let rightViewController: StartRightViewController = StartRightViewController(nibName: "StartRightViewController", bundle: nil)
self.addChildViewController(rightViewController)
self.theScrollView.addSubview(rightViewController.view)
rightViewController.didMove(toParentViewController: self)
var middleFrame : CGRect = middleViewController.view.frame
middleFrame.origin.x = self.view.frame.width
middleViewController.view.frame = middleFrame
var thirdFrame : CGRect = rightViewController.view.frame
thirdFrame.origin.x = 2 * self.view.frame.width
rightViewController.view.frame = thirdFrame
self.theScrollView.contentSize = CGSize(width: self.view.frame.width * 3, height: self.view.frame.height)
theScrollView.contentOffset.x = view.frame.width
}
I already tried setting a background in the middleViewController which has an width of 3x, but I got problems adding elements to left/right ViewController causing the elements hiding behind the background. Thank you.
In your storyboard you can place three Container Views into your scroll view, one after each other horizontally, and connect each of them with Embed segue to your corresponding view controllers.

getting an IBOutlet from another class swift

I have a IBOutlet in a class Main_Screen which is avaliable in a class hooked up to a main ViewController which has a ScrollView but if i try to get it returns nil
code in View Controller
import UIKit
class ViewController: UIViewController , UIScrollViewDelegate {
#IBOutlet weak var scrollVieww: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
self.scrollVieww.pagingEnabled = true
self.scrollVieww.delegate = self
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let vc = storyboard.instantiateViewControllerWithIdentifier("MainScreen") as? Main_Screen {
// imageview returns nil :(
let imageView = vc.avatarImageView
}
// Do any additional setup after loading the view, typically from a nib.
let V1 = self.storyboard?.instantiateViewControllerWithIdentifier("HomeScreen") as UIViewController!
//Add initialized view to main view and its scroll view and also set bounds
self.addChildViewController(V1)
self.scrollVieww.addSubview(V1.view)
V1.didMoveToParentViewController(self)
V1.view.frame = scrollVieww.bounds
//Initialize using Unique ID for the View
let V2 = self.storyboard?.instantiateViewControllerWithIdentifier("MainScreen") as UIViewController!
//Add initialized view to main view and its scroll view also set bounds
self.addChildViewController(V2)
self.scrollVieww.addSubview(V2.view)
V2.didMoveToParentViewController(self)
V2.view.frame = scrollVieww.bounds
//Create frame for the view and define its urigin point with respect to View 1
var V2Frame: CGRect = V2.view.frame
V2Frame.origin.x = self.view.frame.width
V2.view.frame = V2Frame
//The width is set here as we are dealing with Horizontal Scroll
//The Width is x3 as there are 3 sub views in all
self.scrollVieww.contentSize = CGSizeMake((self.view.frame.width) * 2, (self.view.frame.height))
}
Short answer: Don't do that. You should treat another view controller's views as private.
If you need to manipulate another view controller's UI, add public methods that you use to request the UI change, and then have code inside the VC make the change.
This is both much better design, and it avoids cases where the other view controller's views haven't been created yet, so they're nil and it fails/crashes with an "encountered nil when trying to unwrap optional" message.

Views that are loaded in scroll view not sizing correctly

I have 3 views that I'm loading into a scroll view so users can swipe through the app to change views (similar to Snapchat or Tinder navigation). However, I added a horizontally and vertically centered label to one of the nibs and it appears as though the size is much wider than my screen. I have the nib frames set to the size of the device screen.
How can I make the subviews match the frame of the view (full screen)?
Here's what I've got in the viewDidLoad for the view controller with a full screen scroll view in it, as well of a screenshot of the result:
#IBOutlet weak var scrollViewOfViews: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let settingsVC = UIViewController(nibName: "SettingsViewController", bundle: nil)
let friendsFeedVC = UIViewController(nibName: "FriendsFeedViewController", bundle: nil)
let notificationsVC = UIViewController(nibName: "NotificationsViewController", bundle: nil)
let screenSize = UIScreen.mainScreen().bounds
settingsVC.view.frame = screenSize
friendsFeedVC.view.frame = screenSize
notificationsVC.view.frame = screenSize
self.addChildViewController(settingsVC)
self.scrollViewOfViews.addSubview(settingsVC.view)
settingsVC.didMoveToParentViewController(self)
self.addChildViewController(friendsFeedVC)
self.scrollViewOfViews.addSubview(friendsFeedVC.view)
friendsFeedVC.didMoveToParentViewController(self)
self.addChildViewController(notificationsVC)
self.scrollViewOfViews.addSubview(notificationsVC.view)
notificationsVC.didMoveToParentViewController(self)
var friendsFeedFrame = friendsFeedVC.view.frame
friendsFeedFrame.origin.x = self.view.frame.width
friendsFeedVC.view.frame = friendsFeedFrame
var notificationsFrame = notificationsVC.view.frame
notificationsFrame.origin.x = self.view.frame.width * 2
notificationsVC.view.frame = notificationsFrame
self.scrollViewOfViews.contentSize = CGSize(width: self.view.frame.width * 3, height: self.view.frame.size.height)
// start scrollview page on middle view
var startFrame = scrollViewOfViews.frame
startFrame.origin.x = self.view.frame.width
startFrame.origin.y = 0
scrollViewOfViews.scrollRectToVisible(startFrame, animated: false)
}
The result for the friendsFeedVC with a centered Label:
"You should not initialize UI geometry-related things in viewDidLoad, because the geometry of your view is not set at this point and the results will be unpredictable"
"viewWillAppear: is the correct place to do these things, at this point the geometry is set so getting and setting geometry-related properties makes sense."
Or you can set them in viewDidLayoutSubviews
Try to place the codes that set frame of views in viewWillAppear or viewDidLayoutSubviews
Ref: Why am I having to manually set my view's frame in viewDidLoad?

I have viewcontrollers in a scrollview, but how do I start on a certain page?

I have a scroll view with 3 viewcontrollers which scrolls horizontally, one of the viewcontrollers has 3 more viewcontrollers which scroll vertically. The only way to access these viewcontrollers is by logging in. On the vertical scrollview I want the middle page to be the starting page, then the user has the option to scroll up or down. Right now the vertical scrollview starts with the first page, but I want it to start with middle page (second page).
#IBOutlet weak var scroller: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
let profile: ProfileView = self.storyboard?.instantiateViewControllerWithIdentifier("profile") as! ProfileView
let music: View2 = self.storyboard?.instantiateViewControllerWithIdentifier("View2") as! View2
let camera: Camera = self.storyboard?.instantiateViewControllerWithIdentifier("camera") as! Camera
self.addChildViewController(profile)
self.scroller.addSubview(profile.view)
profile.didMoveToParentViewController(self)
self.addChildViewController(music)
self.scroller.addSubview(music.view)
music.didMoveToParentViewController(self)
self.addChildViewController(camera)
self.scroller.addSubview(camera.view)
camera.didMoveToParentViewController(self)
var musicFrame: CGRect = profile.view.frame
musicFrame.origin.y = 2 * self.view.frame.height
music.view.frame = musicFrame
var profileFrame: CGRect = camera.view.frame
profileFrame.origin.y = self.view.frame.height
profile.view.frame = profileFrame
self.scroller.contentSize = CGSizeMake(self.view.frame.width, self.view.frame.size.height*3)
// Do any additional setup after loading the view.
}
The scrollview has a contentoffset which you can set the y position of. Detect the the y position of the middle view and set the scrollviews contentOffSet to it
If memory serves you'll have to animate (or not) the change to the contentOffset in viewWillAppear

Resources