Obtaining image details in detailviewcontroller when clicking image in collectionviewcontroller - ios

My CollectionViewController contains an image. When I click the image in my CollectionViewController it takes me to the detailviewcontroller where I want to edit the details of the image. When I enter the details such as an URL, then click the back button, it should fetch the image from the URL and replace the image in my CollectionViewController.
I have two problems:
When I click the image in my CollectionViewController it opens the detailviewcontroller, but it doesn't show the url of the image I clicked.
When I click the back button it does not take me back to the CollectionViewController.
import Foundation
class Photo {
var url: String = ""
var data: NSData? = nil
var Title: String = ""
var Tags: String = ""
func loadImage(completionHandler: (data: NSData?) ->Void) {
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
dispatch_async(queue) {
let mainQueue = dispatch_get_main_queue()
if let url = NSURL(string: self.url) {
if let data = NSData(contentsOfURL: url) {
sleep(3)
dispatch_async(mainQueue) {
self.data = data
completionHandler(data: data)
}
return
}
dispatch_async(mainQueue) {
completionHandler(data: nil)
}
}
}
}
}
detailviewcontroller
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
var photo: Photo!
#IBOutlet weak var textURL: UITextField!
#IBOutlet weak var textTitle: UITextField!
#IBOutlet weak var textTags: UITextField!
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
let urlString = textField.text
photo.url = urlString
let DatatoImage: (NSData?) -> Void = {
if let d = $0 {
let image = UIImage(data: d)
self.imageView.image = image
} else {
self.imageView.image = nil
}
//textField.resignFirstResponder()
}
if let d = photo.data {
DatatoImage(d)
textField.resignFirstResponder()
let image = UIImage(data: d)
self.imageView.image = image
} else {
photo.loadImage(DatatoImage)
}
return true
}
}
My masterViewController
import UIKit
let reuseIdentifier = "Cell"
class PhotosCollectionViewController: UICollectionViewController {
var photos = Array<Photo>()
override func viewDidLoad() {
super.viewDidLoad()
let photo = Photo()
photo.url = "http://www.griffith.edu.au/__data/assets/image/0019/632332/gu-header-logo.png"
photos.append(photo)
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.collectionView!.registerClass(PhotoCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
//#warning Incomplete method implementation -- Return the number of sections
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return photos.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as PhotoCollectionViewCell
let photo = photos[indexPath.row]
if let d = photo.data {
let image = UIImage(data: d)
cell.imageView.image = image
photo.url = "\(photo.url)"
photo.Title = "\(photo.Title)"
} else {
photo.loadImage {
if $0 != nil {
collectionView.reloadItemsAtIndexPaths([indexPath])
}
}
}
return cell
}
// Uncomment this method to specify if the specified item should be highlighted during tracking
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let indexPath = sender as? NSIndexPath {
let photo = photos[indexPath.row]
}
if (segue.identifier == "showDetail") {
if let dvc = segue.destinationViewController as? ViewController {
let photo = Photo()
dvc.photo = photo
}
}
}
override func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool {
performSegueWithIdentifier("showDetail", sender: indexPath)
return true
}

In your prepareForSegue method, you have this line,
let photo = Photo()
This creates a new Photo object, it doesn't get the one you were displaying in the cell you selected. You got a reference to the correct one in your if let statement. Use that one (let photo = photos[indexPath.row]).
As for the back button, you shouldn't have to write any code to make that work, if that back button is the one you get automatically when you push to a new controller.

Related

Swift: Show data from tableView to another ViewController (JSON, Alamorife, AlamofireImage)

I'm trying to do an app in which the data were obtained from JSON.
In the picture below you can see the project:
Project
If we click on the photo opens the details page. The problem is because I do not know how to pick up the data shown in the details page. Please help me.
Here is the code
import UIKit
import Alamofire
import AlamofireImage
import SwiftyJSON
class ViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate {
#IBOutlet weak var searchbarValue: UISearchBar!
weak open var delegate: UISearchBarDelegate?
#IBOutlet weak var tableView: UITableView!
var albumArray = [AnyObject]()
var url = ("https://jsonplaceholder.typicode.com/photos")
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.searchbarValue?.delegate = self
Alamofire.request("https://jsonplaceholder.typicode.com/photos").responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar[].arrayObject {
self.albumArray = resData as [AnyObject]; ()
}
if self.albumArray.count > 0 {
self.tableView.reloadData()
}
}
}
}
public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) // called when text ends editing
{
callAlamo(searchTerm: searchbarValue.text!)
}
func callAlamo(searchTerm: String)
{
Alamofire.request("https://jsonplaceholder.typicode.com/photos").responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar[].arrayObject {
self.albumArray = resData as [AnyObject]; ()
}
if self.albumArray.count > 0 {
self.tableView.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return albumArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? CostumTableViewCell
let title = albumArray[indexPath.row]
cell?.titleLabel?.text = title["title"] as? String
//cell?.url?.image = UIImage(data: title as! Data)
let imageUrl = title["thumbnailUrl"] as? String
//print(imageUrl)
let urlRequest = URLRequest(url: URL(string: imageUrl!)!)
Alamofire.request(urlRequest).responseImage { response in
if let image = response.result.value {
// print("image downloaded: \(title["url"])")
cell?.url?.image = image
}
}
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetails", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let indexPath = self.tableView.indexPathForSelectedRow?.row
let vc = segue.destination as! DetailsViewController
//here should be the code
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Also you can see the DetailsViewController code:
import UIKit
class DetailsViewController: UIViewController {
var image2 = UIImage()
var title2 = String()
#IBOutlet var mainImageView: UIImageView!
#IBOutlet var songTitle: UILabel!
override func viewDidLoad() {
songTitle.text = title2
mainImageView.image = image2
}
}
You can easily pass value from tableview to detail view using the below code :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let indexPath = self.tableView.indexPathForSelectedRow
let cell : CostumTableViewCell = self.tableView.cellForRow(at: indexPath!) as! CostumTableViewCell
let vc = segue.destination as! DetailsViewController
vc.image2 = cell.url.image!
vc.title2 = cell.titleLabel.text!
}
class ViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate {
var customArr = [CustomElement]()
var arr = [Any]()
// In viewDidLoad , you can append element to customArr
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var image = customArr[indexpath.row].image
var title = customArr[indexpath.row].title
arr.append(image)
arr.append(title)
performSegue(withIdentifier: "showDetails", sender: arr)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let indexPath = self.tableView.indexPathForSelectedRow?.row
if segue.identifier = "showDetails" {
if let vc = segue.destination as! DetailsViewController {
vc.arr = sender
}
}
//here should be the code
}
}
class DetailsViewController: UIViewController {
var arr = [Any]()
}

