I am trying to use a UICollectionView to create a grid which a user can set to be x cells by y cells (entered in text boxes), while still occupying the same width on the scr
Simple, add an extension of your view controller that implements the UICollectionViewDelegateFlowLayout
protocol:
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let size = collectionView.frame.size.width / CGFloat(cols) - CGFloat((cols - 1)) * spacingBetweenCells
return CGSize(width: size, height: size)
}
}
spacingBetweenCells
represents here the spacing you want to place between your cells.
Here is code to add to your collectionView. The properties cellsAcross
, cellsDown
, horizGap
, and vertGap
are the values you can set in your app. The horizGap
and vertGap
are set in the methods minimumInteritemSpacingForSectionAtIndex
and minimumLineSpacingForSectionAtIndex
. sizeForItemAtIndexPath
will compute the necessary dimensions of your cell to make it work.
var cellsAcross = 5
var cellsDown = 7
var horizGap = 4
var vertGap = 4
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cellsAcross * cellsDown
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return CGFloat(vertGap)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return CGFloat(horizGap)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
// Compute the dimensions of a cell for a cellsAcross x cellsDown layout.
let dimH = (collectionView.bounds.width - (CGFloat(cellsAcross) - 1) * CGFloat(horizGap)) / CGFloat(cellsAcross)
let dimV = (collectionView.bounds.height - (CGFloat(cellsDown) - 1) * CGFloat(vertGap)) / CGFloat(cellsDown)
return CGSize(width: dimH, height: dimV)
}
For this demo, I added the following code:
@IBAction func threeBy5(sender: UIButton) {
cellsAcross = 3
cellsDown = 5
collectionView.reloadData()
}
@IBAction func fourBy6(sender: UIButton) {
cellsAcross = 4
cellsDown = 6
collectionView.reloadData()
}
@IBAction func fiveBy7(sender: UIButton) {
cellsAcross = 5
cellsDown = 7
collectionView.reloadData()
}
// Handle screen rotation
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
collectionView.reloadData()
}
Try this code:
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
@IBOutlet var collectionView: UICollectionView!
let rows = 7
let columns = 3
let spaceBetweenRows = 2
let spaceBetweenColumns = 4
var cellHeight: CGFloat {
get {
return collectionView.frame.height/CGFloat(rows)-CGFloat(spaceBetweenRows)
}
}
var cellWidth: CGFloat {
get {
return collectionView.frame.width/CGFloat(columns)-CGFloat(spaceBetweenColumns)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
collectionView.dataSource = self
collectionView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return rows*columns
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return CGFloat(spaceBetweenRows)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return CGFloat(spaceBetweenRows)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSize(width: cellWidth, height: cellHeight)
}
}
Main.storyboard
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="stackoverflow_38993378" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="StH-xe-9BK">
<rect key="frame" x="0.0" y="20" width="600" height="580"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="ILL-QA-nex">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="cell" id="NTl-75-34J">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
</collectionViewCell>
</cells>
</collectionView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="StH-xe-9BK" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="RD1-j0-nbz"/>
<constraint firstItem="StH-xe-9BK" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" id="SMe-NI-E7C"/>
<constraint firstItem="StH-xe-9BK" firstAttribute="bottom" secondItem="wfy-db-euE" secondAttribute="top" id="hxb-pd-z2C"/>
<constraint firstAttribute="trailing" secondItem="StH-xe-9BK" secondAttribute="trailing" id="zgF-i5-4fX"/>
</constraints>
</view>
<connections>
<outlet property="collectionView" destination="StH-xe-9BK" id="eU8-eE-LXc"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="706" y="586"/>
</scene>
</scenes>
</document>
results on different iPhones
Swift 4.2 version for the answer provided by @vacawama:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return CGFloat(vertGap)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return CGFloat(horizGap)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// Compute the dimensions of a cell for a cellsAcross x cellsDown layout.
let dimH = (collectionView.bounds.width - (CGFloat(cellsAcross) - 1) * CGFloat(horizGap)) / CGFloat(cellsAcross)
let dimV = (collectionView.bounds.height - (CGFloat(cellsDown) - 1) * CGFloat(vertGap)) / CGFloat(cellsDown)
return CGSize(width: dimH, height: dimV)
}