Embedding UINavigationController in scrollView - ios

I'm trying to do an application with 3 startup views, which you can swipe between. Pretty much Snapchat like.
I've got this going pretty well, but when I try to embed one of the 3 View Controller, it gets resized to a small part of the screen.
Here's my code:
override func viewDidLoad() {
super.viewDidLoad()
let accCtrl = AccountViewController()
let feedCtrl = UINavigationController(rootViewController: HomeViewController())
let hashsCtrl = AccountViewController()
self.addChildViewController(hashsCtrl)
self.scrollView!.addSubview(hashsCtrl.view)
hashsCtrl.didMoveToParentViewController(self)
self.addChildViewController(feedCtrl)
self.scrollView!.addSubview(feedCtrl.view)
feedCtrl.didMoveToParentViewController(self)
self.addChildViewController(accCtrl)
self.scrollView!.addSubview(accCtrl.view)
accCtrl.didMoveToParentViewController(self)
accCtrl.view.frame = view.frame
// feedCtrl.view.autoresizesSubviews = false
feedCtrl.view.frame = CGRectMake(view.frame.width, 0, view.frame.width, view.frame.height)
feedCtrl.setNavigationBarHidden(true, animated: false)
feedCtrl.view.setNeedsLayout()
hashsCtrl.view.frame = CGRectMake(view.frame.width * 2, 0, view.frame.width, view.frame.height)
scrollView.contentSize = CGSizeMake(view.frame.width * 3, view.frame.height)
scrollView.pagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false
scrollView.contentOffset = CGPointMake(view.frame.width, 0)
}
I've noticed that the resulting width of the view on-screen is pretty much x - 280. If I set the feedCtrl.view.frame to a width, the resulting width is 1.0 point. As it is actually - aka the screen width -, it results in a 40.0 point width.
I've also noticed that if I uncomment the line where I put the feedCtrl.view.autoresizeSubviews boolean to false, the width on-screen is correct, but I can't get user-interaction on the view.
Any ideas ?

I had a similar problem. In viewDidLoad, try to add (that worked for me):
self.ScrollView.frame = (UIApplication.sharedApplication().delegate?.window!.frame)!

Related

UIView unable to get transparent property

I am trying to add a subview to the window (for using it as side menu). It has a child view which is transparent view. However I am unable to get the transparent property when it runs in simulator.
I am using the following code:
override func viewWillAppear(_ animated: Bool) {
frame = CGRect(x: 0, y:0, width:0, height:0)
frame.size.height = UIScreen.main.bounds.height
frame.size.width = UIScreen.main.bounds.width
sideMenuView.frame = frame
sideMenuTransparentView.isOpaque = false
sideMenuTransparentView.backgroundColor = UIColor.gray.withAlphaComponent(0.5)
UIApplication.shared.keyWindow?.addSubview(sideMenuView)
}
However I am seeing no effect of this to make the view as transparent. What's possibly wrong here and how should I proceed rectifying this?
The weird thing here is that when I replace the lines
sideMenuTransparentView.isOpaque = false
sideMenuTransparentView.backgroundColor = UIColor.gray.withAlphaComponent(0.5)
with
sideMenuView.isOpaque = false
sideMenuView.backgroundColor = UIColor.gray.withAlphaComponent(0.5)
I can see the effect. I am unable to understand what's going in here and how should I correct this.
You seem to have forgotten to add the transparent view to the main sideMenuView.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) // You need to call super when overriding
frame = CGRect(x: 0, y:0, width:0, height:0)
frame.size.height = UIScreen.main.bounds.height
frame.size.width = UIScreen.main.bounds.width
sideMenuView.frame = frame
sideMenuTransaparentView.frame = yourFrame // required frame
sideMenuTransparentView.isOpaque = false
sideMenuTransparentView.backgroundColor = UIColor.gray.withAlphaComponent(0.5) // 100% transparency
sideMenuView.addSubview(view: sideMenuTransparentView)
UIApplication.shared.keyWindow?.addSubview(sideMenuView)
}
Also, you forgot to call super.

UIScrollView pages content not working properly - iOS

