UICollectionview in a keyboard extension - swift

I have tried to get a UICollectionview into my keyboard extension, which i made in swift. But i canĀ“t get it to work. The keyboard just crashes on launch. Anyone know how to fix this? Or is it even possible to use a UICollectionview in a keyboard extension.
class KeyboardViewController: UIInputViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource{
#IBOutlet var nextKeyboardButton: UIButton!
#IBOutlet var testButton: UIButton!
var collectionView: UICollectionView!
override func updateViewConstraints() {
super.updateViewConstraints()
// Add custom view sizing constraints here
}
override func viewDidLoad() {
super.viewDidLoad()
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 10, height: 10)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(collectionView)
// Perform custom UI setup here
self.nextKeyboardButton = UIButton(type: .System)
self.nextKeyboardButton.setTitle(NSLocalizedString("Next Keyboard", comment: "Title for 'Next Keyboard' button"), forState: .Normal)
self.nextKeyboardButton.sizeToFit()
self.nextKeyboardButton.translatesAutoresizingMaskIntoConstraints = false
self.nextKeyboardButton.addTarget(self, action: "advanceToNextInputMode", forControlEvents: .TouchUpInside)
self.view.addSubview(self.nextKeyboardButton)
let nextKeyboardButtonLeftSideConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1.0, constant: 0.0)
let nextKeyboardButtonBottomConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
self.view.addConstraints([nextKeyboardButtonLeftSideConstraint, nextKeyboardButtonBottomConstraint])
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated
}
override func textWillChange(textInput: UITextInput?) {
// The app is about to change the document's contents. Perform any preparation here.
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 14
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
cell.backgroundColor = UIColor.orangeColor()
return cell
}
override func textDidChange(textInput: UITextInput?) {
// The app has just changed the document's contents, the document context has been updated.
var textColor: UIColor
let proxy = self.textDocumentProxy
if proxy.keyboardAppearance == UIKeyboardAppearance.Dark {
textColor = UIColor.whiteColor()
} else {
textColor = UIColor.blackColor()
}
self.nextKeyboardButton.setTitleColor(textColor, forState: .Normal)
}
}

Just add these line in viewDidAppear not in viewDidLoad
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 10, height: 10)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
collectionView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(collectionView)

Related

How to make a dynamic change in the height of the ScrollView on which the TableView is added? Swift

