I have custom CollectionViewCell called DiaryCell in my project. I configure my cells using that class. Each cell has this menu button, and when it's clicked I want UICollectionviewcontroller to present actionsheet, but it gives me the following error.
'NSInvalidArgumentException', reason: 'UICollectionView must be initialized with a non-nil layout parameter'
Here is the code to handle that action when menu button is pressed in the cell. This action is handled in UICollectionViewController. To sum it up, my tap gesture is defined in customCell and when tapped it redirects the code to controller to handle the actions.
func menuButtonPressed(_ gesture: UITapGestureRecognizer) {
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let editAction = UIAlertAction(title: "Edit", style: .default) { (action) in
}
let deleteAction = UIAlertAction(title: "Delete", style: .destructive) { (action) in
}
let alert = UIAlertController(title: "Pick Your Adventure 😜", message: "You have the following options", preferredStyle: .actionSheet)
alert.addAction(cancelAction)
alert.addAction(editAction)
alert.addAction(deleteAction)
present(alert, animated: true, completion: nil)
}
I believe it has something to do with view layers. Since a cell is tapped inside UICollectionview, it doesn't know how and where to present actionsheet. I could be wrong. Thank you for reading my question.
EDIT: Code responsible for redirection of action to controller.
var homeCollection = HomeCollectionViewController()
let menuImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = #imageLiteral(resourceName: "menu_image")
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
imageView.isUserInteractionEnabled = true
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
func setUpMenuItemGesture() {
let tapGesture = UITapGestureRecognizer(target: self.homeCollection, action: #selector(self.homeCollection.menuButtonPressed(_:)))
tapGesture.numberOfTapsRequired = 1
tapGesture.delegate = homeCollection
menuImageView.addGestureRecognizer(tapGesture)
}
CODE FOR LAYOUT IN MY UICollectionViewController:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.size.width
return CGSize(width: width, height: width + (width*(0.15)))
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
My guess is that the problem is this line:
var homeCollection = HomeCollectionViewController()
That causes a completely new, separate HomeCollectionViewController to be created. It was never properly initialized and thus is invalid; more important, it is not the HomeCollectionViewController you want. You want a reference to the existing HomeCollectionViewController that you are already using, not a new and different HomeCollectionViewController.
Related
I've created a UICollectionView in Swift with photos taken from an API : https://jsonplaceholder.typicode.com/photos
I've created a window where my images can be set to fullscreen here:
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return posts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = myCollectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCollectionViewCell
let url = URL(string: "https://via.placeholder.com/600/\(posts[indexPath.row].thumbnailUrl)")
cell.myImageView.downaloadImage(from: url!)
cell.myImageView.layer.cornerRadius = 25
cell.myLabelName.text = posts[indexPath.row].title
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
let alert = UIAlertController(title: "FullScreen", message: "Are you sure you want to see the image fullscreen?", preferredStyle: .alert)
let actionyes = UIAlertAction(title: "Yes", style: .default) { action in
cell?.frame = UIScreen.main.bounds
cell?.backgroundColor = .magenta
cell?.contentMode = .scaleAspectFit
//de schimbat imagine thumbnailURL cu url
cell?.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(self.dismissFullscreenImage))
cell?.addGestureRecognizer(tap)
self.view.addSubview((cell)!)
self.navigationController?.isNavigationBarHidden = true
self.tabBarController?.tabBar.isHidden = true
}
let actionno = UIAlertAction(title: "No", style: .default) { action in
}
alert.addAction(actionno)
alert.addAction(actionyes)
present(alert, animated: true)
}
#objc func dismissFullscreenImage(sender: UITapGestureRecognizer) {
let alert2 = UIAlertController(title: "Go Back", message: "Are you sure you want to go back?", preferredStyle: .alert)
let actionyes2 = UIAlertAction(title: "Yes", style: .default) { action in
self.navigationController?.isNavigationBarHidden = false
self.tabBarController?.tabBar.isHidden = false
sender.view?.removeFromSuperview()
}
let actionno2 = UIAlertAction(title: "No", style: .default) { action in
}
alert2.addAction(actionno2)
alert2.addAction(actionyes2)
self.present(alert2, animated: true)
}
}
I'm trying to zoom the image that's in fullscreen but I don't really know where to start. My structures are here:
import Foundation
struct Post : Codable
{
let albumId : Int
let id : Int
let title : String
let url : String
let thumbnailUrl : String
}
Also, when I exit full screen my image disappears and I don't know how to keep it there. I think the problem is from here:
sender.view?.removeFromSuperview()
Can I make the image zoom from code? Or do I need something else? I've seen a lot of people using a scrollview but my images are in a collection view cell as shown here:
import UIKit
class MyCollectionViewCell: UICollectionViewCell {
#IBOutlet var myImageView: UIImageView!
#IBOutlet var myLabelName: UILabel!
}
Yes, calling sender.view?.removeFromSuperview() is bad because you are removing the collection view cell from the collection view. You must not do that.
You should not be doing anything to the collection view cell when you want to show the image fullscreen. Instead, create a new UIImageView with the image from the cell's image view, and show that new image view fullscreen. When you want to dismiss it, simply remove that image view. This will leave the original collection view cell and its image view untouched and still visible after the zoom.
It would also be a nice effect if you create the new image view with the same size and position of the cell's image view and then animate the new image view to fullscreen.
You could put the new image view into a UIScrollView if you want to allow the user to zoom in on the image but that's beyond the scope of your original question.
You can use ImageScrollView in place of ImageView in your cell.
Below is the link for the same.
https://github.com/huynguyencong/ImageScrollView
I have a uiTableView with 3 cells that have a uiContextualAction implemented through the trailingSwipeActionsConfigurationForRowAt delegate method.
On tap of the uiContextualAction I display an actionSheet with two actions - delete and dismiss.
When the actionSheet is displayed without having pressed any action, the cell's content for all 3 cells, specifically a uiImageView suddenly disappears.
Is there something inherent to uiTableViews causing this? I placed breakpoints throughout the aforementioned flow but to no avail on how to remedy this. Any thoughts would be appreciated.
BEFORE presented actionSheet - ImageView (red background w/ blue gradient image) of UITableViewCell
AFTER on presented actionSheet - ImageView (red background w/o blue gradient image)
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = UIContextualAction(style: .normal, title: "") { (action, view, completion) in
self.onDeleteCell(indexPath)
completion(true)
}
delete.backgroundColor = UIColor.red
delete.image = #imageLiteral(resourceName: "x_circle")
let config = UISwipeActionsConfiguration(actions: [delete])
config.performsFirstActionWithFullSwipe = false
return config
}
func onDelete(_ indexPath: IndexPath) {
AlertController.showActionSheet(self, title: nil, message: nil, actionOneTitle: "Delete", actionOneStyle: .destructive, actionTwoTitle: "Dismiss", actionTwoStyle: .cancel) { (_) in
self.updateCells(indexPath)
}
}
//From custom AlertController class
static func showActionSheet(_ inViewController: UIViewController, title: String?, message: String?, actionOneTitle: String, actionOneStyle: UIAlertAction.Style, actionTwoTitle: String, actionTwoStyle: UIAlertAction.Style, actionOneHandler: #escaping ((UIAlertAction) -> Void)) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
let actionOne = UIAlertAction(title: actionOneTitle, style: actionOneStyle, handler: actionOneHandler)
alert.addAction(actionOne)
let actionTwo = UIAlertAction(title: actionTwoTitle, style: actionTwoStyle, handler: nil)
alert.addAction(actionTwo)
inViewController.present(alert, animated: true, completion: nil)
}
Still unsure what the issue is/was...but I replaced the vector image as png and it appears to render and not suddenly vanish anymore..
Not a definitive solution, but it for now happens to work for me.
My code is this:
let alrController = UIAlertController(title: "Membri", message: nil, preferredStyle: UIAlertControllerStyle.actionSheet)
tableView.backgroundColor = UIColor.white
alrController.view.addSubview(tableView)
let cancelAction = UIAlertAction(title: "Esci", style: UIAlertActionStyle.cancel, handler: {(alert: UIAlertAction!) in})
alrController.addAction(cancelAction)
self.present(alrController, animated: true, completion:{})
I want to populate (but I don't know how) the tableView with this values in my array: names["name1","name2","name3"]
Can someone help me?
To populate an action sheet, you don't add a tableView. Instead, you simply add the actions and it will create and manage the tableView privately.
For a recent tutorial, see UIAlertController Examples
The idea is that you'd create an UIAlertAction for each String in your array, including a closure for what to do when user taps that action row.
for name in names
{
let namedAction = UIAlertAction(title: name, style: .default)
{ (action) in
// do something when this action is chosen (tapped)
}
alrController.addAction(namedAction)
}
You need to implement the following tableView dataSource methods in order to populate this data and also set tableView datasource to alertController like below:-
tableView.dataSource = alertController
TableView DataSource method
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell: yourCustomCell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as? yourCustomCell
else {
fatalError("yourCustomCell not found")
}
cell.textLabel.text = names[indexpath.row]
return cell
You can use custom UIViewController transition to achieve this kind of functionality.
This link: https://github.com/pgpt10/Custom-Animator will give you an idea of how you can achieve that.
Good day to all. Faced a problem. I need to make a table with a button and by clicking on the button I get a alert with the number of the cell. The table cells themselves are not active. That's how I realized it. When I scroll the table in the beginning everything is fine, when you press the button, a alert is displayed with the correct line number, but after 4 elements an error appears.
This error appears in the line where I'm working with the 4 tag.
Fatal error: unexpectedly found nil while unwrapping an Optional value
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as UITableViewCell
if (tableView.tag == 1) {
let numLabel: UILabel = tableView.viewWithTag(3) as! UILabel
numLabel.text = String(indexPath.row)
} else if (tableView.tag == 2) {
//Error appears here
let numButton: UIButton = tableView.viewWithTag(4) as! UIButton
numButton.setTitle(String(indexPath.row), for: .normal)
numButton.tag = indexPath.row
}
return cell
}
#IBAction func showAlertForRow(row: UIButton) {
let alert = UIAlertController(title:"Test work",message:"Cell at row \(row.tag) was tapped!",preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Okay", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
What you are designing for implementing this procedure is not correct. What you can do
Make a custom cell
Add a button in custom cell
Add action in that button in CellForRowAtIndexPath
Handle action from ViewController where you added tableView.
I made a whole project for you.Just to let you know. if you want to add customCell in tableView you need to register it like this in viewDidLoad.
i have done is in ViewController.swift file. check out my project.
let nib = UINib.init(nibName:String(describing: sampleTableViewCell.self) , bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "chatCell")
Then check cellForRowAtIndexPath function:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "chatCell", for: indexPath) as! sampleTableViewCell
cell.clickMeBtn.tag = indexPath.row
cell.clickMeBtn.addTarget(self, action: #selector(onButtonPressed(sender :)), for: .touchUpInside)
return cell
}
Button press function:
func onButtonPressed(sender:UIButton) {
let alert = UIAlertController.init(title:"Cell index is"+String(sender.tag), message: nil, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction.init(title: "ok", style: UIAlertActionStyle.default) { (UIAlertAction) in
}
alert.addAction(okAction)
self.present(alert, animated: true, completion: nil)
}
Check only three files:
Github link
ViewController.swift
sampleTableViewCell.swift
sampleTableViewCell.xib**
Here is the output:
Alright, I am testing around a bit with core data, something I recently have started to discover, basically, what i have so far, is a single view app, that has a data source, and i can press a button and it brings up and alert, which from there i can add names to the list, and delete names from the list, i can close my app and still maintain my data. here is the issue/question, i am trying to do an update, so i can edit names in the list, i have a uibutton set up on my prototype cell, and i have it linked to my viewController, and have a function set inside the IBAction for the button. however, the button does not appear in my sim at run time.
here i have some code.
this is code for the edit button, and its function:
#IBAction func editButton(sender: AnyObject) {
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// 2
let alert = UIAlertController(title: "Update",
message: "Please enter the new name.",
preferredStyle: .Alert)
// 3
let updateAction = UIAlertAction(title: "Save",
style: .Default){(_) in
let nameTextField = alert.textFields![0]
self.updateName(indexPath.row, newName: nameTextField.text!)
self.tableView.reloadData()
}
// 4
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alert.addTextFieldWithConfigurationHandler(nil)
alert.addAction(updateAction)
alert.addAction(cancelAction)
// 5
self.presentViewController(alert, animated: true, completion: nil)
}
here is my cellForRowAtIndexPath func
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell")!
let people = peoples[indexPath.row]
cell.textLabel!.text = people.name
return cell
}
here is an image of my storyboard
Storyboard
if you need further information or code, please let me know and i will provide it.