I've been trying for one whole day to make it work right. I tried different tutos and made adjustments (searching in here) but it doesnt work properly.
On iphone 5s, it almost works right, excepts for the last page that it seems to add "half" of a page on scroll view. The green part is the background color of the scroll view and it doesn't bounce back.
On iphone 6, the content is all messed up and instead of having 5 images, it has only 4 and a half (i cant scroll it anymore and it doesn't bounce back).
If I press continue, it works perfectly on both screen sizes. It is only the swipe option that is not working properly.
Here is the code where I put the images inside the scroll view.
func loadContent() {
var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
for i in 0..<imageArr.count { // is 5
frame.origin.x = scrollView.frame.size.width * CGFloat(i)
frame.size = self.scrollView.frame.size
let imageView = UIImageView(frame: frame)
let image = UIImage(named: imageArr[i])
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleBottomMargin, .flexibleRightMargin, .flexibleLeftMargin, .flexibleTopMargin]
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
imageView.backgroundColor = UIColor.red
imageView.image = image
scrollView.addSubview(imageView)
self.scrollView.contentSize.width = self.scrollView.frame.width * CGFloat(i)
}
}
///Function of Continue button
#IBAction func nextImage(_ sender: Any) {
UIView.animate(withDuration: 0.3) {
self.scrollView.contentOffset.x = self.scrollView.frame.width * CGFloat(self.pageControl.currentPage+1)
}
}
I've been searching for 4 hours for a solution but nothing seems to work.
[Edit]: With the answer of TheFuquan I could solve the problem.
[Obs:] if you searching about the messed content, these lines of code can help you. All the tutos that I saw didn't have these lines and the content just wouldnt align perfect and this solved:
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleBottomMargin, .flexibleRightMargin, .flexibleLeftMargin, .flexibleTopMargin]
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
Why is the following line in your for loop:
self.scrollView.contentSize.width = self.scrollView.frame.width * CGFloat(i)
You should do this before the for loop like this:
self.scrollView.contentSize.width = self.scrollView.frame.width * imageArr.count
this is a typical problem that i face each time i try to load some dynamic content inside a view.
If you re calling your function loadContent from viewDidLoad, then you should call your loadContent inside viewDidLayoutSubviews
Be aware that unlike viewDidLoad, viewDidLayoutSubviews may get called multiple times, so have a flag to ensure that your loadContent gets invoked only once.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard self.didLoadContent == false else {
return
}
self.didLoadContent = true
loadContent()
}
this way you let the view loaded from your storyboard to resize the iPhone's size.

How to embed stack view in scroll view programmatically

I have tried embedding it, but my stack view is dynamic and my app is also changing orientations time to time. I have segment control at the end of the view.
I have also tried googling it but had no luck. thanks in advance.
So far I have done:
In view did load:
mainStackView.axis = UILayoutConstraintAxis.Vertical
mainStackView.spacing = 3
scrollView.frame = self.view.bounds
scrollView.addSubview(mainStackView)
view.addSubview(scrollView)
In view did layout:
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
let top = topLayoutGuide.length
let bottom = bottomLayoutGuide.length
self.mainStackView.frame = CGRect(x: 0, y: top, width: view.frame.width, height: view.frame.height - top - bottom).insetBy(dx: 10, dy: 10)
dispatch_async(dispatch_get_main_queue())
{
self.scrollView.frame = self.view.bounds
self.scrollView.contentSize = CGSize(width: self.view.bounds.width, height: self.segmentedControl.frame.origin.y + self.segmentedControl.frame.height + 50)
}
print(scrollView.contentSize)
}
You need to set the height constraint of segment control.
For Example:
segmentedControl.heightAnchor.constraintEqualToConstant(50).active = true
More over, you can Add Empty bottom view to avoid stack view's must fill mechanism. This will show you desired view output.
var bottomView = UIView(frame: CGRectZero)
stackView.addArrangedSubview(bottomView)

When to use UITouch vs UIScroll

