hidesbarsonswipe mutates view size - ios

I am having trouble with this piece of code:
navigationController?.hidesBarsOnSwipe = true
My navigation controller's root view controller is a UICollectionViewController. The blue view below is a cell that represents the user's current screen. I think the problem is that I need to resize the cell when the navigation bar hides.
I set the cell size like this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height - 44)
}
I am not breaking any constraints, and I am very sure everything is set up properly. But when I swipe up, this happens:
Before
After
As you can see, the view gets shortened. I can't find those measurements anywhere in my code.
Is there a way to ensure that the view gets resized properly?

Put this in your viewDidLoad() and you should be good to go.
self.edgesForExtendedLayout = []
yourBlueView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(yourBlueView)
yourBlueView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
yourBlueView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
yourBlueView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
yourBlueView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
Edit:
Change yourBlueView to the name you've given that particular UIView. Let me know if you get stuck, but do try to figure it out first. That's the best way to learn it and retain it.
The bar on the bottom takes at least 44 and I notice it's going under the nav bar at the top. Remove your hard code sizing which is too high, and only use the auto layout
Second Edit: (This is a non-autolayout approach)
let reuseIdentifier = "Cell"
This code goes in viewDidLoad:
// Do any additional setup after loading the view, typically from a nib.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
let displaySize = UIScreen.main.bounds
let displayHeight = displaySize.height - 40
let displayWidth = displaySize.width
layout.itemSize = CGSize(width: displayWidth, height: displayHeight)
if let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout) {
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
collectionView.backgroundColor = UIColor.white
self.view.addSubview(collectionView!)
}

Related

UICollectionView content hidden by TabBar

The problem
Hello, community! I created a UICollectionView programmatically and want it to display images that take the full size of the screen, surpassing the top safe area but respecting the bottom safe area, since at the bottom I have TabBar. The issue with the current UI is that I got it to ignore the top safe area, but the tab bar hides part of the content since the collection takes the size of the whole screen (Video). I'm trying so that my UI looks like the TikTok home page where the collectionView ends when the tab bar starts.
My Code
In ViewDidLoad:
let layout = UICollectionViewFlowLayout()
self.edgesForExtendedLayout = UIRectEdge.bottom
layout.scrollDirection = .vertical
layout.itemSize = CGSize(width: view.frame.size.width, height: view.frame.size.height)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView?.register(PostCollectionViewCell.self, forCellWithReuseIdentifier: PostCollectionViewCell.identifier)
collectionView?.isPagingEnabled = true
collectionView?.dataSource = self
collectionView?.showsVerticalScrollIndicator = false
collectionView?.showsHorizontalScrollIndicator = false
view.addSubview(collectionView!)
In ViewDidLayoutSubviews:
collectionView?.contentInsetAdjustmentBehavior = .never
collectionView?.frame = view.bounds
What I've tried
layout.itemSize = CGSize(width: view.frame.size.width, height: view.frame.size.height - (self.tabBarController?.tabBar.frame.height)!)
Substracting height of tabBar to CollectionViewFlowLayout
collectionView?.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: self.tabBarController!.tabBar.frame.height, right: 0)
collectionView?.contentSize = CGSize(width: view.frame.size.width, height: view.frame.size.height - self.tabBarController!.tabBar.frame.height)
Many Thanks!
You should use auto-layout instead of setting the frame of your collection view manually. After view.addSubview(collectionView!) put:
collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
This pins the bottom anchor of the collection viewto the bottom safe area of your view, which should account for the tab bar.
You should remove collectionView?.frame = view.bounds from viewDidLayoutSubviews.
Also you should not set your itemSize in viewDidLoad because the view's frame may change. You can set it in viewDidLayoutSubviews.

Can't change CollectionView frame with CGRect

