Can't change CollectionView frame with CGRect - ios

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.

Related

CollectionView does not scroll when added to view

Here is my code:
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = .greatestFiniteMagnitude
layout.minimumLineSpacing = 15
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: screenWidth*0.2, height: screenHeight*0.15)
collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: screenWidth*0.9, height: screenHeight*0.15), collectionViewLayout: layout)
collectionView?.layer.zPosition = 10
collectionView?.register(PlantSnapshotCell.self,forCellWithReuseIdentifier:PlantSnapshotCell.identifier)
collectionView?.backgroundColor = .clear
collectionView?.alwaysBounceHorizontal = true
collectionView?.bounces = true
collectionView?.showsHorizontalScrollIndicator = false
collectionView?.dataSource = self
collectionView?.delegate = self
scrollView.addSubview(collectionView!)
view.addSubview(scrollView)
It renders in the view, but any sort of interaction does not work. The width of the collectionView is 90% the width of the superview. Scrolling horizontally does not work.
Why you are adding collectionView as a subview to the scrollView?

Swift - Change UICollectionViewCell size programmatically

I am struggling to change the size my items in the CollectionView.
Custom Flowlayout class found here:
This is my CollectionView:
class ExampleViewController: UIViewController, UICollectionViewDataSource {
let theCollectionView: UICollectionView = {
let v = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .white
v.contentInsetAdjustmentBehavior = .always
v.layer.cornerRadius = 30
return v
}()
let columnLayout = FlowLayout(
itemSize: CGSize(width: 50, height: 50),
minimumInteritemSpacing: 10,
minimumLineSpacing: 10,
sectionInset: UIEdgeInsets(top: 20, left: 20, bottom: 10, right: 20)
)
I tried changing the itemSize but that doesnt do anything.
You didn't assigned you flowLayout to the collectionView either try:
theCollectionView.collectionViewLayout = columnLayout or
First define your layout :
let columnLayout = FlowLayout( ...
Then define your collectionView like this:
let theCollectionView: UICollectionView = {
let v = UICollectionView(frame: CGRect.zero, collectionViewLayout:columnLayout)
Here is Code for CollectionViewCell Size Change
(Note:- Change values according to your Requirement!)
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
layout.itemSize = CGSize(width: screenWidth/4, height: screenWidth/4)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
cell2.collectionView2.collectionViewLayout = layout

hidesbarsonswipe mutates view size

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!)
}

UICollectionViewCell with AutoLayout not working in iOS 10

I'm trying to create a dynamic UICollectionView whose cells auto-resize based on the text inside it. But for some reasons, my custom UICollectionViewCell won't expand to the full width. I am using SnapKit as AutoLayout and all my views are code-based; no xib or storyboard. Here's a debug view of what I got at the moment:
I want the cell to expand full width and the height to fit whatever the content is. Here's a snippet on my UICollectionViewController
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Home"
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: view.frame.width, height: view.frame.height)
layout.scrollDirection = .vertical
layout.estimatedItemSize = CGSize(width: 375, height: 250)
collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height), collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.backgroundColor = UIColor(red: 245/255, green: 245/255, blue: 245/255, alpha: 1)
collectionView.register(HomeCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
self.view.addSubview(collectionView)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
// Configure the cell
if let c = cell as? HomeCollectionViewCell {
c.contentView.frame = c.bounds
c.contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
configureCell(c, indexPath: indexPath)
}
return cell
}
func configureCell(_ cell: HomeCollectionViewCell, indexPath: IndexPath) {
cell.setText(withTitle: items[(indexPath as NSIndexPath).section].title)
}
And here's a snippet of my custom UICollectionViewCell
override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.contentView.translatesAutoresizingMaskIntoConstraints = true
self.contentView.bounds = CGRect(x: 0, y: 0, width: 99999, height: 99999)
createTitle()
}
private func createTitle() {
titleView = TTTAttributedLabel(frame: CGRect(x: 0, y: 0, width: contentView.frame.width, height: 56))
titleView.tag = 1
titleView.numberOfLines = 2
titleView.delegate = self
titleView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(titleView)
titleView.snp_updateConstraints(closure: {
make in
make.leading.trailing.equalTo(contentView).offset(10)
make.top.equalTo(contentView).offset(10)
make.bottom.equalTo(contentView).offset(-10)
})
}
func setText(withTitle title:String, paragraph: String, image: String) {
let titleAttributed = AttributedString(string: title, attributes: titleStringAttr)
titleView.attributedText = titleAttributed
titleView.sizeToFit()
}
I've spent 3 days just working on this on and on.. Any advice appreciated!
Well it's not really a solution, but the TTTAttributedLabel does some black magic that causes the AutoLayout not to work. So for me, I changed the TTTAttributedLabel to UILabel and it works fine.
FYI I posted similar question on SnapKit Github issues; credits to robertjpayne there for the hint (https://github.com/SnapKit/SnapKit/issues/261)

Block row and column CollectionView scroll

And this is my code :
var collectionView: UICollectionView?
var collectionViewSeries: UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
// Year
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 90, height: 120)
layout.scrollDirection = UICollectionViewScrollDirection.Horizontal
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView!.dataSource = self
collectionView!.delegate = self
collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
collectionView!.backgroundColor = UIColor.whiteColor()
collectionView?.scrollEnabled = false
self.view.addSubview(collectionView!)
// Series
let layoutSeries: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layoutSeries.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layoutSeries.itemSize = CGSize(width: 90, height: 120)
layoutSeries.scrollDirection = UICollectionViewScrollDirection.Horizontal
collectionViewSeries = UICollectionView(frame: self.view.frame, collectionViewLayout: layoutSeries)
collectionViewSeries!.dataSource = self
collectionViewSeries!.delegate = self
collectionViewSeries!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellSeries")
collectionViewSeries!.backgroundColor = UIColor.whiteColor()
collectionViewSeries?.scrollEnabled = true
self.view.addSubview(collectionViewSeries!)
}
My question:
I'm trying to block the first column and the first row by scrolling.
So that only the central cells can flow.
This is an example of what I want to do:
link
Thanks
scrolling cannot be disabled for a particular cell. You create a UIView in Section Header similar to Cell 1. This will work as you like.
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view = [[UIView alloc] init]; //your view
return view;
}

Resources