Perform Segue from UICollectionViewCell

So I'm creating a blog app, and on the home news feed collection view (imageCollection, loaded from firebase database) I have a button. This button title depends on the Category of the image. What i'm having an issue with is performing the segue in the UICollectionViewCell class. I ran the button action with the print statement, and it worked. But when i try to add performSegue, well it doesn't let me. (Use of unresolved identifier 'performSegue')
Any tips? thank you!
P.S. i'm still fairly new to swift, so if i come off a little ignorant, i apologize
My ViewController
import UIKit
import Firebase
import FirebaseDatabase
import SDWebImage
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var popImageCollection: UICollectionView!
#IBOutlet weak var imageCollection: UICollectionView!
var customImageFlowLayout = CustomImageFlowLayout()
var popImageFlowLayout = PopImagesFlowLayout()
var images = [BlogInsta]()
var popImageArray = [UIImage]()
var homePageTextArray = [NewsTextModel]()
var dbRef: DatabaseReference!
var dbPopularRef: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
dbRef = Database.database().reference().child("images")
dbPopularRef = Database.database().reference().child("popular")
loadDB()
loadImages()
loadText()
customImageFlowLayout = CustomImageFlowLayout()
popImageFlowLayout = PopImagesFlowLayout()
imageCollection.backgroundColor = .white
popImageCollection.backgroundColor = .white
// Do any additional setup after loading the view, typically from a nib.
}
func loadText() {
dbRef.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
self.homePageTextArray.removeAll()
for homeText in snapshot.children.allObjects as! [DataSnapshot] {
let homeTextObject = homeText.value as? [String: AnyObject]
let titleHome = homeTextObject?["title"]
let categoryButtonText = homeTextObject?["category"]
self.imageCollection.reloadData()
let homeLabels = NewsTextModel(title: titleHome as! String?, buttonText: categoryButtonText as! String?)
self.homePageTextArray.append(homeLabels)
}
}
})
}
func loadImages() {
popImageArray.append(UIImage(named: "2")!)
popImageArray.append(UIImage(named: "3")!)
popImageArray.append(UIImage(named: "4")!)
self.popImageCollection.reloadData()
}
func loadDB() {
dbRef.observe(DataEventType.value, with: { (snapshot) in
var newImages = [BlogInsta]()
for BlogInstaSnapshot in snapshot.children {
let blogInstaObject = BlogInsta(snapshot: BlogInstaSnapshot as! DataSnapshot)
newImages.append(blogInstaObject)
}
self.images = newImages
self.imageCollection.reloadData()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.imageCollection {
return images.count
} else {
return popImageArray.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.imageCollection {
let cell = imageCollection.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ImageCollectionViewCell
let image = images[indexPath.row]
let text: NewsTextModel
text = homePageTextArray[indexPath.row]
cell.categoryButton.setTitle(text.buttonText, for: .normal)
cell.newTitleLabel.text = text.title
cell.imageView.sd_setImage(with: URL(string: image.url), placeholderImage: UIImage(named: "1"))
return cell
} else {
let cellB = popImageCollection.dequeueReusableCell(withReuseIdentifier: "popCell", for: indexPath) as! PopularCollectionViewCell
let popPhotos = popImageArray[indexPath.row]
cellB.popularImageView.image = popPhotos
cellB.popularImageView.frame.size.width = view.frame.size.width
return cellB
}
}
}
My ImageCollectionViewCell.swift
import UIKit
import Foundation
class ImageCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var categoryButton: UIButton!
#IBOutlet weak var newTitleLabel: UILabel!
#IBOutlet weak var imageView: UIImageView!
#IBAction func categoryButtonAction(_ sender: Any) {
if categoryButton.currentTitle == "Fashion" {
print("Fashion Button Clicked")
performSegue(withIdentifier: "homeToFashion", sender: self)
}
}
override func prepareForReuse() {
super.prepareForReuse()
self.imageView.image = nil
}
}
You need a custom delegate. Do this:
protocol MyCellDelegate {
func cellWasPressed()
}
// Your cell
class ImageCollectionViewCell: UICollectionViewCell {
var delegate: MyCellDelegate?
#IBAction func categoryButtonAction(_ sender: Any) {
if categoryButton.currentTitle == "Fashion" {
print("Fashion Button Clicked")
self.delegate?.cellWasPressed()
}
}
}
// Your viewcontroller must conform to the delegate
class ViewController: MyCellDelegate {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.imageCollection {
let cell = imageCollection.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ImageCollectionViewCell
// set the delegate
cell.delegate = self
// ...... rest of your cellForRowAtIndexPath
}
// Still in your VC, implement the delegate method
func cellWasPressed() {
performSegue(withIdentifier: "homeToFashion", sender: self)
}
}
You should use your own delegate. It is already described here
performSegue(withIdentifier:sender:) won't work from cell because it is UIViewController metod.
also you can make use of closure