I am trying to build a simple side menu using CollectionView. The problem is that I can't get it to work because changing frame using CGRect doesn't seem to do anything and the menu won't appear. Any help would be much appreciated.
let blackView = UIView()
var collectionView: UICollectionView {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv
}
func showMenu() {
if let window = UIApplication.shared.keyWindow {
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
window.addSubview(blackView)
window.addSubview(collectionView)
//This line doesn't work
collectionView.frame = CGRect(x: 0, y: 0, width: 700, height: window.frame.height)
//This works fine
blackView.frame = window.frame
That's because the collection view here:
window.addSubview(collectionView)
and the collectionView here
//This line doesn't work
collectionView.frame = CGRect(x: 0, y: 0, width: 700, height: window.frame.height)
are different objects. You add the first collectionView to the window, and then you change the frame of the second one. The misunderstanding comes from this code:
var collectionView: UICollectionView {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv
}
This creates a dynamic variable, so that whenever you access collectionView, a new UICollectionView is created. It's actually equivalent to a function call. It's like
func makeCollectionView() -> UICollectionView {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv
}
and then later doing
window.addSubview(makeCollectionView())
//This line doesn't work
makeCollectionView().frame = CGRect(x: 0, y: 0, width: 700, height: window.frame.height)
What you should do instead is define the variable at the beginning, and initialize it later. For example like this:
let blackView = UIView()
var collectionView: UICollectionView!
func showMenu() {
if let window = UIApplication.shared.keyWindow {
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
// Create collectionView
if collectionView == nil {
collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.backgroundColor = UIColor.white
}
window.addSubview(blackView)
window.addSubview(collectionView)
//This line works finally
collectionView.frame = CGRect(x: 0, y: 0, width: 700, height: window.frame.height)
//This works fine
blackView.frame = window.frame
(depending on your code setup, you may have to create the collectionView at another place, maybe in viewDidLoad or something like that (I'm not sure))
You define the wrong lazy var as the following:
var collectionView: UICollectionView {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv
}
In this situation, the following is the correct format which means its READONLY. you cannot change after return.
var collectionView: UICollectionView {
get{
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv}
}
So if you want to change frame, you should change before return like this:
var collectionView: UICollectionView {
get{
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
cv.frame = CGRect(x: 0, y: 0, width: 300, height: 300) // just an example.
return cv}
}
What you Really want should be like this and define it as a computed property, then you can change frame at anywhere:
var collectionView : UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv
}()
So, basically you just miss a "=" and "()". Hope you know the reason now.

Cell unwanted centered vertically in UICollectionView

I have a UICollectionView which is supposed to display one cell at a time. My problem is, that the cell is centered vertically which is not a desired effect and I can't seem to find where this is caused.
This screenshot shows that the cell has a padding of 46 to the top (as well as to the bottom). The cell is 308 high. Adding the 2x46 that comes to 400px which is the height of the collectionView.
I have tried to add UIEdgeInsets to the collectionView and set them all to 0 but that didn't do it.
Any idea why my collectionView would do this? Thanks!!
This is how I declare my collectionView:
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
cv.register(AddCardCell.self, forCellWithReuseIdentifier: "CardCell")
cv.backgroundColor = .white
cv.showsHorizontalScrollIndicator = false
cv.isPagingEnabled = true
cv.translatesAutoresizingMaskIntoConstraints = false
return cv
}()

Add horizontally scrollable filter options under UISearchController

I am developing a page that allows the user to search contents in a table, and I want to add some filter options under the search bar. I've added the UISearchController programmatically into the navbar, but I want to know how to add a list of horizontally scrollable filter options under the search bar (Just like the layout on the right in this picture: https://i.stack.imgur.com/XrjIq.png). Thanks!
I realize that this is probably something that has been asked/answered before, but I cannot find a single page discussing this. If you have a link to a solution I would really appreciate it!
You can create a horizontally scrolling collectionView with filter buttons in them and set it below the UISearchController.
You can create a collection view like this programmatically:
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.minimumInteritemSpacing = 0
layout.scrollDirection = UICollectionViewScrollDirection.horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
cv.dataSource = self
cv.delegate = self
cv.translatesAutoresizingMaskIntoConstraints = false
return cv
}()
Now you can add items to this collectionView using cellForItemAtIndexPath, numberOfItemsInSection and other delegates.
You would need to then set constraints on the collectionView like this:
collectionView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
collectionView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
collectionView.topAnchor.constraint(equalTo: searchController.bottomAnchor).isActive = true
collectionView.heightAnchor.constraint(equalToConstant: 44).isActive = true
Let me know if you need further explanation.

How to make UICollectionViewFlowLayout stay inside a UICollectionView

I have built a custom calendar using collection view. My problem is that when I add UICollectionViewFlowLayout it covers the whole screen and doesn't stay inside UICollectionView. How do I make the UICollectionViewFlowLayout stay inside UICollectionView? Here is my code:
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 100, left: 10, bottom: 1, right: 10)
let width = UIScreen.mainScreen().bounds.width
layout.itemSize = CGSize(width: width/10, height: 35)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView!.dataSource = self
collectionView!.delegate = self
collectionView!.registerClass(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
collectionView!.backgroundColor = UIColor.whiteColor()
self.view.addSubview(collectionView!)
Where I want the calendar to be:
This is how it covers the whole screen:
This line:
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
is creating a new instance of UICollectionView, and setting its frame to be the same as self.view (hence it covers the full screen). I suspect you actually want to use an existing instance of collection view which is established in a storyboard (and is probably being presented - but behind the new one!). Check in your storyboard to see whether the CollectionView is hooked up to the collectionView property of your view controller:
If so, you can just comment out the above line (and possibly the next two lines as well), since the links will be established when your view controller is instantiated.
If you do not have a storyboard instance, then just amend the frame to suit the position and size that you want, rather than using self.view.frame.

Resources