displaying CollectionViewController in Playground - xcode

I am simply trying to display a CollectionView in playground to quickly try out ideas. Yet this has been a hour+ affair. I'm getting the error:
2017-03-17 17:28:37.862 uikitTest[88414:10270872] * Assertion failure in -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3600.6.21/UICollectionView.m:4971
2017-03-17 17:28:37.866 uikitTest[88414:10270872] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'could not dequeue a view of kind: UICollectionElementKindCell with identifier test1 - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
*** First throw call stack:
I tried setting a view to the PlaygroundPage, and also creating a UIWindow. It isn't clear to me how to work with ViewControllers in Playground, and even whether this is the root problem and if its connected to the could not dequeue exception.
My Playground is as follows:
import UIKit
import XCPlayground
import PlaygroundSupport
class CVCCell: UICollectionViewCell {
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: .zero)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
label.frame = bounds
}
}
class CVC: UICollectionViewController {
var datasource = ["1","2","3"]
override func viewDidLoad() {
view.backgroundColor = .red
print(collectionView)
collectionView?.register(CVCCell.self, forCellWithReuseIdentifier: "test1")
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return datasource.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "test1", for: indexPath) as! CVCCell
cell.label.text = datasource[indexPath.row]
return cell
}
}
let containerView = UIView(frame: CGRect(x: 0.0,
y: 0.0,
width: 375.0,
height: 667.0))
containerView.backgroundColor = .green
let fl = UICollectionViewFlowLayout()
let cvc = CVC(collectionViewLayout: fl)
let w = UIWindow(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
w.rootViewController = cvc
print(cvc.view)
containerView.addSubview(cvc.view)
//PlaygroundPage.current.liveView = containerView

Related

UIImage inside UIImageView is not scalling to fit

I receive a high resolution images from API, and I need to scale to fit them to my collection view cells. I'm using scaleToFit in UIImageView init but images are still in original ratio, moreover if I start to scroll in some moment they will get in full size
Cell:
final class SnapCarouselCellImpl: UICollectionViewCell, SnapCarouselCell{
var imageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleToFill
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(imageView)
imageView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
imageView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
imageView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
}
VC:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SnapCarouselCell", for: indexPath) as! SnapCarouselCellImpl
let item = items[indexPath.row % items.count]
cell.imageView.image = UIImage(data: item.poster)
return cell
}
private func setUpCollectionView() -> UICollectionView{
let collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: flowLayout)
collectionView.showsHorizontalScrollIndicator = false
collectionView.frame = CGRect(x: 0, y: 100, width: view.frame.size.width, height: cellHeight * 1.2)
collectionView.decelerationRate = .init(rawValue: 20)
collectionView.register(SnapCarouselCellImpl.self, forCellWithReuseIdentifier: "SnapCarouselCell")
return collectionView
}
FlowLayout:
override func prepare() {
scrollDirection = .horizontal
minimumLineSpacing = 20
itemSize = CGSize(width: cellWidth, height: cellHeight)
let sideMargin = (collectionView!.bounds.width - cellWidth) / 2.0
sectionInset = UIEdgeInsets(top: 0.0, left: sideMargin, bottom: 0.0, right: sideMargin)
}
Result:
After scrolling:
You didn't provide enough code to confirm this, but...
You can probably correct the issue by changing this line in init in your SnapCarouselCellImpl class from:
addSubview(imageView)
to:
contentView.addSubview(imageView)

Increasing `collectionViewContentSize` width causes CollectionView Cells to disappear