I would like to achieve the design you see in dating apps. Where you can vertically scroll images of a profile and also horizontally scroll to view the next or previous person in the list.
Currently I have my views laid out as such.
Previous-UIView - current UIView - next UIView
UIScrollView. UIScrollView. UIScrollView
Images. Images. Images
UIView. UIView. UIView
Profile info. Profile info. Profile info
UIPageControl. UIPageControl UIPageControl.
Only one of the Views occupies the main view with next and previous off screen. Ideally when the user moves the view left I would programmatically remove the previous view, make current the previous, the next current and add a new view for next. Visa versa for moving right.
What is the best way to scroll the views horizontally?
Should I wrap them all in a UIScrollView? And would that interfere with the UIScrollView sub Views?
Or should I program touch controls to move the views?
Or is there a better way?
I'm still a newbie at iOS development so any help would be greatly appreciated.
So I've tried some experimenting with a test app and I'm pleased to say you can have UIScrollviews inside UIScrollviews.
I was able to get it running perfectly. Here is my code below.
override func viewDidLoad() {
super.viewDidLoad()
self.superView.delegate = self
// Do any additional setup after loading the view, typically from a nib.
var subImages1 = ["IMG_0004.JPG","IMG_0005.JPG","IMG_0008.JPG"]
var subImages2 = ["IMG_0009.JPG","IMG_0010.JPG","IMG_0011.JPG"]
var subImages3 = ["IMG_0013.JPG","IMG_0017.JPG","IMG_0018.JPG"]
self.images.append(subImages1)
self.images.append(subImages2)
self.images.append(subImages3)
self.superView.frame = self.view.frame
self.superView.contentSize = CGSizeMake(self.view.frame.width*3, self.view.frame.height)
self.superView.contentOffset = CGPoint(x:self.view.frame.width,y:0)
self.superView.pagingEnabled = true
self.view.addSubview(self.superView)
//layout the UIVeiws into the master ScrollView
for i in 0...2{
var offset = self.view.frame.width * CGFloat(i)
var pView = UIView()
pView.frame = CGRectMake(offset, 0, self.view.frame.width, self.view.frame.height)
pView.backgroundColor = colours[i]
self.superView.addSubview(pView)
self.profileViews.append(pView)
}
// Add sub Scroll views and images to the Views.
for (index, view) in enumerate(self.profileViews){
var scrollView = UIScrollView()
scrollView.delegate = self
scrollView.frame = CGRectMake(10, 10, self.view.frame.width-20, self.view.frame.height-20)
scrollView.pagingEnabled = true
scrollView.contentSize = CGSizeMake(scrollView.frame.width, scrollView.frame.height * CGFloat(images[index].count))
for (index2, image) in enumerate(images[index]){
var subImage = UIImageView()
subImage.frame = CGRectMake(0, scrollView.frame.height * CGFloat(index2), scrollView.frame.width, scrollView.frame.height)
subImage.contentMode = UIViewContentMode.ScaleAspectFit
subImage.image = UIImage(named: image as! String)
scrollView.addSubview(subImage)
}
view.addSubview(scrollView)
self.scrollViews.append(scrollView)
}
}
//Use the did end decelerating as it executes the code once the scoll has finished moving.
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
if(scrollView == self.superView){
var contentOffset = scrollView.contentOffset
var pageWidth = self.superView.frame.width
var fractionalPage:Double = Double(self.superView.contentOffset.x / pageWidth)
var page = lround(fractionalPage)
// In this example I take the last UIView from the stack and move it to the first.
// I would do the same in the real app but update the contents of the view after
if(page == 0){
var tempView = self.profileViews[2]
self.profileViews[2].removeFromSuperview()
self.profileViews.removeAtIndex(2)
for view in self.profileViews{
view.frame = CGRectMake(view.frame.minX + self.view.frame.width, 0, view.frame.width, view.frame.height)
println(view.frame)
}
tempView.frame = CGRectMake(0, 0, tempView.frame.width, tempView.frame.height)
self.profileViews.insert(tempView, atIndex: 0)
self.superView.addSubview(tempView)
var newOffset = contentOffset.x + pageWidth
self.superView.contentOffset = CGPoint(x: newOffset, y: 0)
}
// Take the first view and move it to the last.
if(page == 2){
var tempView = self.profileViews[0]
self.profileViews[0].removeFromSuperview()
self.profileViews.removeAtIndex(0)
for view in self.profileViews{
view.frame = CGRectMake(view.frame.minX - self.view.frame.width, 0, view.frame.width, view.frame.height)
println(view.frame)
}
tempView.frame = CGRectMake(tempView.frame.width*2, 0, tempView.frame.width, tempView.frame.height)
self.profileViews.append(tempView)
self.superView.addSubview(tempView)
var newOffset = contentOffset.x - pageWidth
self.superView.contentOffset = CGPoint(x: newOffset, y: 0)
}
}
}

Twitter-like UIScrollView with ViewControllers as pages

