Cocoa NSScrollView not scrolling - swift

I try to create a scrollview. Created a scrollview via Xib, programmatically added scrolledView (a NSView that contains all subview in scrollView).
The following code shows the subviews but the scrollView not scroll. Why?
class LevelScrollController: NSViewController {
#IBOutlet var scrollView: NSScrollView!
override func viewDidLoad() {
super.viewDidLoad()
let scrolledView = NSView(frame: NSRect(x: 0, y: 0, width: scrollView.frame.size.width, height: 300))
// Inserisco pulsanti di esempio
for i in 1 ... 10{
scrolledView.addSubview(NSButton(frame: NSRect(x: 0, y: i*30, width: 100, height: 30)))
}
scrollView.addSubview(scrolledView)
}
Putting the code in viewDidLayout instead of viewDidLoad not change the result: not scroll

Instead of add scrolledView to subview of scrollView, set it as documentView.
Replace scrollView.addSubview(scrolledView) with scrollView.documentView = scrolledView

Related

How to change the height of the naviagtionBar

In the latest version of xcode, I wrote the following code in the viewDidLoad to change the height of the NavigationBar.
self.navigationController?.navigationBar.frame = CGRect(x:0, y:0, width:UIScreen.main.bounds.size.width, height:40)
However, I couldn't change the height of the NavigationBar.
I would appreciate it if you could tell me how to solve it.
There are mainly 2 Approaches that i used
First Approach
Better to create Subclass of UINavigationController ... and add this method there .. Use this class as NavigationController
class MyNavigationController: UINavigationController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let height = CGFloat(72)
navigationBar.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: height)
}
}
Second workaround
You can create a Subclass of UINavigationBar like this
class MyNavigationBar: UINavigationBar {
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width, height: 75)
}
}

Addding UIView between tabBar and Navigation view in UITabBarController

I am working on a project that uses a UITabBarController for displaying all the different UIViewControllers but now I need to add a mini player just in between the tabBar and the navigation view (ViewControllers will have to resize too).
Is there anyway I can achieve that by reusing the existing class?
EDIT
I have tried 2 methods:
1- Adding it into the view. Gets Added but above of the VCs
let aView = UIView()
view.addSubview(aView)
aView.backgroundColor = .white
aView.anchor(top: nil, leading: view.leadingAnchor, bottom: tabBar.topAnchor, trailing: view.trailingAnchor, size: .init(width: 0, height: 100))
2- Adding it into the tabBar. It might sound silly but I thought It would work.
let viewOverTabBar = UIView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
viewOverTabBar.backgroundColor = UIColor.black
tabBar.addSubview(viewOverTabBar)
Add your view as subview to view of UITabBarViewController not tab bar itself. Just place it above tab bar.
Also change:
aView.anchor(top: nil, leading: view.leadingAnchor, bottom: tabBar.topAnchor, trailing: view.trailingAnchor, size: .init(width: 0, height: 100))
to setting directly frame property of your view.
Also you need to do in in viewWillAppear method.
You can try this way :
class MyTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.createSmallPlayer()
}
func createSmallPlayer() {
let viewOverTabBar = UIView(frame: CGRect(x: 0, y: self.tabBar.frame.origin.y-40, width: self.tabBar.frame.size.width, height: 30))
viewOverTabBar.backgroundColor = UIColor.brown
//viewOverTabBar.layer.cornerRadius = viewOverTabBar.frame.size.height/2
viewOverTabBar.layer.masksToBounds = false
viewOverTabBar.layer.shadowColor = UIColor.black.withAlphaComponent(0.5).cgColor
viewOverTabBar.layer.shadowRadius = 5.0
viewOverTabBar.layer.shadowOffset = CGSize(width: 0.0, height: -5.0)
viewOverTabBar.layer.shadowOpacity = 0.5
//tabBar.addSubview(viewOverTabBar)
view.addSubview(viewOverTabBar)
}
}
And make sure that your all other view controller(which will navigate within tabbar) adjust frame accordingly.
Either you have to manage bottom view of all view controller by 30 pixels up and keep 30 pixels space blank at bottom, so no any content hide behind your player view.
Or you have you add :
Container view UIView same as added player view.
In that you have to add view controller's view with navigation controller as subview(Refer this : Adding a view controller as a subview in another view controller).

How to make a stretchable image view by scroll view