Friends, I need help, I spent 2 days on solving this issue. I need to make my ScrollView dynamically change its height based on the amount of text I add to the tableView. The problem is that if I don't strictly set the scroll.contentSize.height height, then the ability to scroll the screen is disabled.
private let scrollView: UIScrollView = {
let scroll = UIScrollView()
scroll.contentSize.height = 4000
scroll.backgroundColor = UIColor.white
scroll.translatesAutoresizingMaskIntoConstraints = false
return scroll
}()
func setConstraints() {
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
NSLayoutConstraint.activate([
headLabel.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
headLabel.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 45)
])
NSLayoutConstraint.activate([
ellipseBabyImage.topAnchor.constraint(equalTo: stackViewName.bottomAnchor, constant: 75),
ellipseBabyImage.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
ellipseBabyImage.heightAnchor.constraint(equalToConstant: 131.3),
ellipseBabyImage.widthAnchor.constraint(equalToConstant: 145)
])
NSLayoutConstraint.activate([
goButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
goButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -40),
goButton.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 20),
goButton.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -20),
goButton.heightAnchor.constraint(equalToConstant: 48)
])
NSLayoutConstraint.activate([
characterLabel.topAnchor.constraint(equalTo: ellipseBabyImage.bottomAnchor, constant: 68.35),
characterLabel.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor)
])
NSLayoutConstraint.activate([
stackViewName.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
stackViewName.topAnchor.constraint(equalTo: headLabel.topAnchor,constant: 44),
])
NSLayoutConstraint.activate([
leftBlockStack.topAnchor.constraint(equalTo: fullNameLabel.topAnchor, constant: 60),
leftBlockStack.leftAnchor.constraint(equalTo: view.leftAnchor,constant: 28),
leftBlockStack.rightAnchor.constraint(equalTo: ellipseBabyImage.leftAnchor,constant: -10)
])
NSLayoutConstraint.activate([
rightBlockStack.topAnchor.constraint(equalTo: fullNameLabel.topAnchor, constant: 60),
rightBlockStack.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -28),
rightBlockStack.leftAnchor.constraint(equalTo: ellipseBabyImage.rightAnchor,constant: 10)
])
NSLayoutConstraint.activate([
arrayStackCharacteristicOne.topAnchor.constraint(equalTo: characterLabel.bottomAnchor,constant: 16),
arrayStackCharacteristicOne.centerXAnchor.constraint(equalTo: view.centerXAnchor),
arrayStackCharacteristicOne.leftAnchor.constraint(equalTo: scrollView.leftAnchor,constant: 22),
healthProgressView.heightAnchor.constraint(equalToConstant: 5),
healthProgressView.widthAnchor.constraint(equalToConstant: 90),
willpowerProgressView.heightAnchor.constraint(equalToConstant: 5),
willpowerProgressView.widthAnchor.constraint(equalToConstant: 90),
luckProgressView.heightAnchor.constraint(equalToConstant: 5),
luckProgressView.widthAnchor.constraint(equalToConstant: 90),
])
NSLayoutConstraint.activate([
arrayStackCharacteristicTwo.topAnchor.constraint(equalTo: arrayStackCharacteristicOne.bottomAnchor,constant: 16),
arrayStackCharacteristicTwo.centerXAnchor.constraint(equalTo: view.centerXAnchor),
arrayStackCharacteristicTwo.leftAnchor.constraint(equalTo: scrollView.leftAnchor,constant: 22),
talentProgressView.heightAnchor.constraint(equalToConstant: 5),
talentProgressView.widthAnchor.constraint(equalToConstant: 90),
successProgressView.heightAnchor.constraint(equalToConstant: 5),
successProgressView.widthAnchor.constraint(equalToConstant: 90),
creationProgressView.heightAnchor.constraint(equalToConstant: 5),
creationProgressView.widthAnchor.constraint(equalToConstant: 90),
])
NSLayoutConstraint.activate([
descriptionView.topAnchor.constraint(equalTo: arrayStackCharacteristicTwo.topAnchor, constant: 50),
descriptionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
descriptionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
descriptionView.bottomAnchor.constraint(equalTo: view.bottomAnchor,constant: 0),
])
class DescriptionView: UIView {
private let characteristicModel = CharacteristicModel()
private let descriptionIdentifier = "CELL"
private let tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.backgroundColor = .none
tableView.bounces = false
tableView.separatorStyle = .none
tableView.showsVerticalScrollIndicator = false
tableView.isScrollEnabled = false
tableView.translatesAutoresizingMaskIntoConstraints = false
return tableView
}()
override init(frame: CGRect) {
super.init(frame: frame)
setDelegates()
setupViews()
setupConstraints()
tableView.sizeToFit()
tableView.register(DescriptionTableViewCell.self, forCellReuseIdentifier: "CELL")
}
required init?(coder: NSCoder) {
fatalError()
}
private func setDelegates() {
tableView.delegate = self
tableView.dataSource = self
}
private func setupViews() {
addSubview(tableView)
translatesAutoresizingMaskIntoConstraints = false
}
}
//MARK: - UI TableViewDataSource
extension DescriptionView: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
characteristicModel.title.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CELL",for: indexPath) as! DescriptionTableViewCell
let descriptionTitle = characteristicModel.title[indexPath.row]
let descriptionText = characteristicModel.description[indexPath.row]
var content = cell.defaultContentConfiguration()
content.textProperties.color = UIColor(red: 0.729, green: 0.443, blue: 0.443, alpha: 1)
content.textProperties.alignment = .center
content.textProperties.font = UIFont(name: "Lato-SemiBold", size: 22) ?? .systemFont(ofSize: 22)
content.text = descriptionTitle
content.secondaryText = descriptionText
content.secondaryTextProperties.font = UIFont(name: "Lato-SemiBold", size: 16) ?? .systemFont(ofSize: 16)
content.secondaryTextProperties.alignment = .justified
cell.contentConfiguration = content
return cell
}
}
extension DescriptionView {
private func setupConstraints() {
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: topAnchor,constant: 5),
tableView.leadingAnchor.constraint(equalTo: leadingAnchor,constant: 5),
tableView.trailingAnchor.constraint(equalTo: trailingAnchor,constant: -5),
tableView.bottomAnchor.constraint(equalTo: bottomAnchor,constant: -5)
])
}
}
I tried to use different approaches that I found on the Internet, but unfortunately I could not solve it. Either scroll is disabled for me, or the scroll width does not change automatically.
The screenshot shows that I use a tableView for the text that will be automatically added from the API, everything else is uiScrollView. tableView should have scroll disabled and scrollView should scroll to the end of the text
The application should look like this.
Set your scrollview and tableview height to 0 and try adding this to your viewcontroller. you can update the height of the scrollview through viewDidLayoutSubviews.
override func viewDidLayoutSubviews() {
DispatchQueue.main.async {
self.scrollView.contentSize.height = self.descriptionView.tableView.contentSize.height + otherViews.frame.height
}
}
you can also add an observer to the tableview whenever it updates its contentsize
override func viewDidLoad() {
descriptionView.tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let obj = object as? UITableView {
if obj == descriptionView.tableView && keyPath == "contentSize" {
if let newSize = change?[.newKey] as? CGSize {
descriptionView.tableViewHeightConstraint.constant = newSize.height
view.layoutIfNeeded()
}
}
}
}