Video of the issue!
Example of what I mean by Twitter-like UIScrollView:
I basically have it working, but I have this small glaring issue and I don't know where it is coming from. I have checked all the constraints and values for my two view controllers, but something is off.
In short,
The code that creates the NavBar and then populates it with the two ViewControllers side by side:
override func viewDidLoad() {
super.viewDidLoad()
var navBar: UINavigationBar = UINavigationBar(frame: CGRectMake(0, 0, self.view.bounds.width, 64))
navBar.barTintColor = UIColor.blackColor()
navBar.translucent = false
//Creating some shorthand for these values
var wBounds = self.view.bounds.width
var hBounds = self.view.bounds.height
// This houses all of the UIViews / content
scrollView = UIScrollView()
scrollView.backgroundColor = UIColor.clearColor()
scrollView.frame = self.view.frame
scrollView.pagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
scrollView.bounces = false
self.view.addSubview(scrollView)
self.scrollView.contentSize = CGSize(width: self.view.bounds.size.width * 2, height: hBounds)
//Putting a subview in the navigationbar to hold the titles and page dots
navbarView = UIView()
//Paging control is added to a subview in the uinavigationcontroller
pageControl = UIPageControl()
pageControl.frame = CGRect(x: 0, y: 35, width: 0, height: 0)
pageControl.pageIndicatorTintColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.3)
pageControl.currentPageIndicatorTintColor = UIColor.whiteColor()
pageControl.numberOfPages = 2
pageControl.currentPage = 0
self.navbarView.addSubview(pageControl)
//Titles for the nav controller (also added to a subview in the uinavigationcontroller)
//Setting size for the titles. FYI changing width will break the paging fades/movement
navTitleLabel1 = UILabel()
navTitleLabel1.frame = CGRect(x: 0, y: 8, width: wBounds, height: 20)
navTitleLabel1.textColor = UIColor.whiteColor()
navTitleLabel1.textAlignment = NSTextAlignment.Center
navTitleLabel1.text = "Title 1"
self.navbarView.addSubview(navTitleLabel1)
navTitleLabel2 = UILabel()
navTitleLabel2.alpha = 0.0
navTitleLabel2.frame = CGRect(x: 100, y: 8, width: wBounds, height: 20)
navTitleLabel2.textColor = UIColor.whiteColor()
navTitleLabel2.textAlignment = NSTextAlignment.Center
navTitleLabel2.text = "Title 2"
self.navbarView.addSubview(navTitleLabel2)
//Views for the scrolling view
//This is where the content of your views goes (or you can subclass these and add them to ScrollView)
feedViewController = storyboard?.instantiateViewControllerWithIdentifier("FeedController") as FeedViewController
view1 = feedViewController.view
addChildViewController(feedViewController)
feedViewController.didMoveToParentViewController(self)
view1.frame = CGRectMake(0, 0, wBounds, hBounds)
self.scrollView.addSubview(view1)
self.scrollView.bringSubviewToFront(view1)
//Notice the x position increases per number of views
secondViewController = storyboard?.instantiateViewControllerWithIdentifier("SecondController") as SecondViewController
view2 = secondViewController.view
addChildViewController(secondViewController)
secondViewController.didMoveToParentViewController(self)
view2.frame = CGRectMake(wBounds, 0, wBounds, hBounds)
self.scrollView.addSubview(view2)
self.scrollView.bringSubviewToFront(view2)
navBar.addSubview(navbarView)
self.view.addSubview(navBar)
}
I've looked at my storyboard and both ViewControllers seem identical in regards to their constraints.
I know this is an issue because both ViewControllers are populated by UITableViews. When I scroll through the SecondViewController, it works perfectly. When I scroll through the FeedViewController, there is a small white space at the top that I can't seem to get rid of and it shows that the text cuts off there. I've been stuck on this for a long time and if there is any other information needed, I'll gladly provide it.
Edit: Included video of the issue. If I could, I would bounty this question right now. I don't understand the cause
Update: After swapping both ViewController positions, I have noticed that the problem does not lie with either ViewController. The problem lies with page 1 being set lower. When swapped, the original SecondViewController also experienced the same behavior
So, I think everyone who implements this runs into this issue at some point. The issue isn't with the first ViewController. Simply adjust the constraint to be 44 from the top. The issue is with the second ViewController and it isn't so much an issue when you understand how they work. Technically, it is off to the side and hence its top constraint does not adhere to the Navigation Bar, so what you have is a constraint - 20. Which, depending on how you originally placed your constraints, can give you this seeming issue.
But basically, anyone and everyone will run into this issue when implementing this.
TL;DR: To make everything seamless, your second, third, fourth, fifth, etc. page View Controllers need a constraint + 20 of your first View Controller. With my set-up, I use a constraint of 44 for my first View Controller and hence 64 for the second

Resources