UIActivityViewController - No share options - Swift 5 - ios

I have implemented a UIActivityViewController in Swift 5 via a nav bar button and upon click this should share the contents of an array called sharedData.
When clicking the button, the share options popup is empty.
Here is my func code:
#IBAction func shareNavButton(_ sender: UIBarButtonItem) {
let shareItems: [Any] = [sharedData]
let activityVC = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
activityVC.popoverPresentationController?.barButtonItem = sender
self.present(activityVC, animated: true, completion: nil)
}
Here is my entire VC code:
class FavoritesViewController: UIViewController {
#IBOutlet var tableView: UITableView!
#IBAction func shareNavButton(_ sender: UIBarButtonItem) {
let shareItems: [Any] = [sharedData]
let activityVC = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
activityVC.popoverPresentationController?.barButtonItem = sender
self.present(activityVC, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
convertArray()
}
}
extension FavoritesViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sharedData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.text = sharedData[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath)
-> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "Remove") { (_, _, completionHandler) in
sharedData.remove(at: indexPath.row)
saveArray()
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
completionHandler(true)
}
if #available(iOS 13.0, *) {
deleteAction.image = UIImage(systemName: "trash")
} else {
// Fallback to default action
}
deleteAction.backgroundColor = .systemRed
let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
return configuration
}
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let copyAction = UIContextualAction(style: .normal, title: "Copy") { (_, _, completionHandler) in
let cell = tableView.cellForRow(at: indexPath)
UIPasteboard.general.string = cell?.textLabel?.text
completionHandler(true)
}
if #available(iOS 13.0, *) {
copyAction.image = UIImage(systemName: "doc.on.clipboard")
} else {
// fall back to default action
}
copyAction.backgroundColor = .systemBlue
let configuration = UISwipeActionsConfiguration(actions: [copyAction])
return configuration
}
}

You are using an array of string inside another array. Your sharedData is an array.
let shareItems: [Any] = [sharedData]
Try with this code:
let shareItems: [Any] = sharedData

Related

How to delete a cell in a section using custom action?

ive been struggling too delete my cells in the tableview using trailingSwipeActionsConfigurationForRowAt I have it set up to where you swipe the cell the delete image shows up and an alertView shows when the delete image is pressed and asks if you want to delete the row. But once yes is pressed the cell doesn't get deleted/removed
import UIKit
class CartViewController: UIViewController {
var selectedProduct: ItemList! // allows data to be passed into the CartVC
// allows data to be sepearted into sections
var cartItems: [CartItem] = []
var groupedItems: [String: [CartItem]] = [:]
var brandTitle: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
groupedItems = Dictionary(grouping: cartItems, by: {$0.itemList.brandName})
brandTitle = groupedItems.map{$0.key}.sorted()
}
}
extension CartViewController: UITableViewDelegate, UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return brandTitle.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let brand = brandTitle[section]
return groupedItems[brand]!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cartCell = tableView.dequeueReusableCell(withIdentifier: "CartCell") as! CartCell
let brand = brandTitle[indexPath.section]
let itemsToDisplay = groupedItems[brand]![indexPath.row]
cartCell.configure(withCartItems: itemsToDisplay.productList)
return cartCell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cartHeader = tableView.dequeueReusableCell(withIdentifier: "CartHeader") as! CartHeader
let headerTitle = brandTitle[section]
cartHeader.brandName.text = "Brand: \(headerTitle)"
return cartHeader
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 45
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let cartFooter = tableView.dequeueReusableCell(withIdentifier: "CartFooter") as! CartFooter
let brand = brandTitle[section]
let subtotal = groupedItems[brand]?.map { $0.getCartTotal() }.reduce(0, +) ?? 0
cartFooter.cartTotal.text = String(subtotal)
return cartFooter
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let edit = UIContextualAction(style: .normal, title: "") { (action, view, nil) in
let refreshAlert = UIAlertController(title: "Deletion", message: "Are you sure you want to remove this item from cart? ", preferredStyle: .alert)
refreshAlert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (action: UIAlertAction!) in
}))
refreshAlert.addAction(UIAlertAction(title: "No", style: .default, handler: { (action: UIAlertAction!) in
refreshAlert .dismiss(animated: true, completion: nil)
}))
self.present(refreshAlert, animated: true, completion: nil)
}
edit.backgroundColor = .red
edit.image = #imageLiteral(resourceName: "best_sellers")
let config = UISwipeActionsConfiguration(actions: [edit])
config.performsFirstActionWithFullSwipe = false
return config
}
}
iOS checks the number of rows before and after a delete operation, and expects them to add up correctly following the change.In your alert action you need to delete your current object.
refreshAlert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (action: UIAlertAction!) in
tableView.beginUpdates()
// brandTitle[indexPath.section].remove(at: indexPath.row) // or you can directly use this
let brand = brandTitle[indexPath.section]
groupedItems[brand]!.remove(at:indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.endUpdates()
}))

UITableView swipe-to-close in iOS 11