iOS master detail sec

I'm working on a Contacts app which I'm developing using swift. I recently implemented sections and now the detail controller is not working properly. Whenever I click on a contact, it shows details for some other contact. I think the main problem is in prepareforsegue function but I can't figure it out. Help Pls!
//
// ContactListViewController.swift
// TechOriginators
//
// Created by Xcode User on 2017-10-09.
// Copyright © 2017 Xcode User. All rights reserved.
//
import UIKit
import Foundation
class ContactListViewController : UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchResultsUpdating {
#IBOutlet var contactsTableView : UITableView!
var contactViewController: ContactViewController? = nil
var contacts : [Contact] = [] {
didSet{
self.contactsTableView.reloadData()
}
}
//Variables to implement sections in UITableView
var sectionLetters: [Character] = []
var contactsDict = [Character: [String]]()
var contactsName = [String]()
//Search Controller
let searchController = UISearchController(searchResultsController: nil)
//Variable to store filtered contacts through search
var filteredContacts = [Contact]()
//Function to show details of a contact record
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail"{
if let indexPath = contactsTableView.indexPathForSelectedRow{
let object = contacts[indexPath.row]
//let object = contactsDict[sectionLetters[indexPath.section]]![indexPath.row]
let controller = segue.destination as! ContactViewController
controller.detailItem = object
}
}
}
func createDict(){
contactsName = contacts.map{$0.FirstName}
//print(contactsName)
sectionLetters = contactsName.map{ (firstName) -> Character in
return firstName[firstName.startIndex]
}
sectionLetters.sorted()
sectionLetters = sectionLetters.reduce([], { (list, firstName) -> [Character] in
if !list.contains(firstName){
return list + [firstName]
}
return list
})
for entry in contactsName{
if contactsDict[entry[entry.startIndex]] == nil {
contactsDict[entry[entry.startIndex]] = [String]()
}
contactsDict[entry[entry.startIndex]]!.append(entry)
}
for (letter, list) in contactsDict{
contactsDict[letter] = list.sorted()
}
print(sectionLetters)
print(contactsDict)
}
// //Function to load contacts
func loadContacts()
{
self.contacts = getContacts()
}
/*private let session: URLSession = .shared
func loadContacts()
{
//let url = URL(string: "http://127.0.0.1:8080/api/Contacts")!
let url = URL(string: "http://10.16.48.237/api/Contacts")!
let task = session.dataTask(with: url) { (data, response, error) in
print("dataRecieved \(data)")
print("error \(error)")
print ("response \(response)")
guard let data = data else { return }
do {
self.contacts = try parse(data)
}
catch {
print("JSONParsing Error: \(error)")
}
}
task.resume() // firing the task
}*/
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 44
}
func numberOfSections(in tableView: UITableView) -> Int {
return sectionLetters.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if self.searchController.isActive {
return nil
} else {
return String(sectionLetters[section])
}
//return String(sectionLetters[section])
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive && searchController.searchBar.text != "" {
return filteredContacts.count
}
//return self.contacts.count
print(contactsDict[sectionLetters[section]]!.count)
return contactsDict[sectionLetters[section]]!.count
}
//Function to display cells in UITableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell" , for : indexPath)
let contact = contacts[indexPath.row]
let object: Contact
if searchController.isActive && searchController.searchBar.text != ""{
let contact = self.filteredContacts[indexPath.row]
cell.textLabel?.text = contact.FirstName + " " + contact.LastName
}else{
//contact = contactsDict[sectionLetters[indexPath.section]]![indexPath.row]
//cell.textLabel?.text = contact.FirstName + " " + contact.LastName
cell.textLabel?.text = contactsDict[sectionLetters[indexPath.section]]![indexPath.row]
}
return cell
}
//Function to update search results when the user types in search bar
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchText: searchController.searchBar.text!)
}
func updateSearchResultsForSearchController(searchController: UISearchController){
filterContentForSearchText(searchText: searchController.searchBar.text!)
}
//Function to find matches for text entered in search bar
func filterContentForSearchText(searchText: String){
filteredContacts = contacts.filter{p in
var containsString = false
if p.FirstName.lowercased().contains(searchText.lowercased()){
containsString = true
}else if p.LastName.lowercased().contains(searchText.lowercased()){
containsString = true
}else if p.Division.lowercased().contains(searchText.lowercased()){
containsString = true
}else if p.Department.lowercased().contains(searchText.lowercased()){
containsString = true
}else if p.BusinessNumber.lowercased().contains(searchText.lowercased()){
containsString = true
}else if p.HomePhone.lowercased().contains(searchText.lowercased()){
containsString = true
}else if p.CellularPhone.lowercased().contains(searchText.lowercased()){
containsString = true
}else if p.Role.lowercased().contains(searchText.lowercased()){
containsString = true
}
return containsString
}
contactsTableView.reloadData()
}
//Function to sorts contacts by First Name
func sortContacts() {
contacts.sort() { $0.FirstName < $1.FirstName }
contactsTableView.reloadData();
}
override func viewDidLoad() {
super.viewDidLoad()
loadContacts()
sortContacts()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
self.definesPresentationContext = true
createDict()
contactsTableView.tableHeaderView = searchController.searchBar
//contactsTableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: false)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
//
// ContactViewController.swift
// TechOriginators
//
// Created by Xcode User on 2017-10-09.
// Copyright © 2017 Xcode User. All rights reserved.
//
import UIKit
class ContactViewController: UIViewController {
//#IBOutlet weak var detailDescriptionLabel: UILabel!
#IBOutlet weak var firstNameLabel: UILabel?
#IBOutlet weak var lastNameLabel: UILabel?
#IBOutlet weak var divisionLabel: UILabel?
#IBOutlet weak var departmentLabel: UILabel?
#IBOutlet weak var businessPhoneButton: UIButton?
#IBOutlet weak var homePhoneButton: UIButton?
#IBOutlet weak var cellularPhoneButton: UIButton?
#IBOutlet weak var roleLabel: UILabel?
/*#IBOutlet weak var firstNameLabel: UILabel?
#IBOutlet weak var lastNameLabel: UILabel?
#IBOutlet weak var phoneButton: UIButton?
#IBOutlet weak var emailButton: UIButton?*/
func configureView() {
// Update the user interface for the detail item.
if let detail = self.detailItem {
self.title = detail.FirstName + " " + detail.LastName
firstNameLabel?.text = detail.FirstName
lastNameLabel?.text = detail.LastName
divisionLabel?.text = detail.Division
departmentLabel?.text = detail.Department
businessPhoneButton?.setTitle(detail.BusinessNumber, for: .normal)
homePhoneButton?.setTitle(detail.HomePhone, for: .normal)
cellularPhoneButton?.setTitle(detail.CellularPhone, for: .normal)
roleLabel?.text = detail.Role
}
}
#IBAction func businessPhoneButtonPressed(sender: UIButton){
if let bPhone = detailItem?.BusinessNumber{
if let url = URL(string: "tel://\(bPhone)"), UIApplication.shared.canOpenURL(url){
if #available(iOS 10, *){
UIApplication.shared.open(url)
}else{
UIApplication.shared.openURL(url)
}
}
}
}
#IBAction func homePhoneButtonPressed(sender: UIButton){
if let hPhone = detailItem?.HomePhone{
if let url = URL(string: "tel://\(hPhone)"), UIApplication.shared.canOpenURL(url){
if #available(iOS 10, *){
UIApplication.shared.open(url)
}else{
UIApplication.shared.openURL(url)
}
}
}
}
#IBAction func cellularPhoneButtonPressed(sender: UIButton){
if let cPhone = detailItem?.CellularPhone{
/*if let url = NSURL(string: "tel://\(phone)"){
UIApplication.shared.open(url as URL, options: [:], completionHandler: nil)
}*/
if let url = URL(string: "tel://\(cPhone)"), UIApplication.shared.canOpenURL(url) {
if #available(iOS 10, *) {
UIApplication.shared.open(url)
} else {
UIApplication.shared.openURL(url)
}
}
}
}
/*#IBAction func emailButtonPressed(sender: UIButton){
if let email = detailItem?.email{
if let url = URL(string:"mailto:\(email)"){
UIApplication.shared.open(url as URL)
}
}
}*/
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
configureView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var detailItem: Contact? {
didSet {
// Update the view.
self.configureView()
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I suggest using the same data structure for all your table operations to be consistent. Try populating your dictionary as:
var contactsDict = [Character: [Contact]]()
That way, you can always use the section to find the right array and the row to find the right offset in the array.
Separating the names from the contacts in the table's data is inviting them to get out of synch.

Swift asynch load image from directory

I have a situation where I am filling out a form and set image from library then storing in a directory.
I am able to get image from the directory but tableview list flickering in the middle of scroll, so I am working with method of dispatch_async but not able to do.
If anybody has a solution, please let me know.
Here is my code.
import UIKit
func getDocumentsURL() -> NSURL {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
return documentsURL
}
func fileInDocumentsDirectory(filename: String) -> String {
let fileURL = getDocumentsURL().URLByAppendingPathComponent(filename)
return fileURL.path!
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
var marrEmpData : NSMutableArray!
#IBOutlet var MyTable: UITableView!
#IBAction func addEmpInfo(){
}
override func viewWillAppear(animated: Bool) {
self.getStudentData()
}
func getStudentData()
{
marrEmpData = NSMutableArray()
marrEmpData = ModelManager.getInstance().getAllStudentData()
MyTable.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
MyTable.delegate = self
MyTable.dataSource = self
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
//return names.count;
return marrEmpData.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CustomCell = self.MyTable.dequeueReusableCellWithIdentifier("cells") as! CustomCell
let emp:EmpInfo = marrEmpData.objectAtIndex(indexPath.row) as! EmpInfo
let randomNum = "Image-\(indexPath.row+1).png"
let imagePathOne = fileInDocumentsDirectory(randomNum)
dispatch_async(dispatch_get_main_queue()) {
if let loadedImage = self.loadImageFromPath(imagePathOne) {
print("view Loaded Image: \(loadedImage)")
cell.photo.image = loadedImage
}
else {
cell.photo.image = UIImage(named: "default_user")
}
}
// user profile
cell.name.text = emp.full_name
cell.age.text = emp.age
cell.phone.text = emp.phone
return cell
}
// which one is tapped
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You tapped cell number \(indexPath.row).")
}
// load image of user
func loadImageFromPath(path: String) -> UIImage? {
print("image-----\(path)")
let image = UIImage(contentsOfFile: path)
if image == nil {
print("missing image at: \(path)")
}
print("Loading image from path: \(path)") // this is just for you to see the path in case you want to go to the directory, using Finder.
return image
}
}
You are using dispatch_async but you are dispatching to the main queue which is the thread you are already on. You should dispatch to a background thread, load the image, then dispatch to the main thread and update the UI.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0))
{
let image = self.loadImageFromPath(imagePathOne)
dispatch_async(dispatch_get_main_queue())
{
if let loadedImage = image
{
print("view Loaded Image: \(loadedImage)")
cell.photo.image = loadedImage
}
else
{
cell.photo.image = UIImage(named: "default_user")
}
}
}