Separator line appear early than cell title and image goes beyond of UIImageView

I programmatically created UITableView and Cell. All work good, but I have a couple questions about constraints and auto layout:
Constraints with layoutMarginsGuide:
In screens where width 375, lower separator and cell starts from one point. On big size screens, separator appears earlier than the cell title(you can see this in attached screenshot). How I can fix this?
The image of the red rectangle with round corners in the attached screenshot goes beyond of UIImageView. I added constraints to UIImageView, but this not help me. How I can fix this?
Constraints without layoutMarginsGuide:
I want that stackView to cover the whole cell. How can I add constraints without using layoutMarginsGuide and avoid console warnings?
How can I add height constraints? I want that my stackView or title was 100 pt.
Screenshot, cell icon, and code you can found below.
Thanks for your answer.
Rectangle with rounded corners
Screenshot:
Code:
import Foundation
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// MARK: - Lifecycle Methods
override func viewDidLoad() {
super.viewDidLoad()
let tableViewstyle = UITableView.Style.insetGrouped
let tableView = UITableView(frame: CGRect(), style: tableViewstyle)
tableView.delegate = self
tableView.dataSource = self
tableView.register(Cell1.self, forCellReuseIdentifier: String(Cell1.description()))
tableView.tableFooterView = UIView()
tableView.translatesAutoresizingMaskIntoConstraints = false
self.view.backgroundColor = .systemBackground
self.view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.widthAnchor.constraint(equalTo: self.view.widthAnchor),
tableView.heightAnchor.constraint(equalTo: self.view.heightAnchor)
])
}
// MARK: - numberOfSections
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// MARK: - numberOfRowsInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 7
}
// MARK: - cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: String(Cell1.description())) as! Cell1
cell.title.text = "Cell \(indexPath.row)"
cell.icon2.image = UIImage(named: "Icon")
return cell
}
}
// MARK: - Cell
class Cell1: UITableViewCell {
// MARK: - Properties
let title = UILabel()
let icon2 = UIImageView()
let stackView = UIStackView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// Off translatesAutoresizingMaskIntoConstraints
title.translatesAutoresizingMaskIntoConstraints = false
icon2.translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
// Set constraints
icon2.addConstraint(NSLayoutConstraint(item: icon2, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30))
icon2.addConstraint(NSLayoutConstraint(item: icon2, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30))
// StackView
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fill
// TitleAndDetailStackView
stackView.axis = .horizontal
stackView.alignment = .center
stackView.distribution = .fill
// Hugging
title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
// Resistance
title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
icon2.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
title.textAlignment = .left
title.numberOfLines = 0
// Highlight stackView and set colors
stackView.addBackground(color: .green)
title.textColor = .label
icon2.backgroundColor = .cyan
// Add title and detail
stackView.addArrangedSubview(title)
stackView.addArrangedSubview(icon2)
icon2.contentMode = .center
// Add to subview
self.contentView.addSubview(stackView)
// Get layoutMarginsGuide
let layoutMarginsGuide = contentView.layoutMarginsGuide
// Set constraints
NSLayoutConstraint.activate([
// Constrain all 4 sides of the stack view to the content view's layoutMarginsGuide
stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 0.0),
stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor, constant: 0.0),
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
// MARK: - AddBackground
extension UIStackView {
func addBackground(color: UIColor) {
let subView = UIView(frame: bounds)
subView.backgroundColor = color
subView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
insertSubview(subView, at: 0)
}
}
To achieve this we need to change constraints to contentView. and set leadingAnchor.constraint constant equal to 16.0.
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0.0),
])
Change icon2.contentMode = .center to icon2.contentMode = .scaleAspectFit
StackView that cover the whole cell:
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0.0),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0.0),
])
To change cell height we can simply add height constraint to any object inside the cell:
title.addConstraint(NSLayoutConstraint(item: title, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 100))