I have a scroll view and an imageView at the top, I want to pin and make a stretchable image view but if I add this image view to the view the image won't disappear when the user scroll down the view and if I add the image to the scroll view this won't be pinned at the top when the user scroll down.
So how can I pin the image at the top and then when the user scroll down the image will disappear.
Like that: http://blog.enabled.com.au/stretchy-layouts-on-ios/ but not with his framework.
class LocalsVC: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
setNavBarSettings()
scrollView.delegate = self
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
view.addSubview(imageView)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let y = 300 - (scrollView.contentOffset.y + 300)
let height = min(max(y, 60), 400)
imageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: height)
}
Add the image to the scrollView and not to the view: scrollView.addSubview(imageView).

UIViewController not adjusting correctly in UIScrollView for all iPhones

I am trying to replicate a Tinder like menu, with the 3 UIViewControllers in a UIScrollView and a custom menu tab on the top, with a button for each UIViewController. I am facing an interesting problem where the UIViewControllers views fit perfectly in the scrollView.frame, but only for iPhone 8. In contrast, for iPhone SE, it leaves a white margin and for iPhone 8+, it seems to overlap the views within the scrollView.view. Could someone explain why this is happening and how I can fix it?
Here's my code where I'm adjusting setting up my UIViewControllers in my UIScrollView:
import UIKit
class MainViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var navigationView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
setUpHorizontalScrollViews()
}
func setUpHorizontalScrollViews(){
let view = (
x: self.view.bounds.origin.x,
y: self.view.bounds.origin.y,
width: self.view.bounds.width,
height: self.view.bounds.height
)
let scrollWidth = 3 * view.width
let scrollHeight = view.height
scrollView.contentSize = CGSize(width: scrollWidth, height: scrollHeight)
scrollView.contentOffset.x = view.x
if let messagesView = self.storyboard?.instantiateViewController(withIdentifier: "MessagesVC") as UIViewController! {
self.addChildViewController(messagesView)
self.scrollView.addSubview(messagesView.view)
messagesView.didMove(toParentViewController: self)
messagesView.view.frame = CGRect(x: 0,
y: 0,
width: view.width,
height: view.height
)
}
if let friendsView = self.storyboard?.instantiateViewController(withIdentifier: "FriendsVC") as UIViewController! {
self.addChildViewController(friendsView)
self.scrollView.addSubview(friendsView.view)
friendsView.didMove(toParentViewController: self)
friendsView.view.frame = CGRect(x: view.width,
y: 0,
width: view.width,
height: view.height
)
}
if let settingsView = self.storyboard?.instantiateViewController(withIdentifier: "SettingsVC") as UIViewController! {
self.addChildViewController(settingsView)
self.scrollView.addSubview(settingsView.view)
settingsView.didMove(toParentViewController: self)
settingsView.view.frame = CGRect(x: 2 * view.width,
y: 0,
width: view.width,
height: view.height
)
}
// offset to the second view
self.scrollView.contentOffset.x = view.width
}
override var shouldAutorotate: Bool {
return false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This is what my Setup looks like. the top is the MainViewController, containing the UIScrollView and on bottom are the 3 viewcontrollers that are supposed to go into the scrollView.
This is what I want it to look like, and the way it sets up in iPhone 8:
This is what it looks like on iPhone SE and where my problem is:
The problem is that since you are calling the setUpHorizontalScrollViews method in the viewDidLoad(), the UIViewController has yet to layout the subview, and also calculate their final size.
It is working in an iPhone 8 because most provably it has the same screen size you used in the interface builder.
Solution 1
In order to solve the problem, you can move your code to the viewDidAppear() method. However, this will cause an ugly effect once you open the UIViewController (unless you add a full screen loading).
Solution 2
Add view.layourIfNeeded() like this:
override func viewDidLoad() {
super.viewDidLoad()
view.layourIfNeeded()
setUpHorizontalScrollViews()
}

How to fix AutoLayout for my ScrollView?

I have a view controller that has a UIView(ParentView) and then UIScrollview(ScrollView). The ParentView is anchored to the leading, trailing, top and bottom of the ViewController. The ScrollView is anchored to the leading, trailing, top and bottom of the ParentView. The structure is like this:
-UIView
--ParentView
---ScrollView
Then I created a xib file that has an image and a label. The xib will be added dynamically to the ScrollView. The xib frame height and width are equal to the ScrollView height and width.
When I run the simulator on iphone 6 plus the scroll works perfectly; the width of the xib is exactly the width of the screen. But when i run the simulator with iphone 6, the width of the xib is the same size of the screen, there is an extra space when scrolling to the next item. What should I do to removed it!?
This is a screen shot for iphone 6 plus
This is a screen shot for iphone 6. Notice the extra space of the second screen
And here is my code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var ParentView: UIView!
#IBOutlet weak var ScrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let photos = ["joe", "john"]
let userNames = ["joe", "john"]
self.ScrollView.frame = CGRectMake(0, 0, self.ParentView.frame.size.width, self.ParentView.frame.size.height)
self.ScrollView.contentSize = CGSizeMake(self.ParentView.frame.size.width * CGFloat(photos.count), self.ParentView.frame.size.height)
self.ScrollView.pagingEnabled = true
var i: Int = 0
while i < 2 {
if let userXib = NSBundle.mainBundle().loadNibNamed("User", owner: self, options: nil)[0] as? User {
userXib.frame = CGRect(x: self.ScrollView.frame.size.width * CGFloat(i), y: 0, width: self.ScrollView.frame.size.width, height: userXib.frame.size.height)
userXib.assignValues(photos[i], myName: userNames[i])
self.ScrollView.addSubview(userXib)
}
i = i + 1
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
UPDATE #1 - Coder1000 solution:
I tried implementing #3 & #4 in your answer by adding the UIView layer inside the scroll. I called it ScrollMainView. In the storyboard, I set its leading, trailing, top and bottom to fill its superview: Scrollview and then in the loop added those elements to it instead of the scrollview. But now the scroll doesn't scroll! My code and final display look like this:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var ParentView: UIView!
#IBOutlet weak var ScrollView: UIScrollView!
#IBOutlet weak var ScrollMainView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let photos = ["joe", "john"]
let userNames = ["joe", "john"]
self.ScrollView.frame = CGRectMake(0, 0, self.ParentView.frame.size.width, self.ParentView.frame.size.height)
self.ScrollView.contentSize = CGSizeMake(self.ParentView.frame.size.width * CGFloat(photos.count), self.ParentView.frame.size.height)
self.ScrollView.pagingEnabled = true
self.ScrollMainView.frame = CGRectMake(0, 0, self.ScrollView.frame.size.width, self.ScrollView.frame.size.height)
print("the frame for parentview is: \(self.ParentView.frame)")
var i: Int = 0
while i < 2 {
if let userXib = NSBundle.mainBundle().loadNibNamed("User", owner: self, options: nil)[0] as? User {
userXib.frame = CGRect(x: self.ScrollMainView.frame.size.width * CGFloat(i), y: 0, width: self.ScrollMainView.frame.size.width, height: userXib.frame.size.height)
userXib.assignValues(photos[i], myName: userNames[i])
self.ScrollMainView.addSubview(userXib)
}
i = i + 1
}
self.ScrollView.addSubview(ScrollMainView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
There are several solutions, in viewDidLoad the frame is not set correctly, so when you ask it, it is wrong.
The parentView and your scrollView are adjusted by the autolayout constraint later, but you User view is not...
A simple solution may be to put the code in:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.ScrollView.frame = CGRectMake(0, 0, self.ParentView.frame.size.width, self.ParentView.frame.size.height)
self.ScrollView.contentSize = CGSizeMake(self.ParentView.frame.size.width * CGFloat(photos.count), self.ParentView.frame.size.height)
self.ScrollView.pagingEnabled = true
for subView: UIView in self.ScrollView.subviews {
if subView is User { subView.removeFromSuperview()
}
var i: Int = 0
while i < 2 {
if let userXib = NSBundle.mainBundle().loadNibNamed("User", owner: self, options: nil)[0] as? User {
userXib.frame = CGRect(x: self.ScrollView.frame.size.width * CGFloat(i), y: 0, width: self.ScrollView.frame.size.width, height: userXib.frame.size.height)
userXib.assignValues(photos[i], myName: userNames[i])
self.ScrollView.addSubview(userXib)
}
i = i + 1
}
}
Or you can add constraints to your custom xib programmatically.
Or you can keep an array of your costom xibs and just adjust their frames, AND THE SCROLLVIEW CONTENTSIZE, in viewDidLayoutSubviews()
Or you can put the code in viewDidAppear()