I want to implement a collection view for images in an array and when I will select the image in collection view cell it should take me to other view controller with an image view in it and should show the enlarged image of the selected on.
What should be the code written for this in did select Item so that the selected image will be displayed in the other view controller?
After you have created a DetailViewController with an imageView (with the frame you prefer) and a variable called for example var selectedImage: String?, in your collectionView delegate function didSelectItemAt you have to instantiate your DetailViewController and before presenting it you can set its selected image var.
Example:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedImage: String = imagesArray[indexPath.row]
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let detailVC = storyboard.instantiateViewController(withIdentifier: "Your identifier") as? DetailViewController
detailVC?.selectedImage = selectedImage
//present or push your detailViewController
}
Then in your DetailViewController:
override func viewDidLoad() {
super.viewDidLoad()
if let image = selectedImage {
myImageView.image = UIImage(named: image)
}
}
Related
I need to open UIViewController from SwiftUI with UINavigation having back button.
Currently, I had implemented this via ViewControllerRepresentable and NavigationLink, its works, but I have two Navigations SwiftUI(NavigationLink with back button) and default UINavigation without back button, and I would like to perform to have my default UINavigation with the back button and hide SwiftUI(NavigationLink) navigation.
struct UIKitVc : UIViewControllerRepresentable {
let navigationController = UINavigationController()
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {
}
func makeUIViewController(context: Context) -> UINavigationController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "SearchResult") as! SearchResultViewController
self.navigationController.addChild(viewController)
return self.navigationController
}
}
NavigationLink(destination: UIKitVc(){}
Thank you in advance.
If you're going to use NavigationView/NavigationLink then you don't need to use UINavigationController, just use representable over your UIViewController, like
struct UIKitVc : UIViewControllerRepresentable {
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
func makeUIViewController(context: Context) -> UIViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier:
"SearchResult") as! SearchResultViewController
// ... make additional set up here if needed
return viewController
}
}
I have a View with an UIImageView that I want to be 'selectable' so that the user can pick a new image.
The function for picking the new image is in the Controller.
Question
How do I call the myDatasourceController.handleTap() function by pressing the ImageView, so that the image picker is presented?
This is an example of my current setup
View
class myView: UICollectionViewCell {
lazy var profileImageView: UIImageView = {
let iv = UIImageView()
iv.isUserInteractionEnabled = true
iv.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(myDatasourceController.handleTap)))
return iv
}()
}
Controller
class myDatasourceController: UICollectionViewController,
UICollectionViewDelegateFlowLayout, UIImagePickerControllerDelegate,
UINavigationControllerDelegate {
func handleTap(){
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.allowsEditing = true
present(imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
// logic for picking the image
dismiss(animated: true, completion: nil)
}
}
This setup currently throws the error
unrecognized selector sent to instance 0x7f9163d493f0
which has led me to try various combinations of
handleTap(_:)
handleTap(sender: UITapGestureRecogniser)
/// etc
but I can't get any of them to work. How should I be constructing my View, Controller, and the interaction between them to present the image picker?
Use Like this
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(RegisterViewController. handleTap(gesture:)))
func handleTap(gesture: UIGestureRecognizer) {
// if the tapped view is a UIImageView then set it to imageview
if (gesture.view as? UIImageView) != nil {
print("Image Tapped")
picker.allowsEditing = false
picker.sourceType = .photoLibrary
picker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
present(picker, animated: true, completion: nil)
}
}
Use like this :
myDatasourceController.handleTap()
In your code :
iv.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(myDatasourceController.handleTap())))
The key to the solution is to implement a protocol / delegate, as suggested by #Akis
I've uploaded the full project to my github account. The key code is copied here.
View Controller
protocol ImagePickerDelegate: class {
func loadImagePicker()
}
class HomeViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UIImagePickerControllerDelegate, UINavigationControllerDelegate, ImagePickerDelegate {
let cellId = "cellId"
func loadImagePicker(){
print(" -- image picker -- ")
// load image picker
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.allowsEditing = true
present(imagePickerController, animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
// get the image
var selectedImageFromPicker: UIImage?
if let editedImage = info["UIImagePickerControllerEditedImage"] as? UIImage {
selectedImageFromPicker = editedImage
}else if let originalImage = info["UIImagePickerControllerOriginalImage"] as? UIImage {
selectedImageFromPicker = originalImage
}
if let selectedImage = selectedImageFromPicker {
//doSomethingWithTheImage(image: selectedImage)
}
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = .black
collectionView?.register(HomeView.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! HomeView
cell.delegate = self
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
}
View
class HomeView: UICollectionViewCell {
// break retain cycle with weak var
weak var delegate: ImagePickerDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
lazy var profileImageView: UIImageView = {
let iv = UIImageView()
iv.isUserInteractionEnabled = true
iv.image = UIImage(named: "kuang-si-falls-waterfall-water-laos-50588.jpg")
iv.contentMode = .scaleAspectFill
let tap = UITapGestureRecognizer(target: self, action: #selector(loadImagePicker))
iv.addGestureRecognizer(tap)
return iv
}()
func loadImagePicker() {
delegate?.loadImagePicker()
print(" imagePickerProtocol called ")
}
func setupViews() {
backgroundColor = .white
addSubview(profileImageView)
profileImageView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
profileImageView.frame = CGRect(x: 0, y: 0, width: 150, height: 150)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I have this DiaryCell (collectionviewcell) class, and I am trying to add a gesture recognizer in that class to call the method of a collectionViewController, which is a different class.
class DiaryCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//imageview for favorite button
let favoriteImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = #imageLiteral(resourceName: "favorite_gray")
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
//imageview for menu button
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
}()
}
I have tried different strategies, but I am not able to add the gesture recognizer to menuImageView. I have tried doing this, where HomeCollectionViewController is the controller where I want to handle the action when image is tapped.
imageView.addGestureRecognizer(UITapGestureRecognizer(target: HomeCollectionViewController.self, action: #selector(HomeCollectionViewController.menuBarPressed))
The problem is that while your code is legal and compiles, it's wrong.
You are setting up your gesture to be send to a type not an instance. You need to set the target as the actual instance of HomeCollectionViewController
One way to achieve this is by setting a weak reference to your controller in the cell.
class DiaryCell: UICollectionViewCell {
var tapGesture:UITapGestureRecognizer?
weak var gestureTarget: HomeCollectionViewController? {
didSet {
setupGestures()
}
}
func setupGestures() {
if let tapGesture = tapGesture {
removeGestureRecognizer(tapGesture)
self.tapGesture = nil
}
if let gestureTarget = gestureTarget {
let gesture = UITapGestureRecognizer(target: gestureTarget, action: #selector(HomeCollectionViewController.menuBarPressed))
addGestureRecognizer(gesture)
tapGesture = gesture
}
}
}
You would probably set the gestureTarget in your collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell method
While this solves your problem its not a good way to approach things and you should look up how to use delegation as a coding pattern.
I have researched this issue and I'm almost certain my code is correct, but clearly there is something wrong. I have two controllers. One has a TableView and the other is a view controller with a label. I want the value of the cell text label in the tableview cell selected by the user to be sent to the second view controller. Pretty simple. Here are my controllers:
import UIKit
import CoreData
class FavoritesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var selectedFavorite = ""
var specifiedFavorite:String!
let appDelegate = UIApplication.shared.delegate as! AppDelegate
var passages = [NSManagedObject]()
//This code creates the number of cells equal the number of items in the object
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return passages.count
}
// The code below displays the passage reference as the cell title
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// This creates the cell for the table. The dequeueReusableCell option ensures the table is scrollable
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCell
// this creates a variable for the record defined at the index that is numbered by the indexPath.row.
let favorite = passages[indexPath.row]
// We then find the value of the key we want to set for the cell label value
cell.textLabel!.text = favorite.value(forKey: "reference") as? String
return cell
}
// This code detects the cell selected and captures a variable that is passed in the segue
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath) as UITableViewCell!
selectedFavorite = (selectedCell?.textLabel?.text)!
print("The text in the selected cell is \(selectedFavorite)")
performSegue(withIdentifier: "toFavoritesDetailsViewController", sender: nil)
print("Segue performed")
}
//This code is the segue that passes the variable values to the next view
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toFavoritesDetailsViewController" {
let favoritesDetailsViewController = segue.destination as! FavoritesDetailsViewController
favoritesDetailsViewController.specifiedFavorite = selectedFavorite
print("The variable for selectedFavorite prior to segue is: \(selectedFavorite)")
print("The variable for favoritesDetailsViewController.specifiedFavorite prior to segue is: \(favoritesDetailsViewController.specifiedFavorite)")
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//This code loads the Core Data Entity into the view
// this is working as evidenced by the tableview being populated with data
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName:"Passages")
//
do {
let results = try managedContext.fetch(fetchRequest)
passages = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error)")
}
}
}
Here is the code for my second controller called FavoritesDetailsViewController:
import UIKit
import CoreData
class FavoritesDetailsViewController: UIViewController {
#IBOutlet weak var referenceLabel: UILabel!
#IBOutlet weak var passageText: UITextView!
var specifiedFavorite : String = ""
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
referenceLabel.text? = specifiedFavorite
print("The viewDidLoad")
// SMC LEFT OFF HERE
// Can't get selectedFavorite to load from previous controller
print("The Value of variable specifiedFavorite sent from segue is: \(specifiedFavorite)")
print(specifiedFavorite)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Passages")
request.predicate = NSPredicate(format: "reference = %#", specifiedFavorite)
request.returnsObjectsAsFaults = false
do {
let results = try context.fetch(request)
print("fetch request was successful")
if results.count > 0 {
for result in results as! [NSManagedObject] {
// print("We got results!")
if let returnedText = result.value(forKey: "passagetext") as? String {
print("This is the value of returnedText: \(returnedText)")
passageText.text? = returnedText
print("This is the text of the selectedFavorite after segue is: \(passageText)")
}
}
}
} catch {
print("Couldn't fetch results")
}
}
override func viewDidLoad() {
super.viewDidLoad()
print("The viewDidLoad")
print("The Value of variable specifiedFavorite sent from segue is: \(specifiedFavorite)")
print(specifiedFavorite)
}
}
When I run the app, the logs indicate that the value of variable "specifiedFavorite" is set prior to segue.
The variable for selectedFavorite prior to segue is:
The variable for favoritesDetailsViewController.specifiedFavorite prior to segue is:
The viewDidLoad
The Value of variable specifiedFavorite sent from segue is:
The text in the selected cell is John 3:16
The variable for selectedFavorite prior to segue is: John 3:16
The variable for favoritesDetailsViewController.specifiedFavorite prior to segue is: John 3:16
2016-12-10 12:43:54.624 Logos to Mind[4036:770173] <UIView: 0x7ff0bf918010; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x600000421fa0>>'s window is not equal to <Logos_to_Mind.FavoritesDetailsViewController: 0x7ff0bf9047f0>'s view's window!
Segue performed
The FavoritesDetailsViewController viewDidLoad
The Value of variable specifiedFavorite sent from segue is:
fetch request was successful
Note the log message:
The Value of variable specifiedFavorite sent from segue is:
is empty.
This is my problem. I don't see what errors exist in my code that is failing to set that "specifiedFavorite" variable in the FavoritesDetailsViewController. I've hit a brick wall here. Would appreciate some insight.
Your segue is wired from your prototype cell. Don't bother with didSelectRowAt because it is called after prepare(for:sender). Do all of your work in prepare(for:sender:). The sender is the cell that triggered the segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toFavoritesDetailsViewController" {
let favoritesDetailsViewController = segue.destination as! FavoritesDetailsViewController
if let selectedCell = sender as? UITableViewCell {
let selectedFavorite = (selectedCell.textLabel?.text)!
favoritesDetailsViewController.specifiedFavorite = selectedFavorite
print("The variable for selectedFavorite prior to segue is: \(selectedFavorite)")
print("The variable for favoritesDetailsViewController.specifiedFavorite prior to segue is: \(favoritesDetailsViewController.specifiedFavorite)")
}
}
}
I am trying to display the contents of an array in table view cells.
I created the arrays (I have one array of images and three arrays of strings).
I managed to display the contents of the arrays without any issues.
Here is my viewController.swift :
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var shopOpen: Bool = false
var openSign: UIImage = UIImage(named: "open")!
var closedSign: UIImage = UIImage(named: "closed")!
var logos = [UIImage(named: "shop1"), UIImage(named: "shop2"), UIImage(named: "shop3")]
var programWorkingDays = ["Luni-Vineri:09:00-20:00", "Luni-Vineri::10:00-21:00", "Luni-Vineri:09:30-19:30"]
var programSambata = ["Sambata:10:00-16:00","Sambata:10:30-13:00" ,"Sambata: 09:00-13:00"]
var programDuminica = ["Duminica:10:00-15:00","Duminica:09:00-14:00","Duminica:10:30-15:00"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! Cell
cell.logo.image = logos[indexPath.row]
cell.programWorkingDays.text = programWorkingDays[indexPath.row]
cell.programSambata.text = programSambata[indexPath.row]
cell.programDuminica.text = programDuminica[indexPath.row]
return cell
}
}
My next goal is to display one image or another based on a true/false value of a variable (shopOpen)
If the variable is true i want to have picture 1, if it's false i want to have picture 2.
The image view is placed in the main.storyboard and the connection is made in the cell class.
The images will be storet in UIImage variables (given the fact that i only have two images for this part, i don't think it makes any sense to put them inside an array)
Where should i write the if statement and how do i refresh the cells to display the correct image ?
First of all, rather than multiple arrays for the parameters use a struct including an boolean open property
struct Shop {
var workingDays : String
var sambata : String
var duminica : String
var logo : UIImage?
var open = false
}
In the ViewController class declare a variable shops as an empty Shop array
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
let openSign: UIImage = UIImage(named: "open")!
let closedSign: UIImage = UIImage(named: "closed")!
var shops = [Shop]()
In viewDidLoad populate the shops array with appropriate Shop instances
override func viewDidLoad() {
super.viewDidLoad()
shops.append(Shop(workingDays: "09:00-20:00", sambata: "10:00-16:00", duminica: "10:00-15:00", logo: UIImage(named: "shop1"), open: true))
shops.append(Shop(workingDays: "10:00-21:00", sambata: "10:30-13:00", duminica: "09:00-14:00", logo: UIImage(named: "shop2"), open: false))
shops.append(Shop(workingDays: "09:30-19:30", sambata: "09:00-13:00", duminica: "10:30-15:00", logo: UIImage(named: "shop3"), open: true))
}
In numberOfRowsInSection return the number of shops rather than a hard-coded integer
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return shops.count
}
In cellForRowAtIndexPath display the values from the Shop instances. Assuming there is an image view openImage the open or close image is displayed depending on the state of the open property.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! Cell
let shop = shops[indexPath.row]
cell.logo.image = shop.logo
cell.programWorkingDays.text = "Luni-Vineri: \(shop.workingDays)"
cell.programSambata.text = "Sambata: \(shop.sambata)"
cell.programDuminica.text = "Duminica: \(shop.duminica)"
cell.openImage.image = shop.open ? openSign : closedSign
return cell
}
}