How do I simultaneously update the border of a UICollectionViewCell and perform a batch update of UICollectionView Cells

My goal is to add a shadow around the border of a UICollectionView Cell while simultaneously performing a batch update of the UICollectionView, which will then cause the selected cell to expand in height. The idea is that the shadow around the border will fade into view, or come into view immediately as it does in this GIF, but do so following the actual size of the cell as it grows. As shown in the code, I specifically want the shadow to be around the cell and not within the cell itself - this is to maintain the proper boundaries.
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if weekTasks[indexPath.section].count == 0 {
let emptyCell = collectionView.dequeueReusableCell(withReuseIdentifier: "emptyCellID", for: indexPath) as! EmptyCell
return emptyCell
} else {
let assignmentCell = collectionView.dequeueReusableCell(withReuseIdentifier: "assignCellID", for: indexPath) as! AssignmentCell
assignmentCell.myAssignment = weekTasks[indexPath.section][indexPath.item]
assignmentCell.contentView.backgroundColor = UIColor.white
return assignmentCell
}
}
// Sets up size of cell
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if weekTasks[indexPath.section].count == 0 {
return CGSize(width: screenWidth, height: 36 )
} else if (collectionView.indexPathsForSelectedItems?.contains(indexPath))! {
return CGSize(width: screenWidth, height: 110)
} else {
return CGSize(width: screenWidth, height: 46)
}
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.contentView.layer.cornerRadius = 12
cell?.contentView.layer.masksToBounds = true;
cell?.contentView.backgroundColor = UIColor.white
cell?.layer.shadowOffset = CGSize(width: 0 ,height: 1)
cell?.layer.shadowRadius = 4
cell?.layer.shadowOpacity = 0.2
collectionView.performBatchUpdates(nil, completion: { done in
if done {
print("Sweet")
}}
)
}
override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
cell?.contentView.layer.cornerRadius = 0
cell?.contentView.layer.masksToBounds = true;
cell?.layer.shadowOpacity = 0
}
My CollectionView Cell
class AssignmentCell: UICollectionViewCell, UIGestureRecognizerDelegate {
var heightConstraint: NSLayoutConstraint?
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
let helperView = UIView()
func setupViews() {
self.contentView.addSubview(helperView)
let topConstraint = NSLayoutConstraint(item: helperView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0)
let leftConstraint = NSLayoutConstraint(item: helperView, attribute: NSLayoutAttribute.left, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.left, multiplier: 1, constant: 0)
let rightConstraint = NSLayoutConstraint(item: helperView, attribute: NSLayoutAttribute.right, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.right, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: helperView, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: 0)
let heightConstraint = helperView.heightAnchor.constraint(equalToConstant: self.frame.height)
helperView.addConstraints([topConstraint, leftConstraint, rightConstraint, heightConstraint])
helperView.addSubview(titleView)
titleView.frame = CGRect(x: 48, y: (self.frame.height / 2) - 10, width: screenWidth - 190, height: 20)
helperView.addSubview(checkboxView)
checkboxView.frame = CGRect(x: 10, y: (self.frame.height / 2) - 9, width: 18, height: 18)
}
func expandCell(to height: CGFloat) {
heightConstraint?.constant = 110
UIView.animate(withDuration: 0.3, animations: {
self.layoutIfNeeded()
}) { (completed) in
}
}
}
It actually follows the cell size. You get that effect because you are setting a fixed cell height and animating the layout change after. You can try to set cell size automatic (only for the expanded one).
In your cell, create a method to animate the size from current value to x (110 in your case).
Add a constraint to be equal to the height of you container and do smth like this:
func expandCell(to height: CGFloat) {
self.cellHeightConstraint?.constant = height
UIView.animate(withDuration: 0.3, animations: {
self.layoutIfNeeded()
}) { (completed) in
}
}
// edit
Add a new UIView in your ContentView and in that put all your other views. Set top, bottom, left, right of that view to 0, to its superview(contentView) and a height constraint on that view with #999 priority. See below image:

CollectionView delegate error

I have made a collection view programmatically but when set collectionView.delegate = self and collectionView.dataSource = self I get a nil while unwrapping an optional. I don't know what I did wrong here.
class MainFeedViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDelegate, UIViewControllerTransitioningDelegate, UIGestureRecognizerDelegate, MyCollectionCell {
let transition = CircularAnimation()
var collectionView: UICollectionView!
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapEdit(recognizer:)))
func MyCollectionCell() {
let vc = DescriptionViewController()
self.present(vc, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.isHidden = true
collectionView.delegate = self
collectionView.dataSource = self
view.backgroundColor = .white
UIApplication.shared.isStatusBarHidden = false
UIApplication.shared.statusBarStyle = .default
view.addSubview(Settings)
view.addSubview(topBar)
view.addSubview(separatorView)
view.addSubview(separatorView2)
Settings.translatesAutoresizingMaskIntoConstraints = false
Settings.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 15).isActive = true
Settings.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
Settings.widthAnchor.constraint(equalToConstant: 50).isActive = true
Settings.heightAnchor.constraint(equalToConstant: 50).isActive = true
separatorView.translatesAutoresizingMaskIntoConstraints = false
separatorView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40).isActive = true
separatorView.heightAnchor.constraint(equalToConstant: 1).isActive = true
view.addConstraint(NSLayoutConstraint(item: separatorView, attribute: .left, relatedBy: .equal, toItem: Settings, attribute: .right, multiplier: 1, constant: 15))
view.addConstraint(NSLayoutConstraint(item: separatorView, attribute: .right, relatedBy: .equal, toItem: topBar, attribute: .right, multiplier: 1, constant: 0))
separatorView2.translatesAutoresizingMaskIntoConstraints = false
separatorView2.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40).isActive = true
separatorView2.heightAnchor.constraint(equalToConstant: 1).isActive = true
view.addConstraint(NSLayoutConstraint(item: separatorView2, attribute: .right, relatedBy: .equal, toItem: Settings, attribute: .left, multiplier: 1, constant: -15))
view.addConstraint(NSLayoutConstraint(item: separatorView2, attribute: .left, relatedBy: .equal, toItem: topBar, attribute: .left, multiplier: 1, constant: 0))
topBar.translatesAutoresizingMaskIntoConstraints = false
topBar.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
topBar.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
topBar.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
view.addConstraint(NSLayoutConstraint(item: topBar, attribute: .bottom, relatedBy: .equal, toItem: separatorView, attribute: .top, multiplier: 1, constant: 0))
view.insertSubview(topBar, belowSubview: Settings)
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
let height = (view.frame.width - 16 - 16) * 9/16
layout.sectionInset = UIEdgeInsets(top: 80, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: view.frame.width, height: height + 16 + 80)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView?.scrollIndicatorInsets = UIEdgeInsetsMake(80, 0, 0, 0)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(Cell.self, forCellWithReuseIdentifier: "cellId")
collectionView.backgroundColor = UIColor.clear
collectionView.addGestureRecognizer(tapGesture)
tapGesture.delegate = self
self.view.addSubview(collectionView)
view.insertSubview(collectionView, belowSubview: topBar)
}
let Settings : UIButton = {
let btn = UIButton()
btn.setTitle("Clotho", for: .normal)
btn.setTitleColor(.white, for: .normal)
btn.layer.cornerRadius = 25
btn.backgroundColor = UIColor.rgb(displayP3Red: 255, green: 165, blue: 0)
btn.titleLabel?.font = UIFont(name: "Pacifico-Regular", size: 16)
btn.addTarget(self, action:#selector(settingsTab), for: .touchUpInside)
return btn
}()
let topBar: UIView = {
let bar = UIView()
bar.backgroundColor = .white
return bar
}()
let separatorView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.rgb(displayP3Red: 211, green: 211, blue: 211)
return view
}()
let separatorView2: UIView = {
let view2 = UIView()
view2.backgroundColor = UIColor.rgb(displayP3Red: 211, green: 211, blue: 211)
return view2
}()
#objc func tapEdit(recognizer: UITapGestureRecognizer) {
if recognizer.state == UIGestureRecognizerState.ended {
let tapLocation = recognizer.location(in: self.collectionView)
if let tapIndexPath = self.collectionView.indexPathForItem(at: tapLocation) {
if (self.collectionView.cellForItem(at: tapIndexPath) as? Cell) != nil {
//do what you want to cell here
}
}
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! Cell
cell.Delegate = self
return cell
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.transitionMode = .present
transition.startingPoint = Settings.center
transition.circleColor = Settings.backgroundColor!
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.transitionMode = .dismiss
transition.startingPoint = Settings.center
transition.circleColor = Settings.backgroundColor!
return transition
}
#objc func settingsTab(){
let secondVC = SettingsViewController()
secondVC.transitioningDelegate = self
secondVC.modalPresentationStyle = UIModalPresentationStyle.custom
self.present(secondVC, animated: true, completion: nil)
}
}
I set a var Delegate: MyCollectionCell? in my cell with a protocol
import UIKit
protocol MyCollectionCell {
func MyCollectionCell()
}
class Cell: UICollectionViewCell {
var Delegate: MyCollectionCell?
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
let TapGesture = UITapGestureRecognizer(target: self, action: #selector(Cell.tapEdit(sender:)))
addGestureRecognizer(TapGesture)
TapGesture.delegate = MainFeedViewController()
}
//other setup code...
#objc func tapEdit(sender: UITapGestureRecognizer) {
Delegate?.MyCollectionCell()
}
You haven't actually created the collection view anywhere.
This line:
var collectionView: UICollectionView!
creates a variable ready to hold the collection view (and the ! character indicates you should populate the variable in the viewDidLoad method) but you don't actually create the instance of the UICollectionView and assign it to that variable.
So when you try to set the delegate and data source the collection view variable is still nil and hence you get an error.
You need to actually create the instance of a UICollectionView which is also going to involve creating an instance of a UICollectionViewLayout (or a subclass of it like UICollectionViewFlowLayout).
At the most basic level you should do something like this:
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 100, height: 100)
collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 500, height: 500), collectionViewLayout: layout)
although of course you should adjust the frame and the parameters of the layout to suit your usage requirements.