So I have an issue I've been battling with for days now. I have a collectionView which is designed to have large cells (width-wise) which go off screen. The height is fixed. The idea is to basically scroll to the end (see below):
To achieve being able to scroll left and right, I've had to override the width inside collectionViewContentSize in my flowLayout. Like below.
override var collectionViewContentSize: CGSize {
var size = super.collectionViewContentSize
size.width = largeWidth
return size
}
This achieves increasing the horizontal scroll area (which is what I want) but the cells start to disappear once I reach a certain point. It's almost as if the cells are being dequeued when they shouldn't be. Any ideas on this. This is the final straw for my project but I'm out of ideas.
Many thanks
Code snippet can be found below. You should be able to just copy and paste this into any project:
class HomeViewController: UIViewController {
let collectionView: UICollectionView
let collectionViewLayout = CustomCollectionViewFlowLayout()
init() {
collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
super.init(nibName: nil, bundle: nil)
collectionView.backgroundColor = UIColor.red.withAlphaComponent(0.4)
collectionView.register(SomeCell.self, forCellWithReuseIdentifier: "SomeCell")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
collectionView.delegate = self
collectionView.dataSource = self
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView.frame = view.bounds
view.addSubview(collectionView)
}
}
class SomeCell: UICollectionViewCell {
}
extension HomeViewController: UICollectionViewDataSource,
UICollectionViewDelegate,
UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 150
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SomeCell", for: indexPath) as! SomeCell
cell.backgroundColor = .blue
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 10000, height: 70)
}
}
class CustomCollectionViewFlowLayout: UICollectionViewFlowLayout {
override var collectionViewContentSize: CGSize {
var size = super.collectionViewContentSize
size.width = 10000
return size
}
}

How to fix: Application tried to present a nil modal view controller on target

I tried using the simulator and real phone, I still get the same error.
ERROR: Thread 1: Exception: Application tried to present a nil modal view controller on target
Here's the complete ViewController. Please see the codes below.
import UIKit
import MessageUI
class CellClass: UITableViewCell {
}
class ContactUsViewController: UIViewController, MFMailComposeViewControllerDelegate {
#IBOutlet weak var messageField: UITextField!
#IBOutlet weak var subjectField: UIButton!
let transparentView = UIView()
let tableView = UITableView()
var selectedButton = UIButton()
var dataSource = [String]()
var MFMailComposeResultCancelled = MFMailComposeResult(rawValue: 0)
var MFMailComposeResultFailed = MFMailComposeResult(rawValue: 0)
var MFMailComposeResultSaved = MFMailComposeResult(rawValue: 0)
var MFMailComposeResultSent = MFMailComposeResult(rawValue: 0)
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(CellClass.self, forCellReuseIdentifier: "Cell")
}
func addTransparentView(frames: CGRect) {
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
transparentView.frame = window?.frame ?? self.view.frame
self.view.addSubview(transparentView)
tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height, width: frames.width, height: 0)
self.view.addSubview(tableView)
tableView.layer.cornerRadius = 5
transparentView.backgroundColor = UIColor.black.withAlphaComponent(0.9)
tableView.reloadData()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(removeTransparentView))
transparentView.addGestureRecognizer(tapGesture)
transparentView.alpha = 0
UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {self.transparentView.alpha = 0.5
self.tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height + 5, width: frames.width, height: CGFloat(self.dataSource.count * 50))
}, completion: nil)
}
#IBAction func subjectList(_ sender: Any) {
dataSource = [" General Inquiries", " Purchase STL File"," Purchase an Eternity Band Ring", " Inquire about Measurements"]
selectedButton = subjectField
addTransparentView(frames: subjectField.frame)
}
#objc func removeTransparentView() {
let frames = selectedButton.frame
UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: { self.transparentView.alpha = 0
self.tableView.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height, width: frames.width, height: 0)
}, completion: nil)
}
#IBAction func sendTapped(_ sender: Any) {
let toCS = ["info#ezbandcalculator.com"]
let mc: MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setToRecipients(toCS)
mc.setSubject(selectedButton.currentTitle!)
mc.setMessageBody("Message: \(String(describing: messageField?.text))", isHTML: false)
self.present(mc, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result.rawValue {
case MFMailComposeResultCancelled!.rawValue:
print ("Cancelled")
case MFMailComposeResultFailed!.rawValue:
print ("Failed")
case MFMailComposeResultSaved!.rawValue:
print ("Saved")
case MFMailComposeResultSent!.rawValue:
print ("Sent")
default:
break
}
controller.dismiss(animated: true, completion: nil)
}
#IBAction func dismissKeyboard(_ sender: Any) {
self.resignFirstResponder()
}
}
extension ContactUsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = dataSource[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedButton.setTitle(dataSource[indexPath.row], for: .normal)
removeTransparentView()
}
}
You may want to check whether your current device can send mail or not before presenting.
if MFMailComposeViewController.canSendMail() {
// present
}
Refer: https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontroller/1616879-cansendmail