I have some trouble with UITableView swipe-to-close gesture to close actions in a cell. When I open cell actions it works fine, but when I'm trying to close it, it will close only after several trying.
I've tested it in a clean project and noticed that it is iOS 11 behaviour because in the previous iOS version these actions are closing by any touch outside action buttons zone.
How can I do the same behaviour?
Video: https://www.youtube.com/watch?v=rKrk7q0HkeY
Code. Just for a case
class ViewController: UIViewController {
let tableView = UITableView(frame: .zero, style: .plain)
var data = (0...3)
.map { _ in
return (0...5).map { _ in return false }
}
override func loadView() {
super.loadView()
view = tableView
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data[section].count
}
func numberOfSections(in tableView: UITableView) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell.init(style: .default, reuseIdentifier: "cell")
let isSelected = data[indexPath.section][indexPath.row]
cell.textLabel?.text = "Section: \(indexPath.section) \nRow: \(indexPath.row)"
cell.textLabel?.numberOfLines = 0
cell.accessoryType = isSelected ? .checkmark : .none
return cell
}
#available(iOS 11.0, *)
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let action = UIContextualAction(style: .normal, title: "Toggle 2") { [weak self] (action, view, handler) in
guard let data = self?.data else { return }
let isSelected = data[indexPath.section][indexPath.row]
self?.data[indexPath.section][indexPath.row] = !isSelected
self?.tableView.reloadRows(at: [indexPath], with: .fade)
handler(true)
}
action.backgroundColor = .blue
let configuration = UISwipeActionsConfiguration(actions: indexPath.section == 0 ? [action] : [])
configuration.performsFirstActionWithFullSwipe = true
return configuration
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let action = UITableViewRowAction(style: .default,
title: "Toggle") { [weak self] (action, indexPath) in
guard let data = self?.data else { return }
let isSelected = data[indexPath.section][indexPath.row]
self?.data[indexPath.section][indexPath.row] = !isSelected
self?.tableView.reloadRows(at: [indexPath], with: .fade)
}
return [action]
}
}
I've found the solution. Swipe-to-close works well in iOS 11 if you add leading actions.
#available(iOS 11.0, *)
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let action = UIContextualAction(style: .normal, title: "Toggle 2") { [weak self] (action, view, handler) in
guard let data = self?.data else { return }
let isSelected = data[indexPath.section][indexPath.row]
self?.data[indexPath.section][indexPath.row] = !isSelected
self?.tableView.reloadRows(at: [indexPath], with: .fade)
handler(true)
}
action.backgroundColor = .blue
let configuration = UISwipeActionsConfiguration(actions: indexPath.section == 0 ? [action] : [])
configuration.performsFirstActionWithFullSwipe = true
return configuration
}
I've updated question source if someone interested in full code

call by touching cell in Swift

i used Core Date to save names and phone numbers
i would like to make a call by touching cell
here is my code:
import UIKit
import CoreData
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var people = [Person]()
override func viewDidLoad() {
super.viewDidLoad()
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
do {
let people = try PersistenceService.context.fetch(fetchRequest)
self.people = people
self.tableView.reloadData()
} catch {}
}
#IBAction func onPlusTapped() {
let alert = UIAlertController(title: "Add Person", message: nil, preferredStyle: .alert)
alert.addTextField { (textField) in
textField.placeholder = "Name"
}
alert.addTextField { (textField) in
textField.placeholder = "Phone number"
textField.keyboardType = .numberPad
}
let action = UIAlertAction(title: "Post", style: .default) { (_) in
let name = alert.textFields!.first!.text!
let phoneNumber = alert.textFields!.last!.text!
let person = Person(context: PersistenceService.context)
person.name = name
person.phoneNumber = phoneNumber
PersistenceService.saveContext()
self.people.append(person)
self.tableView.reloadData()
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
override var prefersStatusBarHidden: Bool {
return true
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
cell.textLabel?.text = people[indexPath.row].name
cell.detailTextLabel?.text = people[indexPath.row].phoneNumber
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
people.remove(at: indexPath.item)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UIApplication.shared.openURL(NSURL(string: "tel://" + (people[indexPath.row].phoneNumber?.description)!)! as URL)
print(people[indexPath.row].phoneNumber?.description)
}
}
No need to add an #IBAction, you can use didSelectRow from UITableViewDelegate
You are already implementing the didSelectRowAt use IBActions only inside UITableView if you have a UIButton inside the UITableViewCell

Xcode can't find my cell identifier