Make UIImageView Clickable and Send To Website

I am making UIImageView instances. I am having trouble making the UIImage clickable. I also would like the UIImage when clicked to send the user to a link on the Internet. How can I accomplish this? I have tried adding tap gestures and such but am having no luck. You can see this with the code that is commented out.
/File 1 Model File/
import Foundation
class Book : NSObject{
var thumbnailImageName: String?
var title : String?
var subTitle : String?
}
/File 2 Cell File/
import UIKit
class BookCell: BaseCell{
var book: Book?{
didSet{
thumbnailImageView.image = UIImage(named: (book?.thumbnailImageName)!)
titleLabel.text = book?.title
subtitleTextView.text = book?.subTitle
}
}
var thumbnailImageView: UIImageView = {
let imageView = UIImageView()
// let tapGesture = UITapGestureRecognizer(target: imageView, action: #selector(BookCell.tapBlurButton(_:)))
imageView.image = UIImage(named: "")
imageView.userInteractionEnabled = true
imageView.tag = 0
imageView.contentMode = .ScaleAspectFit
imageView.clipsToBounds = true
// imageView.addTarget(self, action: #selector(self.tapBlurButton(_:)), forControlEvents: .TouchUpInside)
// imageView.addGestureRecognizer(tapGestureRecognizer)
return imageView
}()
let userProfileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "Gary Vee Profile Pic 1")
imageView.layer.cornerRadius = 22
imageView.layer.masksToBounds = true
return imageView
}()
let separatorView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1)
return view
}()
let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "DailyVee 199"
label.userInteractionEnabled = false
return label
}()
let subtitleTextView: UITextView = {
let textView = UITextView()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.text = "When a street hustler make 130 million"
textView.userInteractionEnabled = false
textView.textContainerInset = UIEdgeInsetsMake(0,-4,0,0)
textView.textColor = UIColor.darkGrayColor()
return textView
}()
let purchaseButton: UIButton = {
let button = UIButton(type: .System) // let preferred over var here
button.frame = CGRectMake(100, 100, 100, 50)
button.backgroundColor = UIColor.greenColor()
button.setTitle("Button", forState: UIControlState.Normal)
button.addTarget(button, action: #selector(Books.tapBlurButton(_:)), forControlEvents: .TouchUpInside)
return button
}()
override func setupViews(){
addSubview(thumbnailImageView)
addSubview(separatorView)
addSubview(userProfileImageView)
addSubview(titleLabel)
addSubview(subtitleTextView)
addSubview(purchaseButton)
addContraintsWithFormat("H:|-16-[v0]-16-|", views: thumbnailImageView)
addContraintsWithFormat("H:|-16-[v0(44)]", views: userProfileImageView)
//Vertical constraints
addContraintsWithFormat("V:|-16-[v0]-8-[v1(44)]-16-[v2(1)]|", views: thumbnailImageView, userProfileImageView, separatorView)
addContraintsWithFormat("H:|[v0]|", views: separatorView)
//top constraint
addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .Top, relatedBy: .Equal, toItem: thumbnailImageView, attribute:.Bottom, multiplier: 1, constant: 8))
//left constraint
addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .Left, relatedBy: .Equal, toItem: userProfileImageView, attribute:.Right, multiplier: 1, constant: 8))
//right constraint
addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .Right, relatedBy: .Equal, toItem: thumbnailImageView, attribute:.Right, multiplier: 1, constant: 0))
//height constraint
addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .Height, relatedBy: .Equal, toItem: self, attribute:.Height, multiplier: 0, constant: 20))
//top constraint
addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .Top, relatedBy: .Equal, toItem: titleLabel, attribute:.Bottom, multiplier: 1, constant: 4))
//left constraint
addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .Left, relatedBy: .Equal, toItem: userProfileImageView, attribute:.Right, multiplier: 1, constant: 8))
//right constraint
addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .Right, relatedBy: .Equal, toItem: thumbnailImageView, attribute:.Right, multiplier: 1, constant: 0))
//height constraint
addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .Height, relatedBy: .Equal, toItem: self, attribute:.Height, multiplier: 0, constant: 30))
}
}
/File 3 Class File/
class Books : UICollectionViewController, UICollectionViewDelegateFlowLayout {
var books: [Book] = {
var askGaryVee = Book()
askGaryVee.thumbnailImageName = "askgaryvee_book"
askGaryVee.title = "#ASKGARYVEE: ONE ENTREPRENEUR'S TAKE ON LEADERSHIP, SOCIAL MEDIA, AND SELF-AWARENESS"
askGaryVee.subTitle = "by Gary Vaynerchuk"
var jabJabJabRightHook = Book()
jabJabJabRightHook.thumbnailImageName = "jab_jab_jab_right_hook_book"
jabJabJabRightHook.title = "JAB, JAB, JAB, RIGHT HOOK: HOW TO TELL YOUR STORY IN A NOISY SOCIAL WORLD"
jabJabJabRightHook.subTitle = "by Gary Vaynerchuk"
var theThankYouEconomy = Book()
theThankYouEconomy.thumbnailImageName = "the_thank_you_economy_book"
theThankYouEconomy.title = "The Thank You Economy"
theThankYouEconomy.subTitle = "by Gary Vaynerchuk"
var crushIt = Book()
crushIt.thumbnailImageName = "cursh_it_book"
crushIt.title = "CRUSH IT! WHY NOW IS THE TIME TO CASH IN ON YOUR PASSION"
crushIt.subTitle = "by Gary Vaynerchuk"
return[askGaryVee, jabJabJabRightHook, theThankYouEconomy, crushIt]
}()
func tapBlurButton(sender: AnyObject) {
print("Please Help!")
}
override func viewDidLoad() {
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
navigationItem.title = "Books"
collectionView!.backgroundColor = UIColor.whiteColor()
collectionView?.registerClass(BookCell.self, forCellWithReuseIdentifier:"cellId")
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return books.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellId", forIndexPath: indexPath) as! BookCell
cell.book = books[indexPath.item]
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let height = (view.frame.width - 16 - 16) * 9 / 16
return CGSizeMake(view.frame.width, height + 16 + 68)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0
}
}
The easiest solution is to add a clear UIButton on top of your UIImageView and set the frame of your UIButton to be the same as your UIImageView. Then you can use the UIButton's IBAction to send the user to the link.
var tumbnailButton: UIButton = {
let button = UIButton(frame: thumbnailImageView.frame)
button.addTarget(self, action: #selector(tapBlurButton(_:)), for: .touchUpInside)
button.clipsToBounds = true
return button
}()
EDIT:
The above code might throw an error since it is a computed property. Try replacing
var tumbnailButton: UIButton = {
with
var tumbnailButton: UIButton {
and remove the parenthesis at the end.
If that doesn't work, try
var tumbnailButton: UIButton {
get{
let button = UIButton(frame: thumbnailImageView.frame)
button.addTarget(self, action: #selector(tapBlurButton(_:)), for: .touchUpInside)
button.clipsToBounds = true
return button
}
}
Make this class which inherits UITapGestureRecognizer
open class BlockTap: UITapGestureRecognizer {
fileprivate var tapAction: ((UITapGestureRecognizer) -> Void)?
public override init(target: Any?, action: Selector?) {
super.init(target: target, action: action)
}
public convenience init (
tapCount: Int = 1,
fingerCount: Int = 1,
action: ((UITapGestureRecognizer) -> Void)?) {
self.init()
self.numberOfTapsRequired = tapCount
#if os(iOS)
self.numberOfTouchesRequired = fingerCount
#endif
self.tapAction = action
self.addTarget(self, action: #selector(BlockTap.didTap(_:)))
}
open func didTap (_ tap: UITapGestureRecognizer) {
tapAction? (tap)
}
}
then make an extension of UIImageView or UIView
extension UIImageView {
public func addTapGesture(tapNumber: Int = 1, action: ((UITapGestureRecognizer) -> ())?) {
let tap = BlockTap(tapCount: tapNumber, fingerCount: 1, action: action)
addGestureRecognizer(tap)
isUserInteractionEnabled = true
}
}
Then You can use this as
imageView?.addTapGesture(action: {[unowned self] (_) in
//Do whatever on click of image
})
In Your code you can use this as
you can use addTapGesture to any imageview in your code like this
let userProfileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "Gary Vee Profile Pic 1")
imageView.layer.cornerRadius = 22
imageView.layer.masksToBounds = true
imageView?.addTapGesture(action: {[unowned self] (_) in
//Code you want to execute on click of Imageview
imageView?.cornerRadius = 4.0
imageView?.clipsToBounds = true
})
return imageView
}()
on click of imageView cornerRadius of image will change to 4.0.