Resign First Responder function in SKScene

I am trying to create a game that requires to create a textfield in SKScene. I have built the text field successfully, but bringing the keyboard down is the problem. This is my code so far.
import SpriteKit
import GameplayKit
import UIKit
class GameScene: SKScene {
var sceneFrame : CGRect = CGRect()
var textFieldFrame : CGRect = CGRect()
var textField : UITextField = UITextField()
var skView: SKView = SKView()
override func didMove(to view: SKView) {
sceneFrame = CGRect(origin: .zero, size: CGSize(width: 200, height: 200))
let scene = SKScene(size: sceneFrame.size)
scene.backgroundColor = UIColor.lightGray
textFieldFrame = CGRect(origin: .init(x: 100, y: 200), size: CGSize(width: 200, height: 30))
textField = UITextField(frame: textFieldFrame)
textField.backgroundColor = UIColor.white
textField.placeholder = "Phone Number"
textField.keyboardType = .numberPad
textField.resignFirstResponder()
self.view!.addSubview(textField)
self.view!.presentScene(scene)
// Where to put the textField.resignFirstResponder() ?
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
Any answers would be helpful.
The problem is you are not adding:
textfield.delegate = self
Then you could call the function:
func textFieldShouldReturn(textField: UITextField) -> Bool {
// Hides the keyboard
textField.resignFirstResponder()
return true
}
Declaring the delegate statement initiate the function textFieldShouldReturn.
Hope this helps :)

tableView didset does not reload

I am trying the to reload the tableview whenever the didset is countRow is called. It has never worked.
I have tried guard and if let in the didset, it has never work out.
I realised it is calling another object of tableview. it is very weird,
import UIKit
class ViewController:UIViewController, UITableViewDelegate, UITableViewDataSource {
// Global Variable
var tableView: UITableView!
var tableView2 : UITableView!
var countRow : Int = 0 {
didSet{
//if let tableview == tableview
guard tableView2 == tableView else
{
tableView2 = UITableView(frame: self.view.bounds)
tableView2.delegate = self
tableView2.dataSource = self
self.tableView2.reloadData()
self.tableView.reloadData()
print ("3countRow: \(countRow)")
return
}
self.tableView2.reloadData()
self.tableView.reloadData()
print ("2countRow: \(countRow)")
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView = UITableView(frame: self.view.bounds)
tableView.delegate = self
tableView.dataSource = self
self.view.addSubview(tableView)
tableView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
tableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
tableView.register(AppCell.self, forCellReuseIdentifier: "NormalCell")
tableView.register(appDays.self, forCellReuseIdentifier: "textField")
tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print (countRow)
return 5 + countRow
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0
{
let cell = tableView.dequeueReusableCell(withIdentifier: "NormalCell", for: indexPath) as! AppCell
cell.backgroundColor = UIColor.groupTableViewBackground
return cell
}
if indexPath.row == 1 || indexPath.row == 2
{
var cell: TableViewCell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
cell.backgroundColor = UIColor.groupTableViewBackground
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "textField", for: indexPath) as! appDays
cell.backgroundColor = UIColor.groupTableViewBackground
return cell
}
}
countrow will be printed in the program
You never set countRow to a value. You only add 5 + countRow which does not mean you set 5 in countRow.
didSet is called immediately after the new value is stored. Swift official documentation

Resources