App not deleting a row from .plist file

In a project that I am working on I have note-esque function that is acting as an Exercise/Training Log. This Training Log is made up of 5 files: Note.swift, NotesTableViewController.swift, NoteDetailViewController.swift, NoteDetailTableViewCell.swift, and NoteStore.swift. The class for this table is NotesTableViewController, which is a UIViewController with UITableViewDelegate, and UITableViewDataSource. This note taking feature works decently, populating the tableview, but fails to delete a note from the .plist file and continues to retrieve it when the app is reopened. I do not know if this is actually failure to save/load, or if something is going wrong somewhere else. I would appreciate any help at all. The files are as follows:
Note.swift
import Foundation
class Note : NSObject, NSCoding {
var title = ""
var text = ""
var date = NSDate() // Defaults to current date / time
// Computed property to return date as a string
var shortDate : NSString {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM/dd/yy"
return dateFormatter.stringFromDate(self.date)
}
override init() {
super.init()
}
// 1: Encode ourselves...
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(title, forKey: "title")
aCoder.encodeObject(text, forKey: "text")
aCoder.encodeObject(date, forKey: "date")
}
// 2: Decode ourselves on init
required init(coder aDecoder: NSCoder) {
self.title = aDecoder.decodeObjectForKey("title") as! String
self.text = aDecoder.decodeObjectForKey("text") as! String
self.date = aDecoder.decodeObjectForKey("date") as! NSDate
}
}
NotesTableViewController.swift
import UIKit
class NotesTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var OpenButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
// Leverage the built in TableViewController Edit button
self.navigationItem.leftBarButtonItem = self.editButtonItem()
OpenButton.target = self.revealViewController()
OpenButton.action = Selector("revealToggle:")
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// ensure we are not in edit mode
editing = false
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Here we pass the note they tapped on between the view controllers
if segue.identifier == "NoteDetailPush" {
// Get the controller we are going to
var noteDetail = segue.destinationViewController as! NoteDetailViewController
// Lookup the data we want to pass
var theCell = sender as! NoteDetailTableViewCell
// Pass the data forward
noteDetail.theNote = theCell.theNote
}
}
#IBAction func saveFromNoteDetail(segue:UIStoryboardSegue) {
// We come here from an exit segue when they hit save on the detail screen
// Get the controller we are coming from
var noteDetail = segue.sourceViewController as! NoteDetailViewController
// If there is a row selected....
if let indexPath = tableView.indexPathForSelectedRow() {
// Update note in our store
NoteStore.sharedNoteStore.updateNote(theNote: noteDetail.theNote)
// The user was in edit mode
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
} else {
// Otherwise, add a new record
NoteStore.sharedNoteStore.createNote(theNote: noteDetail.theNote)
// Get an index to insert the row at
var indexPath = NSIndexPath(forRow: NoteStore.sharedNoteStore.count()-1, inSection: 0)
// Update tableview
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
// MARK: - Table view data source
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Just return the note count
return NoteStore.sharedNoteStore.count()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Fetch a reusable cell
let cell = tableView.dequeueReusableCellWithIdentifier("NoteDetailTableViewCell", forIndexPath: indexPath) as! NoteDetailTableViewCell
// Fetch the note
var rowNumber = indexPath.row
var theNote = NoteStore.sharedNoteStore.getNote(rowNumber)
// Configure the cell
cell.setupCell(theNote)
return cell
}
// Override to support editing the table view.
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
NoteStore.sharedNoteStore.deleteNote(indexPath.row)
// Delete the note from the tableview
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
}
NoteDetailViewController
import UIKit
class NoteDetailViewController: UIViewController {
var theNote = Note()
#IBOutlet weak var noteTitleLabel: UITextField!
#IBOutlet weak var noteTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// The view starts here. By now we either have a note to edit
// or we have a blank note in theNote property we can use
// Update the screen with the contents of theNote
self.noteTitleLabel.text = theNote.title
self.noteTextView.text = theNote.text
// Set the Cursor in the note text area
self.noteTextView.becomeFirstResponder()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Whenever we leave the screen, update our note model
theNote.title = self.noteTitleLabel.text
theNote.text = self.noteTextView.text
}
#IBAction func CancelNote(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
NoteDetailTableViewCell
import UIKit
class NoteDetailTableViewCell : UITableViewCell {
// The note currently being shown
weak var theNote : Note!
// Interface builder outlets
#IBOutlet weak var noteTitleLabel : UILabel!
#IBOutlet weak var noteDateLabel : UILabel!
#IBOutlet weak var noteTextLabel : UILabel!
// Insert note contents into the cell
func setupCell(theNote:Note) {
// Save a weak reference to the note
self.theNote = theNote
// Update the cell
noteTitleLabel.text = theNote.title
noteTextLabel.text = theNote.text
noteDateLabel.text = theNote.shortDate as String
}
}
and finally, NoteStore
import Foundation
class NoteStore {
// Mark: Singleton Pattern (hacked since we don't have class var's yet)
class var sharedNoteStore : NoteStore {
struct Static {
static let instance : NoteStore = NoteStore()
}
return Static.instance
}
// Private init to force usage of singleton
private init() {
load()
}
// Array to hold our notes
private var notes : [Note]!
// CRUD - Create, Read, Update, Delete
// Create
func createNote(theNote:Note = Note()) -> Note {
notes.append(theNote)
return theNote
}
// Read
func getNote(index:Int) -> Note {
return notes[index]
}
// Update
func updateNote(#theNote:Note) {
// Notes passed by reference, no update code needed
}
// Delete
func deleteNote(index:Int) {
notes.removeAtIndex(index)
}
func deleteNote(withNote:Note) {
for (i, note) in enumerate(notes) {
if note === withNote {
notes.removeAtIndex(i)
return
}
}
}
// Count
func count() -> Int {
return notes.count
}
// Mark: Persistence
// 1: Find the file & directory we want to save to...
func archiveFilePath() -> String {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentsDirectory = paths.first as! NSString
let path = documentsDirectory.stringByAppendingPathComponent("NoteStore.plist")
return path
}
// 2: Do the save to disk.....
func save() {
NSKeyedArchiver.archiveRootObject(notes, toFile: archiveFilePath())
}
// 3: Do the reload from disk....
func load() {
let filePath = archiveFilePath()
let fileManager = NSFileManager.defaultManager()
if fileManager.fileExistsAtPath(filePath) {
notes = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as! [Note]
} else {
notes = [Note]()
}
}
}
it look's like you're not calling the save method after changing creating,deleting or updating notes
you could add for example :
func deleteNote(index:Int) {
notes.removeAtIndex(index)
save()
}
or call the save methods on vievWillDisappear if you don't want to write a new plist after every change

Resources