I'm getting an error with my code that says...
"Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'unable to dequeue a cell
with identifier ItemCell - must register a nib or a class for the
identifier or connect a prototype cell in a storyboard'?"
I've set the cell to ItemCell and I've run Product > Clean. It still can't find it. Can anyone see what I've done wrong? I've included a screen shot of my code and my storyboard.
import UIKit
class ItemsViewController: UITableViewController {
var itemStore: ItemStore!
#IBAction func addNewItem(_ sender: UIButton) {
let newItem = itemStore.createItem()
if let index = itemStore.allItems.index(of: newItem) {
let indexPath = IndexPath(row: index, section: 0)
tableView.insertRows(at: [indexPath], with: .automatic)
}
}
#IBAction func toggleEditingMode(_ sender: UIButton) {
if isEditing {
sender.setTitle("Edit", for: .normal)
setEditing(false, animated: true)
} else {
sender.setTitle("Done", for: .normal)
setEditing(true, animated: true)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let statusBarHeight = UIApplication.shared.statusBarFrame.height
let insets = UIEdgeInsetsMake(statusBarHeight, 0, 0, 0)
tableView.contentInset = insets
tableView.scrollIndicatorInsets = insets
tableView.rowHeight = 65
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemStore.allItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! ItemCell
let item = itemStore.allItems[indexPath.row]
cell.nameLabel.text = item.name
cell.serialNumberLabel.text = item.serialNumber
cell.valueLabel.text = "$\(item.valueInDollars)"
return cell
}
override func tableView(_ tableView: UITableView,
commit editingStyle: UITableViewCellEditingStyle,
forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let item = itemStore.allItems[indexPath.row]
let title = "Delete \(item.name)"
let message = "You sure ya wanna delete this?"
let ac = UIAlertController(title: title,
message: message,
preferredStyle: .actionSheet)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
ac.addAction(cancelAction)
let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: { (action) -> Void in
self.itemStore.removeItem(item)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
})
ac.addAction(deleteAction)
present(ac, animated: true, completion: nil)
}
}
override func tableView(_ tableView: UITableView,
moveRowAt sourceIndexPath: IndexPath,
to destinationIndexPath: IndexPath) {
itemStore.moveItem(from: sourceIndexPath.row, to: destinationIndexPath.row)
}
This is my ItemCell
import UIKit
class ItemCell: UITableViewCell {
#IBOutlet var nameLabel: UILabel!
#IBOutlet var serialNumberLabel: UILabel!
#IBOutlet var valueLabel: UILabel!
}
And my ItemStore
import UIKit
class ItemStore {
var allItems = [Item] ()
#discardableResult func createItem() -> Item {
let newItem = Item(random: true)
allItems.append(newItem)
return newItem
}
func removeItem(_ item: Item) {
if let index = allItems.index(of: item) {
allItems.remove(at: index)
}
}
func moveItem(from fromIndex: Int, to toIndex: Int) {
if fromIndex == toIndex {
return
}
let movedItem = allItems[fromIndex]
allItems.remove(at: fromIndex)
allItems.insert(movedItem, at: toIndex)
}
}
You have to check your custom class name in storyboard as ItemCell.swift as your class name.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:ItemCell = tableView.dequeueReusableCell(withIdentifier: "ItemCell") as! ItemCell
let item = itemStore.allItems[indexPath.row]
cell.nameLabel.text = item.name
cell.serialNumberLabel.text = item.serialNumber
cell.valueLabel.text = "$\(item.valueInDollars)"
return cell
}
Check if you have set the correct custom class for the cell (ItemCell in this case) in Identity Inspector.

How to slide out the side menu bar? in Swift

How to slide out the side menu bar when I swipe to the left? and how will it automatically slide out when I click the collectionView? I'm using tableView and constraints, my outlet to that constraint is rightMargin.
import UIKit
import ViewPagerController
class OptionTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var rightMargin: NSLayoutConstraint!
#IBOutlet weak var optionTableView: UITableView!
let option = ["LOG IN","SIGN UP","EDIT PROFILE", "LOG OUT"]
let storyBoardId = ["LogInViewController","SignUpViewController","UserEditPageViewController", "None"]
override func viewDidLoad() {
super.viewDidLoad()
var appearance = ViewPagerControllerAppearance()
appearance.tabMenuAppearance.backgroundColor = UIColor.black
let screenWidth = UIScreen.main.bounds.width
rightMargin.constant = screenWidth
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.rightMargin.constant = 100
UIView.animate(withDuration: 0.3 , animations: {
self.view.layoutIfNeeded()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return option.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = optionTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = option[indexPath.row]
cell.textLabel?.font = UIFont(name: "Arial", size: 10)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let logInViewController = storyboard?.instantiateViewController(withIdentifier: storyBoardId[indexPath.row]) {
navigationController?.pushViewController(logInViewController, animated: true)
}
}
}
SlideInMenu
These will let you slide to open up your own options.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, editActionsForRowAt: IndexPath) -> [UITableViewRowAction]? {
let more = UITableViewRowAction(style: .normal, title: "More") { action, index in
print("more button tapped")
}
more.backgroundColor = .lightGray
let favorite = UITableViewRowAction(style: .normal, title: "Favorite") { action, index in
print("favorite button tapped")
}
favorite.backgroundColor = .orange
let share = UITableViewRowAction(style: .normal, title: "Share") { action, index in
print("share button tapped")
}
share.backgroundColor = .blue
return [share, favorite, more]
}

Resources