Trying to add constraints to a collection view.
Keep getting the error, "Use of unresolved identifier 'view'"
Any help is much appreciated.
import UIKit
class MenuBar: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
// cv.backgroundColor = UIColor.white
cv.dataSource = self
cv.delegate = self
return cv
}()
let cellId = "cellId"
override init(frame:CGRect) {
super.init(frame: frame)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
addSubview(self.collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.leadingAnchor.constraint(
equalTo: view.leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(
equalTo: view.trailingAnchor).isActive = true
collectionView.topAnchor.constraint(
equalTo: view.topAnchor,
constant: -20).isActive = true
collectionView.heightAnchor.constraint(
equalTo: view.heightAnchor,
multiplier: 0.10).isActive = true
backgroundColor = UIColor.red
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
cell.backgroundColor = .blue
return cell
}
There is no view property in a UIView, it's a property of UIViewController.
Since self is the view in this case, replace:
equalTo: view.leadingAnchor).isActive = true
with:
equalTo: self.leadingAnchor).isActive = true
and same for other ones.
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
collectionView.topAnchor.constraint(equalTo: self.topAnchor,constant: -20),
collectionView.heightAnchor.constraint(equalTo: self.heightAnchor,multiplier: 0.10)
)]
You need to replace view with self because this is not viewcontroller this is MenuView so yo need to use self for that:
collectionView.leadingAnchor.constraint(
equalTo: self.leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(
equalTo: self.trailingAnchor).isActive = true
collectionView.topAnchor.constraint(
equalTo: self.topAnchor,
constant: -20).isActive = true
collectionView.heightAnchor.constraint(
equalTo: self.heightAnchor,
multiplier: 0.10).isActive = true
Related
When the collection view cell is selected, cell background view and the other labels in that cell should change color. The below code is working for below ios 15. If i change didSet to willSet in the below code, it is working for ios 15 but its not working below ios 15. Is there a solution to change the color for the selected custom cell? I am adding the collection view delegate and datasource methods code as well.
override var isSelected: Bool{
didSet{
if self.isSelected
{
super.isSelected = true
lblName.textColor = .white
cellBGView.backgroundColor = .themeColor
cellInfoBtn.tintColor = .white
}
else
{
super.isSelected = false
lblName.textColor = .themeColor
cellBGView.backgroundColor = .white
cellInfoBtn.tintColor = .themeColor
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
selectIndex = indexPath.row
let cell = collectionView.cellForItem(at: indexPath) as! CustomCollectionCell
selectedIndexPath = indexPath
selectIndexSec = indexPath.section
collectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCollectionCell", for: indexPath) as! CustomCollectionCell cell.cellInfoBtn.tag = indexPath.row
cell.cellInfoBtn.addTarget(self, action: #selector(infoBtnTapped(_:)), for: .touchUpInside)
if selectIndex == indexPath.row { cell.isSelected=true }
else { cell.isSelected=false }
return cell
}
I tried the above code and i need to find a common solution for old version as well as version above 15. If there is already an answer exists, please redirect me to it.
You are doing a lot of extra work.
A UICollectionView tracks its own selections with .indexPathsForSelectedItems, so there is no need for the additional tracking with your selectedIndexPath and selectIndexSec.
Also, if you're overriding isSelected, there's no need to call .reloadData().
Here's a complete example -- I added the lblName and cellBGView but not the button:
class AutoHighlightCell: UICollectionViewCell {
let lblName = UILabel()
let cellBGView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
lblName.textAlignment = .center
[cellBGView, lblName].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
}
cellBGView.addSubview(lblName)
contentView.addSubview(cellBGView)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
cellBGView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
cellBGView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
cellBGView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
cellBGView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
lblName.topAnchor.constraint(equalTo: cellBGView.topAnchor, constant: 16.0),
lblName.leadingAnchor.constraint(equalTo: cellBGView.leadingAnchor, constant: 24.0),
lblName.trailingAnchor.constraint(equalTo: cellBGView.trailingAnchor, constant: -24.0),
lblName.bottomAnchor.constraint(equalTo: cellBGView.bottomAnchor, constant: -16.0),
])
contentView.layer.borderWidth = 1.0
contentView.layer.borderColor = UIColor.black.cgColor
// set default non-selected properties
lblName.textColor = .blue
cellBGView.backgroundColor = .yellow
}
override var isSelected: Bool {
didSet {
lblName.textColor = isSelected ? .white : .blue
cellBGView.backgroundColor = isSelected ? .systemGreen : .yellow
}
}
}
class AutoHighlightCollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var collectionView: UICollectionView!
let instructionLabel: UILabel = {
let v = UILabel()
v.textAlignment = .center
v.text = "Tap Here"
v.numberOfLines = 0
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
let fl = UICollectionViewFlowLayout()
fl.estimatedItemSize = CGSize(width: 80, height: 50)
fl.scrollDirection = .horizontal
collectionView = UICollectionView(frame: .zero, collectionViewLayout: fl)
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
instructionLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(instructionLabel)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 80.0),
collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
collectionView.heightAnchor.constraint(equalToConstant: 80.0),
instructionLabel.topAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 60.0),
instructionLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
instructionLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
])
collectionView.register(AutoHighlightCell.self, forCellWithReuseIdentifier: "cell")
collectionView.dataSource = self
collectionView.delegate = self
// so we can see the collectionView frame
collectionView.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
let t = UITapGestureRecognizer(target: self, action: #selector(gotTap(_:)))
instructionLabel.addGestureRecognizer(t)
instructionLabel.isUserInteractionEnabled = true
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let c = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! AutoHighlightCell
c.lblName.text = "\(indexPath)"
return c
}
#objc func gotTap(_ g: UITapGestureRecognizer) {
var s = "Tap Here\n\n"
if let pth = collectionView.indexPathsForSelectedItems?.first {
s += "Selected Path: \(pth)"
} else {
s += "No Item Selected"
}
instructionLabel.text = s
}
}
When run, it will look like this:
If you tap "Tap Here" before selecting a cell, you'll see:
after selecting a cell:
Notice that when you scroll the cells in and out of view, the "Selected" state is maintained by the collection view, and the cell's UI is updated in override var isSelected ... no need to worry about any of that in cellForItemAt or in didSelectItemAt
I'm trying to achieve a chat system using UICollectionView -The collectionview is turned upside down and so are the cells using CGAffineTransform(scaleX: -1, y: 1)- and so far everything is going well, I'm calculating cell sizes manually using NSString.boundingRect and it's working as expected, then I enabled the collectionview's interactive keyboard dismissal since then whenever I drag the keyboard a little bit then I let go it results into a really weird animation which I can't seem to figure out what's triggering it.
I also use Typist which is a small utility class that facilitates keyboard handling https://github.com/totocaster/Typist, I'm not really suspecting it since it is a simple wrapper UIKeyboard notifications.
I've tried several stuff including but not limited to
1-Disabling animations when showing cell using
UIView.setAnimationsEnabled(false) then enabling it again just before returning the cell which did not work.
2-Removing the upside-down approach altogether, and again nope even when it's in a normal transform the bug still occurs
3-Rewrote the entire UICollectionView implementation to UITableView implementation which -surprisingly- yielded a better result the weird animation isn't as strong as it's on the UICollectionView but it's still happening to some extent
4-Removing Typist altogether and reverting back to NotificationCenter and manual handling and nope, does not work.
UICollectionView initialization
lazy var chatCollectionView: UICollectionView = {
let collectionViewFlowLayout = UICollectionViewFlowLayout()
collectionViewFlowLayout.scrollDirection = .vertical
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewFlowLayout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.backgroundColor = .white
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
collectionView.register(OutgoingTextCell.self, forCellWithReuseIdentifier: OutgoingTextCell.className)
collectionView.register(IncomingTextCell.self, forCellWithReuseIdentifier: IncomingTextCell.className)
collectionView.register(OutgoingImageCell.self, forCellWithReuseIdentifier: OutgoingImageCell.className)
collectionView.register(IncomingImageCell.self, forCellWithReuseIdentifier: IncomingImageCell.className)
collectionView.transform = CGAffineTransform(scaleX: 1, y: -1)
collectionView.semanticContentAttribute = .forceLeftToRight
collectionView.keyboardDismissMode = .interactive
return collectionView
}()
UICollectionView DataSource / Delete implementation
extension ChatViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return presenter.numberOfRows
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if presenter.isSender(at: indexPath) {
switch presenter.messageType(at: indexPath) {
case .text:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: OutgoingTextCell.className, for: indexPath) as! OutgoingTextCell
presenter.configure(cell: AnyConfigurableCell(cell), at: indexPath)
return cell
case .photo:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: OutgoingImageCell.className, for: indexPath) as! OutgoingImageCell
presenter.configure(cell: AnyConfigurableCell(cell), at: indexPath)
return cell
}
} else {
switch presenter.messageType(at: indexPath) {
case .text:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: IncomingTextCell.className, for: indexPath) as! IncomingTextCell
presenter.configure(cell: AnyConfigurableCell(cell), at: indexPath)
return cell
case .photo:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: IncomingImageCell.className, for: indexPath) as! IncomingImageCell
presenter.configure(cell: AnyConfigurableCell(cell), at: indexPath)
return cell
}
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if presenter.messageType(at: indexPath) == .photo {
return CGSize(width: view.frame.width, height: 250)
} else {
if let height = cachedSizes[presenter.uuidForMessage(at: indexPath)] {
return CGSize(width: view.frame.width, height: height)
} else {
let height = calculateTextHeight(at: indexPath)
cachedSizes[presenter.uuidForMessage(at: indexPath)] = height
return CGSize(width: view.frame.width, height: height)
}
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.row == presenter.numberOfRows - 1 && !isPaginating {
presenter.loadNextPage()
}
}
private func calculateTextHeight(at indexPath: IndexPath) -> CGFloat {
let approximateSize = CGSize(width: view.frame.width - 88, height: .greatestFiniteMagnitude)
let estimatedHeight = NSString(string: presenter.contentForMessage(at: indexPath)).boundingRect(with: approximateSize, options: .usesLineFragmentOrigin, attributes: [.font: DinNextFont.regular.getFont(ofSize: 14)], context: nil).height
return estimatedHeight + 40
}
}
Cell implementation
class OutgoingTextCell: UICollectionViewCell, ConfigurableCell {
lazy var containerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .coral
view.layer.cornerRadius = 10
return view
}()
lazy var contentLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = .white
label.numberOfLines = 0
label.font = DinNextFont.regular.getFont(ofSize: 14)
return label
}()
private lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [timeLabel, checkMarkImageView])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.alignment = .center
stackView.spacing = 4
return stackView
}()
private lazy var checkMarkImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "ic_check")?.withRenderingMode(.alwaysTemplate))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.tintColor = .white
imageView.heightAnchor.constraint(equalToConstant: 8).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 10).isActive = true
return imageView
}()
lazy var timeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = .white
label.font = DinNextFont.regular.getFont(ofSize: 10)
label.text = "12:14"
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func addSubviews() {
addSubview(containerView)
containerView.addSubview(contentLabel)
containerView.addSubview(stackView)
}
func setupContainerViewConstraints() {
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: topAnchor, constant: 0),
containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 100),
containerView.heightAnchor.constraint(greaterThanOrEqualToConstant: 34),
containerView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: 64)
])
}
private func setupContentLabelConstraints() {
NSLayoutConstraint.activate([
contentLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 4),
contentLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8),
contentLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8),
])
}
private func setupStackViewConstraints() {
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: contentLabel.bottomAnchor, constant: 0),
stackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -6),
stackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -4),
stackView.leadingAnchor.constraint(greaterThanOrEqualTo: containerView.leadingAnchor, constant: 6),
stackView.heightAnchor.constraint(equalToConstant: 20),
])
}
private func layoutUI() {
addSubviews()
setupContainerViewConstraints()
setupContentLabelConstraints()
setupStackViewConstraints()
}
func configure(model: MessageViewModel) {
containerView.transform = CGAffineTransform(scaleX: 1, y: -1)
contentLabel.text = model.content
timeLabel.text = model.time
checkMarkImageView.isHidden = !model.isSent
}
}
The output of UICollectionView is as the following
https://www.youtube.com/watch?v=RajfZk5lGCQ
The output of the UITableview is as the following
https://www.youtube.com/watch?v=6ayG7WhYKXo
It might not be really clear but if you look closely you can see the cells are "briefly" animating and with longer messages it looks kind of "sliding animation" which does not leak appealing at all.
How do I remove a uiview that is added into a viewcontroller whenever the user clicks on one of the collectionview? My collectionview is located inside of its own viewcontroller when the user clicks one of the collectionviewcell it will dismiss the controller (it's working) and then it will remove the contextTxt within the mainviewcontroller It did dismiss the view but it failed to remove the contextTxt and replacing it with a new view. In other words, the collectionviewcell did not trigger the #objc func inside the mainviewcontroller. Why won't it trigger?? and how do resolve this problem? Here is the code
class mainViewController: UIViewController {
let navbar:navbarView = {
let content = navbarView()
return content
}()
let contentTxt:UITextView = {
let content = UITextView()
content.backgroundColor = UIColor.green
return content
}()
let bottomBtn:UIButton = {
let content = UIButton()
content.backgroundColor = UIColor.red
return content
}()
override func viewDidLoad() {
super.viewDidLoad()
navbar.translatesAutoresizingMaskIntoConstraints = false
contentTxt.translatesAutoresizingMaskIntoConstraints = false
bottomBtn.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(navbar)
view.addSubview(contentTxt)
contentTxt.addSubview(bottomBtn)
navbar.topAnchor.constraint(
equalTo: view.topAnchor, constant: 20).isActive = true
navbar.centerXAnchor.constraint(
equalTo: view.centerXAnchor).isActive = true
navbar.widthAnchor.constraint(
equalTo: view.widthAnchor).isActive = true
navbar.heightAnchor.constraint(
equalToConstant: 50).isActive = true
contentTxt.topAnchor.constraint(
equalTo: navbar.bottomAnchor, constant: 5).isActive = true
contentTxt.centerXAnchor.constraint(
equalTo: view.centerXAnchor).isActive = true
contentTxt.widthAnchor.constraint(
equalTo: view.widthAnchor).isActive = true
contentTxt.bottomAnchor.constraint(
equalTo: view.bottomAnchor, constant: 0).isActive = true
bottomBtn.bottomAnchor.constraint(
equalTo: contentTxt.bottomAnchor).isActive = true
bottomBtn.centerXAnchor.constraint(
equalTo: contentTxt.centerXAnchor).isActive = true
bottomBtn.widthAnchor.constraint(
equalToConstant: 40).isActive = true
bottomBtn.heightAnchor.constraint(
equalToConstant: 40).isActive = true
}
#objc func addConnections(){
self.contentTxt.removeFromSuperview()
let connect:connectView = {
let content = connectView()
content.backgroundColor = UIColor.red
return content
}()
connect.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(connect)
connect.topAnchor.constraint(
equalTo: navbar.bottomAnchor, constant: 5).isActive = true
connect.centerXAnchor.constraint(
equalTo: view.centerXAnchor).isActive = true
connect.widthAnchor.constraint(
equalTo: view.widthAnchor).isActive = true
connect.bottomAnchor.constraint(
equalTo: view.bottomAnchor, constant: 0).isActive = true
}
#objc func sideController(){
let next = self.storyboard?.instantiateViewController(
withIdentifier: "sideViewController") as! sideViewController
self.present(next, animated: true, completion: nil)
}
#objc func profileController(){
let next = self.storyboard?.instantiateViewController(
withIdentifier: "profileViewController") as! profileViewController
self.present(next, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is the collectionview code:
class sideCollectionView:UIView, UICollectionViewDelegateFlowLayout,UICollectionViewDataSource {
var currentVc:sideViewController?
var mainVc=mainViewController()
let arrayLbl = ["connection","achievement","template","setting"]
let arrayImg = ["connection","achievement","template","setting"]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayLbl.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! sideCollectionViewCell
cell.titleImg.image = UIImage(named: "\(arrayImg[indexPath.row])")
cell.titleLbl.text = arrayLbl[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (self.frame.width / 2) - 40, height: (self.frame.width / 2) - 40)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(25, 25, 10, 25)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0{
print("connection")
currentVc?.bye()
mainVc.addConnections()
}
if indexPath.row == 1{
print("achievement")
currentVc?.bye()
}
if indexPath.row == 2{
print("template")
currentVc?.bye()
}
if indexPath.row == 3{
print("setting")
currentVc?.bye()
}
}
lazy var collectionViews: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.clear
cv.dataSource = self
cv.delegate = self
return cv
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews(){
collectionViews.register(sideCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
collectionViews.translatesAutoresizingMaskIntoConstraints = false
backgroundColor = UIColor.clear
addSubview(collectionViews)
collectionViews.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
collectionViews.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
collectionViews.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0).isActive = true
collectionViews.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0).isActive = true
collectionViews.widthAnchor.constraint(equalTo: widthAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Because var mainVc=mainViewController() creates a new mainViewController which is not the existing mainViewController.
You can try getting the parent mainViewController by this way.
var mainVc: mainViewController? {
return currentVc?.presentingViewController as? mainViewController
}
When presenting sideViewController, set the mainVc variable to self so that it is pointing to the actual mainVc object:
#objc func sideController(){
let next = self.storyboard?.instantiateViewController(withIdentifier: "sideViewController") as! sideViewController
next.mainVc = self
self.present(next, animated: true, completion: nil)
}
This is a popular question but I'm still not able to find a simple answer of how to define a dynamic height for a UICollectionViewCell with a TextView inside. I found this answer UICollectionView - dynamic cell height? but I'm not using interface and I would like a solution for interfaces built in code.
import UIKit
class ResumeController : UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
//layout.estimatedItemSize = CGSize(width: 100, height: 100)
layout.minimumLineSpacing = 0
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .white
cv.dataSource = self
cv.delegate = self
cv.translatesAutoresizingMaskIntoConstraints = false
cv.register(FirmCell.self, forCellWithReuseIdentifier: self.cellId)
return cv
}()
override func viewDidLoad() {
view.backgroundColor = .white
setupViews()
}
func setupViews() {
setupCollectionView()
}
func setupCollectionView() {
view.addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
collectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
collectionView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath);
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 100.0)
}
}
class FirmCell : UICollectionViewCell {
let imageView: UIImageView = {
let iv = UIImageView()
iv.backgroundColor = .orange
// iv.image = somelogo
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
lazy var textView: UITextView = {
let tv = UITextView()
tv.isEditable = false
tv.isSelectable = false
tv.isScrollEnabled = false
tv.attributedText = self.attributedText()
tv.translatesAutoresizingMaskIntoConstraints = false
tv.layer.borderWidth = 0.5
tv.layer.borderColor = UIColor.blue.cgColor
return tv
}()
let separatorLine: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0.9, alpha: 1)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
func attributedText() -> NSAttributedString {
let text = NSMutableAttributedString()
// company
var companyAttr = [String: Any]()
companyAttr[NSFontAttributeName] = UIFont.systemFont(ofSize: 15, weight: UIFontWeightBold)
companyAttr[NSForegroundColorAttributeName] = UIColor.black
text.append(NSAttributedString(string: "line1" , attributes: companyAttr))
// period
var periodAttr = [String: Any]()
periodAttr[NSFontAttributeName] = UIFont.systemFont(ofSize: 14)
periodAttr[NSForegroundColorAttributeName] = UIColor.lightGray
text.append(NSAttributedString(string: " line1" , attributes: periodAttr))
// body
var bodyAttr = [String: Any]()
bodyAttr[NSFontAttributeName] = UIFont.systemFont(ofSize: 14, weight: UIFontWeightThin)
bodyAttr[NSForegroundColorAttributeName] = UIColor.black
text.append(NSAttributedString(string: "\na huge amount of text goes here ... trying to figure out a way to have different collectionview cells row height", attributes: bodyAttr))
return text
}
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
setupImageView()
}
func setupImageView() {
addSubview(imageView)
imageView.topAnchor.constraint(equalTo: topAnchor, constant: 9.0).isActive = true
imageView.leftAnchor.constraint(equalTo: leftAnchor, constant: 9.0).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 40.0).isActive = true
setupTextView()
}
func setupTextView() {
addSubview(textView)
textView.topAnchor.constraint(equalTo: imageView.topAnchor, constant: 0).isActive = true
textView.leftAnchor.constraint(equalTo: imageView.rightAnchor, constant: 7.0).isActive = true
textView.rightAnchor.constraint(equalTo: rightAnchor, constant: -9.0).isActive = true
textView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
}
Could someone provide some pointers?
Im currently have a horizontal collection view. I currently have multiple selection enabled set to off. For some reason when I select the first item the item toward the end of the collectionView also become selected. Thank you for the hep.
protocol customDatePickerDelegate:class {
func dateFromCustomDatePicker(date:Date)
}
class datePickerCollectionViewCell:UICollectionViewCell {
var labelNumber:UILabel = UILabel()
var minuteLabel:UILabel = {
let v = UILabel()
v.text = "Minutes"
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .white
minuteLabel.labelFormCustom(labelType: .dateForm)
labelNumber.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(labelNumber)
labelNumber.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0).isActive = true
labelNumber.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: 0).isActive = true
minuteLabel.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(minuteLabel)
minuteLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: 0).isActive = true
minuteLabel.topAnchor.constraint(equalTo: labelNumber.bottomAnchor, constant: 0).isActive = true
self.layer.cornerRadius = 6
self.layer.borderWidth = 0.5
self.layer.borderColor = UIColor(red:205.0/255.0, green:205.0/255.0, blue:205.0/255.0, alpha: 1.0).cgColor
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class NewRquestDatePickerCollectionViewController: UIViewController,UICollectionViewDelegate {
var collectionView:UICollectionView!
override func viewDidLoad() {
setupCollectionView()
}
lazy var centerView:UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.heightAnchor.constraint(equalToConstant: 300).isActive = true
v.backgroundColor = .white
v.addBorderCustom(.bottom)
return v
}()
lazy var durationLabel:UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textAlignment = .center
v.font = v.font.withSize(17)
return v
}()
lazy var topActionBar:UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.heightAnchor.constraint(equalToConstant: 60).isActive = true
v.backgroundColor = UIColor(hex: "F9F9F9")
v.addBorderCustom(.top)
v.addBorderCustom(.bottom)
return v
}()
lazy var doneButton:UIButton! = {
let v = UIButton(type:.system)
v.translatesAutoresizingMaskIntoConstraints = false
v.setTitle("Done", for: .normal)
v.addTarget(self, action: #selector(doneButtonPressed), for: .touchUpInside)
return v
}()
lazy var cancelButton:UIButton = {
let v = UIButton(type:.system)
v.translatesAutoresizingMaskIntoConstraints = false
v.setTitle("Cancel", for: .normal)
v.addTarget(self, action: #selector(dismissCurrentView), for: .touchUpInside)
return v
}()
lazy var dayLabel:UILabel = {
let v = UILabel()
// v.text = "Monday"
return v
}()
lazy var titleView:UILabel = {
let v = UILabel()
v.text = "Select a deadline"
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
var selectedIndexPath: NSIndexPath?
// var selectedTime:Int!
var timer = Timer()
var timeSeleted:TimeInterval = 10.0
let timeArray:[TimeInterval] = [10,20,30,45,60,140,400,900,1200]
var delegate:customDatePickerDelegate!
var dateSelected:Date!
func setupCollectionView(){
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
//collection view setup
self.view.addSubview(centerView)
centerView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0).isActive = true
centerView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0).isActive = true
centerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
collectionView = UICollectionView(frame: view.frame, collectionViewLayout: layout)
collectionView.register(datePickerCollectionViewCell.self, forCellWithReuseIdentifier: "imageCell")
collectionView.delegate = self
collectionView.dataSource = self
centerView.addSubview(topActionBar)
topActionBar.heightAnchor.constraint(equalToConstant: 49).isActive = true
topActionBar.leftAnchor.constraint(equalTo: self.centerView.leftAnchor, constant: 0).isActive = true
topActionBar.rightAnchor.constraint(equalTo: self.centerView.rightAnchor, constant: 0).isActive = true
topActionBar.topAnchor.constraint(equalTo: self.centerView.topAnchor, constant: 0).isActive = true
topActionBar.addSubview(cancelButton)
topActionBar.addSubview(titleView)
cancelButton.leftAnchor.constraint(equalTo: topActionBar.leftAnchor, constant: 16).isActive = true
cancelButton.centerYAnchor.constraint(equalTo: topActionBar.centerYAnchor, constant: 0).isActive = true
titleView.centerXAnchor.constraint(equalTo: topActionBar.centerXAnchor, constant: 0).isActive = true
titleView.centerYAnchor.constraint(equalTo: topActionBar.centerYAnchor, constant: 0).isActive = true
centerView.addSubview(doneButton)
centerView.addBorderCustom(.bottom)
doneButton.centerXAnchor.constraint(equalTo: centerView.centerXAnchor, constant: 0).isActive = true
doneButton.bottomAnchor.constraint(equalTo: centerView.bottomAnchor, constant: -20).isActive = true
centerView.addBorderCustom(.bottom)
centerView.addSubview(collectionView)
collectionView.heightAnchor.constraint(equalToConstant: 100).isActive = true
collectionView.leftAnchor.constraint(equalTo: self.centerView.leftAnchor, constant: 0).isActive = true
collectionView.rightAnchor.constraint(equalTo: self.centerView.rightAnchor, constant: 0).isActive = true
collectionView.topAnchor.constraint(equalTo: self.topActionBar.bottomAnchor, constant: 0).isActive = true
dayLabel.translatesAutoresizingMaskIntoConstraints = false
centerView.addSubview(dayLabel)
dayLabel.centerXAnchor.constraint(equalTo: collectionView.centerXAnchor, constant: 0).isActive = true
dayLabel.topAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 20).isActive = true
centerView.addSubview(durationLabel)
durationLabel.leftAnchor.constraint(equalTo: self.centerView.leftAnchor, constant: 0).isActive = true
durationLabel.rightAnchor.constraint(equalTo: self.centerView.rightAnchor, constant: 0).isActive = true
durationLabel.topAnchor.constraint(equalTo: self.collectionView.bottomAnchor, constant: 0).isActive = true
durationLabel.bottomAnchor.constraint(equalTo: self.doneButton.topAnchor, constant: 20).isActive = true
timerStart()
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
collectionView.contentInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
collectionView.isPagingEnabled = true
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.backgroundColor = UIColor(hex:"EFF3F4")
centerView.addBorderCustom(.bottom)
collectionView.allowsMultipleSelection = false
}
}
extension NewRquestDatePickerCollectionViewController {
func dismissCurrentView(){
self.dismiss(animated: true , completion: nil)
}
func doneButtonPressed(){
delegate.dateFromCustomDatePicker(date: dateSelected)
dismissCurrentView()
}
func tappedDeadlineSelection(){
}
func timerStart(){
if #available(iOS 10.0, *) {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { (timer) in
let date = Date()
let adjustedDate = date.addingTimeInterval(self.timeSeleted * 60.0)
self.dateSelected = adjustedDate
self.durationLabel.text = String(describing: adjustedDate.string(withFormat: "MM/dd/yy, h:mm a"))
self.dayLabel.text = String(describing: adjustedDate.string(withFormat: "EEEE"))
})
} else {
// Fallback on earlier versions
}
}
}
extension NewRquestDatePickerCollectionViewController:UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return timeArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "imageCell", for: indexPath) as! datePickerCollectionViewCell
cell.awakeFromNib()
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let cellInfo = cell as! datePickerCollectionViewCell
//converTime
cellInfo.labelNumber.text = String(self.timeArray[indexPath.row])
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! datePickerCollectionViewCell
let timeAdded = self.timeArray[indexPath.row]
cell.backgroundColor = UIColor(hex: "F2784B")
// let timeAdded = self.timeArray[indexPath.row]
cell.labelNumber.textColor = .white
cell.minuteLabel.textColor = .white
cell.layer.borderColor = UIColor.white.cgColor
timeSeleted = timeAdded
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally)
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! datePickerCollectionViewCell
cell.backgroundColor = .white
cell.labelNumber.textColor = .black
cell.minuteLabel.textColor = UIColor().RquestCustomColors(.fontGray)
// collectionView.performBatchUpdates(nil, completion: nil)
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 64, height: 64)
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 30.0
}
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 30.0
}
}
Because you are reusing existed cell, you should keep track selected item index and update cell state as below:
var selectedIndex: IndexPath?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! datePickerCollectionViewCell
let timeAdded = self.timeArray[indexPath.row]
cell.backgroundColor = UIColor(hex: "F2784B")
// let timeAdded = self.timeArray[indexPath.row]
cell.labelNumber.textColor = .white
cell.minuteLabel.textColor = .white
cell.layer.borderColor = UIColor.white.cgColor
timeSeleted = timeAdded
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally)
// add the following codes
let lastSelectedIndex = self.selectedIndex
self.selectedIndex = indexPath
// unselect last selected index
collectionView.reloadItems(at: [lastSelectedIndex!])
// reload new selected index
collectionView.reloadItems(at: [self.selectedIndex!])
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let cellInfo = cell as! datePickerCollectionViewCell
//converTime
cellInfo.labelNumber.text = String(self.timeArray[indexPath.row])
// add this line of code
cell.isSelected = (self.selectedIndex == indexPath)
}
You should make use of the "isSelected" method for the collection view cell. This way you will not have not save any selected indexpath.
So I recommend using this method:
Firstly,
collectionView.allowsMultipleSelection = false
on the UICollectionViewCell you can override the isSelected method
didSet {
if isSelected {
// Change UI for selected state
radioButton.setImage(#imageLiteral(resourceName: "greenTick"), for: .normal)
} else {
// Chage UI for unselected state
radioButton.setImage(#imageLiteral(resourceName: "radioInactive"), for: .normal)
}
}
Finally when you need to find out the indexpath for the selected item.
guard let selectedIndex = self.collectionView.indexPathsForSelectedItems?.first else { return }