CollectionView delegate error - swift

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.

Related

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

Swift - Custom Text Input View

I'm trying to implement a custom text input view, similar to what is used in most messaging apps like below:
Where the whole view appears at the bottom of the screen initially then above the keyboard when selected, the text box re-sizes based on the content and includes a button to upload text.
I assume I need to create a custom UIView that contain all these elements, but am unsure how to change the textbox size and move the view above a keyboard when pressed.
Can someone point me in the right direction
Have a look at MessageInputBar
https://github.com/MessageKit/MessageInputBar
It will make your like easy and will stop you from reinventing the wheel plus its highly customisable, you can run the example to see how it is working.
Edit
Just to give you an idea
import UIKit
import MessageInputBar
class CustomInputBar: MessageInputBar {
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure() {
backgroundView.backgroundColor = UIColor(red: 245/255, green: 245/255, blue: 245/255, alpha: 1)
let button = InputBarButtonItem()
button.setSize(CGSize(width: 36, height: 36), animated: false)
button.setImage(#imageLiteral(resourceName: "ic_up").withRenderingMode(.alwaysTemplate), for: .normal)
button.imageView?.contentMode = .scaleAspectFit
button.tintColor = UIColor(red: 0, green: 122/255, blue: 1, alpha: 1)
inputTextView.backgroundColor = .white
inputTextView.placeholderTextColor = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
inputTextView.textContainerInset = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
inputTextView.placeholderLabelInsets = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20)
inputTextView.layer.borderColor = UIColor(red: 200/255, green: 200/255, blue: 200/255, alpha: 1).cgColor
inputTextView.layer.borderWidth = 1.0
inputTextView.layer.cornerRadius = 4.0
inputTextView.layer.masksToBounds = true
inputTextView.scrollIndicatorInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
setLeftStackViewWidthConstant(to: 36, animated: false)
setStackViewItems([button], forStack: .left, animated: false)
sendButton.setSize(CGSize(width: 52, height: 36), animated: false)
}
}
which will look like this:
With all the feature you wanted plus more.
I've edited the code from the example project a little to make it look exactly as you added in the question.
And you ViewController will just be
import UIKit
import MessageInputBar
final class ExampleViewController: UITableViewController {
// MARK: - Properties
override var inputAccessoryView: UIView? {
return messageInputBar
}
override var canBecomeFirstResponder: Bool {
return true
}
// MARK: - MessageInputBar
private let messageInputBar = CustomInputBar()
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
tableView.keyboardDismissMode = .interactive
messageInputBar.delegate = self
}
}
And to listen to MessageInputBarDelegate simply add
extension ExampleViewController: MessageInputBarDelegate {
func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String) {
// Use to send the message
messageInputBar.inputTextView.text = String()
messageInputBar.invalidatePlugins()
}
func messageInputBar(_ inputBar: MessageInputBar, textViewTextDidChangeTo text: String) {
// Use to send a typing indicator
}
func messageInputBar(_ inputBar: MessageInputBar, didChangeIntrinsicContentTo size: CGSize) {
// Use to change any other subview insets
}
}
Simple as that :)
If you want to do it programatically by yourself you can try this.
Custom textentry view which will containt text input and send button
import UIKit
class TextEntryView: UIView {
let tvMessage: UITextView = {
let textView = UITextView()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.textColor = Constants.charlie
textView.font = UIFont.systemFont(ofSize: 17)
textView.isScrollEnabled = false
return textView
}()
let btnSend: UIButton = {
let image: UIImage = UIImage(named: "send_icon")!
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(image, for: .normal)
button.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .white
self.addBorders(edges: .top, color: UIColor(red: 220/250, green: 220/250, blue: 220/250, alpha: 1))
setupLayout()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override class var requiresConstraintBasedLayout: Bool {
return true
}
private func setupLayout() {
self.addSubview(tvMessage)
self.addSubview(btnSend)
tvMessage.topAnchor.constraint(equalTo: self.topAnchor, constant: 6).isActive = true
tvMessage.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 12).isActive = true
tvMessage.trailingAnchor.constraint(equalTo: btnSend.leadingAnchor, constant: -12).isActive = true
tvMessage.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -6).isActive = true
btnSend.topAnchor.constraint(equalTo: self.topAnchor, constant: 6).isActive = true
btnSend.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -12).isActive = true
btnSend.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -6).isActive = true
btnSend.widthAnchor.constraint(equalToConstant: 40).isActive = true
}
}
Add custom view in controller
import UIKit
class ChatController: UIViewController, UITextViewDelegate {
let textEntry = TextEntryView()
var bottomConstraint: NSLayoutConstraint?
var textEntryHeightConstraint: NSLayoutConstraint?
override func viewWillAppear(_ animated: Bool) {
initViews()
setupLayout()
NotificationCenter.default.addObserver(self,
selector: #selector(self.keyboardNotification(notification:)),
name: NSNotification.Name.UIKeyboardWillChangeFrame,
object: nil)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard))
tapGesture.cancelsTouchesInView = true
tableView.addGestureRecognizer(tapGesture)
}
#objc func hideKeyboard() {
self.endEditing(true)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
#objc func keyboardNotification(notification: NSNotification) {
if let userInfo = notification.userInfo {
let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let endFrameY = keyboardFrame?.origin.y ?? 0
let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue
let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
if endFrameY >= UIScreen.main.bounds.size.height {
bottomConstraint?.constant = 0
} else {
bottomConstraint?.constant = -(keyboardFrame?.size.height)!
}
UIView.animate(withDuration: duration,
delay: TimeInterval(0),
options: animationCurve,
animations: { self.layoutIfNeeded() },
completion: nil)
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (textEntry.tvMessage.contentSize.height + 12 < (textEntryHeightConstraint?.constant)!) {
self.textEntry.tvMessage.isScrollEnabled = false
} else {
self.textEntry.tvMessage.isScrollEnabled = true
}
return true
}
func textViewDidBeginEditing(_ textView: UITextView) {
if textEntry.tvMessage.textColor == .lightGray {
textEntry.tvMessage.text = nil
textEntry.tvMessage.textColor = Constants.tertiaryColor
}
}
func textViewDidEndEditing(_ textView: UITextView) {
if (textEntry.tvMessage.text?.isEmpty)! {
textEntry.tvMessage.text = "Write a message"
textEntry.tvMessage.textColor = .lightGray
}
}
}
extension MessageView {
func initViews() {
if #available(iOS 11.0, *) {
bottomConstraint = NSLayoutConstraint(item: textEntry, attribute: .bottom, relatedBy: .equal, toItem: self.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0)
} else {
// Fallback on earlier versions
bottomConstraint = NSLayoutConstraint(item: textEntry, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0)
}
textEntry.translatesAutoresizingMaskIntoConstraints = false
textEntry.tvMessage.text = "Write a message"
textEntry.tvMessage.textColor = .lightGray
textEntry.tvMessage.delegate = self
}
func setupLayout() {
self.addSubview(textEntry)
textEntry.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
textEntry.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
self.addConstraint(bottomConstraint!)
textEntryHeightConstraint = textEntry.heightAnchor.constraint(lessThanOrEqualToConstant: 150)
textEntryHeightConstraint?.isActive = true
}
}

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.

UICollectionViewCell size larger the UICollectionView size

I'm having the following runtime error: the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values.
This started occuring after a implemented, insize my custom UICollectionViewCell a UISearchBar item. When I click in it the keyboard shows up and the cell are half slided to the top.,
These are my relevant code:
class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
func setupCollectionView() {
if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.scrollDirection = .horizontal
flowLayout.minimumLineSpacing = 0
flowLayout.invalidateLayout()
}
collectionView?.backgroundColor = UIColor.white
collectionView?.register(FeedCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.register(TrendingCell.self, forCellWithReuseIdentifier: trendingCellId)
collectionView?.register(SubscriptionCell.self, forCellWithReuseIdentifier: subscriptionCellId)
collectionView?.register(MapCell.self, forCellWithReuseIdentifier: mapCellId)
collectionView?.register(AccountCell.self, forCellWithReuseIdentifier: accountCellId)
collectionView?.contentInset = UIEdgeInsetsMake(0, 0, 50, 0)
collectionView?.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, 50, 0)
collectionView?.isPagingEnabled = true
view.addSubview(menuBar)
view.addConstraintsWithFormat(format: "H:|[v0]|", views: menuBar)
view.addConstraintsWithFormat(format: "V:[v0(50)]", views: menuBar)
menuBar.bottomAnchor.constraint(equalTo: (collectionView?.bottomAnchor)!).isActive = true
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height - 50)
}
}
class MapCell: UICollectionViewCell{
let topContainer: TopContainerView = {
let v = TopContainerView()
return v
}()
override func setupViews() {
super.setupViews()
backgroundColor = .brown
topContainer.setupView()
addSubview(topContainer)
addConstraintsWithFormat(format: "V:[v0(40)]", views: topContainer)
addConstraintsWithFormat(format: "H:|[v0]|", views: topContainer)
addConstraint(NSLayoutConstraint(item: topContainer, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: topContainer, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 0))
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.endEditing(true)
}}
class TopContainerView: UIView {
func setupView(){
backgroundColor = .yellow
addSubview(searchField)
addConstraintsWithFormat(format: "V:|[v0]|", views: searchField)
addConstraintsWithFormat(format: "H:|[v0]-40-|", views: searchField)
addConstraint(NSLayoutConstraint(item: searchField, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 5))
addConstraint(NSLayoutConstraint(item: searchField, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 5))
}
let searchField: UISearchBar = {
let sf = UISearchBar()
sf.placeholder = "Search"
return sf
}()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.endEditing(true)
}}
Does any one have any ideia?

UICollectionview in a keyboard extension

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)