I have a UICollectionView where the label (recordingTitle) for each cell is created using the input from a UIAlert. However, at the moment any new input from the alert overrides the labels of all previously created cells in the UICollectionView. How can I have each cell have an individual label?
import UIKit
import AVFoundation
class ViewController: UIViewController, AVAudioRecorderDelegate, UICollectionViewDelegate, UICollectionViewDataSource {
var audioRecorder: AVAudioRecorder?=nil
var audioPlayer: AVAudioPlayer!
var numberOfRecordings = 0
var recordingTitle: String = ""
var fileURL: URL!
#IBOutlet weak var recordButton: UIButton!
#IBOutlet weak var editButton: UIButton!
#IBOutlet weak var myCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
recordButton.layer.cornerRadius = 10
editButton.layer.cornerRadius = 10
// Set the numberOfRecordings to be exactly the number of files stored in the File Manager so that they're in sync
let fileManager = FileManager.default
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
do {
let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
numberOfRecordings = fileURLs.count
} catch {
print("Error while enumerating files \(documentsURL.path): \(error.localizedDescription)")
}
}
// Get the directory where we're going to store the recordings
func getDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentDirectory = paths[0]
return documentDirectory
}
// Create a general alert to display error messages
func displayAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
// Create an alert to ask for recording title input
func askForRecordingTitle() {
let alertController = UIAlertController(title: "Recording Title", message: nil, preferredStyle: .alert)
alertController.addTextField(configurationHandler: nil)
let submitAction = UIAlertAction(title: "Save", style: .default) { [weak self] (alertAction) in
guard let `self` = self else { return }
if let userResponse = alertController.textFields?[0].text {
self.recordingTitle = userResponse
let fileURL = self.getDirectory().appendingPathComponent("\(self.recordingTitle).m4a")
self.fileURL = fileURL
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
let audioRecorder = try AVAudioRecorder(url: fileURL, settings: settings)
audioRecorder.delegate = self
audioRecorder.record()
self.recordButton.setTitle("Stop Recording", for: .normal)
self.audioRecorder = audioRecorder
} catch {
self.displayAlert(title: "Oops!", message: "Recording Failed")
}
} else {
print("Not nil or a value")
}
}
alertController.addAction(submitAction)
present(alertController, animated: true, completion: nil)
}
//MARK: - Record Button Methods
#IBAction func recordAction(_ sender: Any) {
if audioRecorder == nil {
askForRecordingTitle()
numberOfRecordings += 1
} else {
audioRecorder?.stop()
audioRecorder = nil
UserDefaults.standard.set(numberOfRecordings, forKey: "numberOfRecordings")
recordButton.setTitle("Start Recording", for: .normal)
myCollectionView.reloadData()
}
}
//MARK: - Collection View Setup
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numberOfRecordings
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! RecordingCollectionViewCell
if editButton.titleLabel?.text == "Stop Editing" {
cell.isEditing = true
} else {
cell.isEditing = false
}
cell.recordingLabel.text = recordingTitle
cell.layer.cornerRadius = 10
cell.delegate = self as RecordingCellDelegate
return cell
}
//MARK: - Audio Player
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let path = getDirectory().appendingPathComponent("\(recordingTitle).m4a")
do {
audioPlayer = try AVAudioPlayer(contentsOf: path)
audioPlayer.volume = 1.0
audioPlayer.prepareToPlay()
audioPlayer.play()
} catch let error {
print("Error: \(error.localizedDescription)")
}
}
//MARK: - Edit Button Methods
#IBAction func editButtonTapped(_ sender: Any) {
if editButton.titleLabel?.text == "Edit" {
recordButton.isEnabled = false
editButton.setTitle("Stop Editing", for: .normal)
if let indexPaths = myCollectionView?.indexPathsForVisibleItems {
for indexPath in indexPaths {
if let cell = myCollectionView?.cellForItem(at: indexPath) as? RecordingCollectionViewCell {
cell.isEditing = true
}
}
}
} else {
editButton.setTitle("Edit", for: .normal)
recordButton.isEnabled = true
if let indexPaths = myCollectionView?.indexPathsForVisibleItems {
for indexPath in indexPaths {
if let cell = myCollectionView?.cellForItem(at: indexPath) as? RecordingCollectionViewCell {
cell.isEditing = false
}
}
}
}
}
}
extension ViewController: RecordingCellDelegate {
func delete(cell: RecordingCollectionViewCell) {
if let indexPath = myCollectionView?.indexPath(for: cell) {
// 1. Delete the recording from the File Manager
let fileManager = FileManager.default
fileURL = getDirectory().appendingPathComponent("\(recordingTitle).m4a")
do {
try fileManager.removeItem(at: fileURL)
} catch let error {
print("File not found: \(error.localizedDescription)")
displayAlert(title: "Oops!", message: "File not found: \(error.localizedDescription)")
}
// 2. Update the data model
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
do {
let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
numberOfRecordings = fileURLs.count
} catch {
print("Error while enumerating files \(documentsURL.path): \(error.localizedDescription)")
}
// 3. Delete cell in the collectionView
myCollectionView.deleteItems(at: [indexPath])
}
}
}
You are saving your recordingTitle in a single variable, so every time you write to it you override it, you need to store them as an array.
You initialize your variable with:
var recordingTitles: [String] = []
You add your new title with:
recordingTitles.append(userResponse)
and you read it with:
let recordingTitle = recordingTitles[indexPath.item]
Related
Here I Try MVVM pattern to achieve TableView but for showing alert I face problems , it compiles successfully but not showing alert.
[Result] on the tapped on profile pic which is in tableview cell, I want to show alert
TableViewCell
import Foundation
import UIKit
class ParentsImageCell : UITableViewCell {
weak var myVC : ProfileOfParentsDetailsViewController?
var parentProfileVC = ProfileOfParentsDetailsViewController()
#IBOutlet weak var imageProfile : UIImageView!
var items : ParentProfileViewModelItem? {
didSet {
guard let items = items as? ParentProfileViewModelProfileItem else {
return
}
imageProfile?.image = UIImage(named: items.profileImg)
}
}
static var nib:UINib {
return UINib(nibName: identifier, bundle: nil)
}
static var identifier: String {
return String(describing: self)
}
override func awakeFromNib() {
super.awakeFromNib()
imageProfile?.layer.cornerRadius = 62
imageProfile?.clipsToBounds = true
imageProfile?.contentMode = .scaleAspectFill
imageProfile?.backgroundColor = UIColor.lightGray
//Add Tapped Gesture
imageProfile.isUserInteractionEnabled = true
let gesture = UITapGestureRecognizer(
target: self,
action: #selector(didTappedChangeProfilePic))
gesture.numberOfTapsRequired = 1
gesture.numberOfTouchesRequired = 1
imageProfile.addGestureRecognizer(gesture)
}
#objc private func didTappedChangeProfilePic(){
print("tapped on imageView")
presentPhotoActionSheet()
}
override func prepareForReuse() {
super.prepareForReuse()
imageProfile?.image = nil
}
}
extension ParentsImageCell : UIImagePickerControllerDelegate ,UINavigationControllerDelegate {
func presentPhotoActionSheet(){
let actionSheet = UIAlertController(title: "Profile Picture", message: "How would you write to select a picture", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: nil))
actionSheet.addAction(UIAlertAction(title: "Take Photo", style: .default, handler: {[weak self] _ in
self?.presentCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Choose Photo", style: .default, handler: { [weak self]_ in
self?.presentPhotoPicker()
}))
myVC?.present(actionSheet , animated: true)
}
func presentCamera(){
let vc = UIImagePickerController()
vc.sourceType = .camera
vc.delegate = self
vc.allowsEditing = true
myVC?.present(vc , animated: true)
}
func presentPhotoPicker(){
let vc = UIImagePickerController()
vc.sourceType = .photoLibrary
vc.delegate = self
vc.allowsEditing = true
myVC?.present(vc , animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info:
[UIImagePickerController.InfoKey : Any]) {
guard let selectedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage else {
return
}
self.imageProfile.image = selectedImage
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
ViewModel
class ParentProfileViewModel: NSObject {
var items = [ParentProfileViewModelItem]()
var reloadSections: ((_ section: Int) -> Void)?
override init() {
super.init()
guard let data = dataFromFile("ServerData"),
let profile = Profile(data: data) else {
return
}
// initialization code will go here
if let profile = profile.pictureUrl {
let profileItem = ParentProfileViewModelProfileItem(profileImg: profile)
items.append(profileItem)
}
if let name = profile.fullName {
let nameItem = ParentProfileViewModelNameItem(name: name)
items.append(nameItem)
}
if let email = profile.email {
let emailItem = ParentProfileViewModelEmailItem(email: email)
items.append(emailItem)
}
let coach = profile.coach
if !coach.isEmpty {
let coachItem = ParentProfileViewModelCoachItem(coach: coach)
items.append(coachItem)
}
let candidate = profile.candidate
if !candidate.isEmpty {
let candidateItem = ParentProfileViewModelCandidateItem(candidate: candidate)
items.append(candidateItem)
}
}
}
//MARK:- TableviewDatasource & Delegates
extension ParentProfileViewModel: UITableViewDataSource {
//Number of section
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
//Number of RowInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let item = items[section]
guard item.isCollapsible else {
return item.rowCount
}
if item.isCollapsed {
return 0
} else {
return item.rowCount
}
}
//Cell for row at indexpath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// we will configure the cells here
let item = items[indexPath.section]
switch item.type {
case .profileImg:
let vc = ProfileOfParentsDetailsViewController()
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsImageCell.identifier, for: indexPath) as? ParentsImageCell {
cell.items = item
cell.myVC = vc
return cell
}
case .fullName:
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsFulNameCell.identifier, for: indexPath) as? ParentsFulNameCell {
cell.items = item
return cell
}
case .email:
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsEmailCell.identifier, for: indexPath) as? ParentsEmailCell {
cell.items = item
return cell
}
case .candidate:
if let item = item as? ParentProfileViewModelCandidateItem, let cell = tableView.dequeueReusableCell(withIdentifier: CandidatCell.identifier, for: indexPath) as? CandidatCell {
let candidate = item.candidate[indexPath.row]
cell.item = candidate
return cell
}
case .coach:
if let item = item as? ParentProfileViewModelCoachItem, let cell = tableView.dequeueReusableCell(withIdentifier: ParentCoachCell.identifier, for: indexPath) as? ParentCoachCell {
cell.item = item.coach[indexPath.row]
return cell
}
}
return UITableViewCell()
}
}
ViewController
import Foundation
import UIKit
class ProfileOfParentsDetailsViewController: UIViewController {
let viewModel = ParentProfileViewModel()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
viewModel.reloadSections = { [weak self] (section: Int) in
self?.tableView?.beginUpdates()
self?.tableView?.reloadSections([section], with: .fade)
self?.tableView?.endUpdates()
}
tableView?.dataSource = viewModel
tableView.delegate = viewModel
tableView?.estimatedRowHeight = 250
tableView?.rowHeight = UITableView.automaticDimension
tableView?.register(ParentsImageCell.nib, forCellReuseIdentifier: ParentsImageCell.identifier)
tableView?.register(ParentsEmailCell.nib, forCellReuseIdentifier: ParentsEmailCell.identifier)
tableView?.register(ParentsFulNameCell.nib, forCellReuseIdentifier: ParentsFulNameCell.identifier)
tableView?.register(CandidatCell.nib, forCellReuseIdentifier: CandidatCell.identifier)
tableView?.register(ParentCoachCell.nib, forCellReuseIdentifier: ParentCoachCell.identifier)
tableView?.register(ParentsHeaderView.nib, forHeaderFooterViewReuseIdentifier: ParentsHeaderView.identifier)
}
I try to get called alert Sheet , but I Failed and also comment about my approach towards MVVM
I try to called the tableview data source and delegate in VM
I try to get called alert Sheet , but I Failed and also comment about my approach towards MVVM
I try to called the tableview data source and delegate in VM
I begin to create a chat app and want it to scroll to bottom every time I enter text or image. For text, the code is perfect to execute scrollToRow after tableView reload. However, the image always shows under the keyboard, I tried many ways to solve this issue but still get the same result.
image does not show above the keyboard
image is under keyboard
class ChatTableViewController: UITableViewController, UITextViewDelegate {
private let reuseIdentifier = "reuseIdentifier"
private let chatImageIdentifier = "chatImageCell"
var sSection = 0
var rRow = 0
var commentRef: DatabaseReference!
var firebaseHandle: DatabaseHandle?
var dataSnapshot: DataSnapshot?
let uid = FirebaseHelper.shared.getUserId()
let username = FirebaseHelper.shared.getUsername()
let email = FirebaseHelper.shared.getUserEmail()
var comments = [Comment]()
var intArray: [Int]? {
didSet{
guard let intArray = intArray else {return}
self.sSection = intArray[0]
self.rRow = intArray[1]
commentRef = FirebaseHelper.shared.getCommentRef(firebaseRef: .comments, section: sSection, row: rRow)
comments = []
tableView.reloadData()
observeCommentsFromFirebase {
}
setUpKeyboardObserver()
}
}
func observeCommentsFromFirebase(completion: #escaping (()->Void)) {
firebaseHandle = commentRef.queryOrdered(byChild: "timeStamp").observe(.childAdded, with: { (snapshot) in
self.dataSnapshot = snapshot
if let dictionary = snapshot.value as? [String: Any]{
let comment = Comment(dictionary: dictionary)
self.comments.append(comment)
DispatchQueue.main.async {
self.tableView.reloadData()
self.handleKeyboardDidShow()
completion()
}
}
}, withCancel: nil)
}
func setUpKeyboardObserver() {
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardDidShow), name: UIResponder.keyboardDidShowNotification, object: nil)
}
#objc func handleKeyboardDidShow() {
if comments.count > 0 {
let indexPath = IndexPath(row: comments.count - 1, section: 0)
self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(ChatTableViewCell.self, forCellReuseIdentifier: reuseIdentifier)
tableView.register(ChatImageCell.self, forCellReuseIdentifier: chatImageIdentifier)
tableView.separatorStyle = .none
tableView.allowsSelection = false
tableView.backgroundColor = grayColor
tableView.keyboardDismissMode = .interactive
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(handleDismiss))
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
tableView.alwaysBounceVertical = true
}
#objc func handleDismiss() {
if let firebaseHandle = firebaseHandle {
self.commentRef.removeObserver(withHandle: firebaseHandle)
}
NotificationCenter.default.removeObserver(self)
self.dismiss(animated: true, completion: nil)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let comment = comments[indexPath.row]
if comment.imageUrl != "" {
let cell = tableView.dequeueReusableCell(withIdentifier: chatImageIdentifier, for: indexPath) as! ChatImageCell
if comments.count != 0 {
cell.comment = comment
}
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! ChatTableViewCell
if comments.count != 0 {
cell.comment = comment
}
return cell
}
}
#objc func uploadImage() {
showImageActionSheet()
}
extension ChatTableViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
func showImageActionSheet() {
// declaring action sheet
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
// declaring camera button
let camera = UIAlertAction(title: "Camera", style: .default) { (action) in
// if there is camera, then execute
if UIImagePickerController.isSourceTypeAvailable(.camera) {
self.showPicker(with: .camera)
}
}
// declaring library button
let library = UIAlertAction(title: "Library", style: .default) { (action) in
// if there is library, then execute
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
self.showPicker(with: .photoLibrary)
}
}
// declaring cancel button
let cancel = UIAlertAction(title: "Cancel", style: .cancel)
// add action to the sheet
alertController.addAction(camera)
alertController.addAction(library)
alertController.addAction(cancel)
// present action sheet to the user
self.present(alertController, animated: true, completion: nil)
}
func showPicker(with source: UIImagePickerController.SourceType) {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = true
picker.sourceType = source
self.present(picker, animated: true, completion: nil)
}
// fetch selected image from info with key
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
var selectedImage: UIImage?
// declaring editedImage
if let editedImage = info[.editedImage] as? UIImage {
selectedImage = editedImage
} else if let originalImage = info[.originalImage] as? UIImage {
selectedImage = originalImage
}
self.dismiss(animated: true, completion: nil)
FirebaseHelper.shared.uploadImage(image: selectedImage, firebaseRef: .comment_images, viewController: self) {
urlString in
let timeStamp = Helper.shared.dateOfCurrentTimeZone()
let childKey = FirebaseHelper.shared.getChildKey()
let profileImageUrlString: String
if let profileImageUrl = FirebaseHelper.shared.getPhotUrl() {
profileImageUrlString = profileImageUrl.absoluteString
} else {
profileImageUrlString = ""
}
let values: [String: Any] = ["text": "", "uid": self.uid, "timeStamp": timeStamp, "childkey": childKey, "username": self.username, "email": self.email ?? "", "imageUrl": urlString, "profileImageUrl": profileImageUrlString]
self.commentRef.child(childKey).updateChildValues(values, withCompletionBlock: { (error, dataRef) in
if error != nil {
Helper.shared.alertController(title: "Error", message: error!.localizedDescription, viewController: self)
return
}
})
}
}
}
enter image description herePlease help me I want to upload multiple image from my uicollectionview to firebase Storage and show downloadURL in Realtime database
i have try to search a lot but it no one work for me.
i can import the pictures from libray and make it show in myCollectionView but i don't know how to save it in to firebase.
Reports
-(userID)
-(reportID) - Using childByAutoId
- where i want to save to image
import UIKit
import Firebase
class ProblemViewController: UIViewController, UITextViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var reportID : String!
var phonNumber : String!
var ref: DatabaseReference!
let storage = Storage.storage()
let userID = Auth.auth().currentUser!.uid
#IBOutlet weak var problemButton: UIButton!
#IBOutlet weak var myCollectionView: UICollectionView!
#IBOutlet weak var problemImage: UIImageView!
var images = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
self.myCollectionView.delegate = self
self.myCollectionView.dataSource = self
retrieveUser()
generateReportID()
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .camera, target: self, action: #selector(importPicture))
}
#IBAction func sendProblemAction(_ sender: Any) {
//TODO: Storage image and Send information to Firebase and save it in our database
let imageName = UUID().uuidString
let storageRef = storage.reference().child("product_images/\(userID)/report/\(imageName).jpg")
if let uploadImage = problemImage.image!.jpegData(compressionQuality: 0.15) {
storageRef.putData(uploadImage, metadata: nil) { (metadata, error) in
if error != nil {
print("error")
return
}
storageRef.downloadURL(completion: {(url, error) in
if error != nil {
print(error!.localizedDescription)
return
}
let downloadURL = url?.absoluteString
let post = ["report_image_url": downloadURL!] as [String : Any]
self.registerUserIntoDatabase(userID: self.userID, values: post as [String : AnyObject])
})
}
}
self.navigationController?.popToRootViewController(animated: true)
}
private func registerUserIntoDatabase(userID: String, values: [String:AnyObject]){
//TODO: Send information to Firebase and save it in our database
ref = Database.database().reference()
let usersRef = ref.child("Reports").child("\(userID)/\(reportID!)")
usersRef.updateChildValues(values, withCompletionBlock: { (err, ref) in
})
}
func generateReportID () {
ref = Database.database().reference()
guard let key = self.ref.child("Reports").child("\(userID)/\(String(describing: reportID))").childByAutoId().key else { return }
reportID = key
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageView", for: indexPath)
if let imageView = cell.viewWithTag(1000) as? UIImageView {
imageView.image = images[indexPath.item]
}
return cell
}
#objc func importPicture() {
let picker = UIImagePickerController()
picker.allowsEditing = true
picker.delegate = self
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "picture", style: .default, handler: { (action) in
picker.sourceType = .photoLibrary
self.present(picker, animated: true, completion: nil)
}))
actionSheet.addAction(UIAlertAction(title: "cancel", style: .default, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
dismiss(animated: true)
images.insert(image, at: 0)
myCollectionView.reloadData()
print("key = \(image)")
}
}
I want to upload multiple images that show in uicollectionview to my firebase storage and show downloadURL in realtime database.
like this
Reports
-(userID)
-(reportID) - Using childByAutoId
- image1 : "https://firebasestorage.googleapis.com/..."
- image2 : "https://firebasestorage.googleapis.com/..."
- image3 : "https://firebasestorage.googleapis.com/..."
- image4 : "https://firebasestorage.googleapis.com/..."
My UICollection
When i press save it will error like this
My error
So, I'm still working on with my poem mobile project.. I'm working on this login module. What's happening here is that it jumps from one ViewController to another ViewController.. maybe it's because of the task? Is there any way to finish all the tasks in the first ViewController before jumping to the second ViewController (TimelineViewController)? Like something to stop it from going to the TimelineViewController if the task in the first ViewController is not yet finish?
I hope someone can help me solve this.. :( Thank you in advance!
This is my ViewController code
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var txtusername: UITextField!
#IBOutlet weak var txtpassword: UITextField!
#IBOutlet weak var lblResult: UILabel!
var returnVal:String? = nil
#IBOutlet weak var btnLogin: UIButton!
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.
}
#IBAction func btnLogin_Tapped(sender: UIButton) {
let url:NSURL = NSURL(string: "http://192.168.1.6/Test/login.php")!
let request:NSMutableURLRequest = NSMutableURLRequest(url:url as URL)
let bodyData = "username=\(txtusername.text!)&password=\(txtpassword.text!)"
request.httpMethod = "GET"
request.httpBody = bodyData.data(using: String.Encoding.utf8);
// NSURLConnection.sendAsynchronousRequest(request as URLRequest, queue: OperationQueue.main){(response, data, error) in
//print(response)
// }
let task = URLSession.shared.dataTask(with: request as URLRequest)
{
data, response, error in
guard let data = data, error == nil else {
print ("Error = \(error?.localizedDescription ?? "Unknown error")")
return
}
if let response = response {
print ("Response = \(response)")
}
if let responseString = String(data: data, encoding: .utf8) {
print ("Response data = \(responseString)")
}
//Converting response to NSDictionary
guard let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments),
let responseObject = json as? String,
let returnValidation = responseObject as? String else {
print("did not validate")
return
}
DispatchQueue.main.async()
{
self.returnVal = returnValidation
print("RETURN VALUE: \(self.returnVal!)")
self.performSegue(withIdentifier: "signinSegue", sender: self)
}
}
task.resume()
}
func AlertMessage(titleA: String, messageA: String)
{
let alert = UIAlertController(title: titleA, message: messageA, preferredStyle: UIAlertControllerStyle.alert)
let alertAction = UIAlertAction(title: "Ok", style:UIAlertActionStyle.default){(UIAlertAction)-> Void in }
alert.addAction(alertAction)
present(alert, animated: true){() -> Void in}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "signinSegue")
{
if(self.returnVal != nil)
{
if(self.returnVal! == "true")
{
let viewController = segue.destination as! TimelineViewController
viewController.passedValue = self.returnVal!
viewController.viewDidLoad()
}
else if (self.returnVal! == "false")
{
print("Incorrect Password!")
}
}
else
{
print("Empty")
}
}
}
}
This is my TimelineViewController code
import UIKit
import Foundation
struct Poem {
let poemTitle: String
let poemBody: String
init?(dictionary: [String: Any]) {
guard let poemTitle = dictionary["Title"] as? String,
let poemBody = dictionary["Body"] as? String else {
print("Did not find fullName/Bio")
return nil
}
self.poemTitle = poemTitle
self.poemBody = poemBody
}
}
class TimelineViewController: UIViewController {
#IBOutlet weak var contentTableView: UITableView!
#IBOutlet weak var poemBodyText: UITextView!
#IBOutlet weak var poemTitleText: UILabel!
var poems = [Poem]()
var passedValue:String? = nil
#IBOutlet weak var oPostBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
if(passedValue != nil)
{
getPoems()
}
else {
print("Empty")
}
}
func getPoems()
{
let url = URL(string: "http://192.168.1.6/Test/feed1.php")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request as URLRequest)
{
data, response, error in
guard let data = data, error == nil else {
print("Error = \(error?.localizedDescription ?? "Unknown error")")
return
}
if let response = response {
print("Response = \(response)")
}
if let responseString = String(data: data, encoding: .utf8){
print ("Response data = \(responseString)")
}
//Converting response to NSDictionary
guard let json = try? JSONSerialization.jsonObject(with: data),
let responseObject = json as? [String:Any],
let returnPoems = responseObject["returnPoems"] as? [[String:Any]] else {
print ("Did not find return Poems")
return
}
print (returnPoems)
DispatchQueue.main.async {
self.poems = returnPoems.flatMap { Poem(dictionary: $0) }
self.contentTableView.reloadData() }
}
task.resume()
}
/* override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}*/
func AlertMessage(titleA: String, messageA: String)
{
let alert = UIAlertController(title: titleA, message: messageA, preferredStyle: UIAlertControllerStyle.alert)
let alertAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default){(UIAlertAction)-> Void in }
alert.addAction(alertAction)
present(alert, animated: true){() -> Void in}
}
#IBAction func aPostBtn(_ sender: Any) {
var count = 0
while(count < 2)
{
//print("RESPONSE!!! : \(poemTitle[count])")
count = count + 1;
}
}
}
extension TimelineViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = contentTableView.dequeueReusableCell(withIdentifier: "PoemTableViewCell", for: indexPath) as? PoemTableViewCell else
{
fatalError("The dequeued cell is not an instance of PoemTableViewCell")
}
let poem = poems[indexPath.row]
cell.poemTitleText?.text = poem.poemTitle
cell.poemBodyText?.text = poem.poemBody
// cell.textLabel?.text = poem.poemTitle
// cell.detailTextLabel?.text = poem.poemBody
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return poems.count
}
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
let poem = poems[indexPath.row]
print(poem)
}
}
In btnLogin_Tapped remove DispatchQueue.main.async(). I don't see why you would need this. It creates a new queue and execute the code inside it asynchronously.
Also remove viewController.viewDidLoad(). You should never call viewDidLoad yourself, it is called automatically when the new view controller appears.
Another thing that might be causing a problem is if you've created a segue in your Storyboard (Ctrl + drag) from the login button to your TimelineViewController. You should remove it as you perform the segue at the end of btnLogin_Tapped.
I am having trouble playing a playlist in background mode while the device is locked. It plays the current audio when locked but I am having trouble with it going to the next audio file in background mode.
I am using a collectionView and a custom cell where I am allowing the user to tap and play the audio in that cell. I made it autoplay, so when the audio is done, it scrolls to the next cell automatically and plays that one and so on. All of that works, its just when the device is locked and in background mode that the playing of the next audio cell stops and just wont continue. I do have 3 second dispatch delays in between the load of the next audio file.
Any help or insight is greatly appreciated.
View Controller
import UIKit
import AVFoundation
class MainVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, CustomCellDelegate {
var streams: [[String:Any]] = []
#IBOutlet weak var cv: UICollectionView!
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = cv.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? CollectionViewCell {
cell.delegate = self
cell.nameLbl.text = streams[indexPath.item]["shortname"] as? String
return cell
} else {
return CollectionViewCell()
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if thereIsCellTapped != true {
let item = cv.cellForItem(at: indexPath) as! CollectionViewCell
self.loadingCell()
let when = DispatchTime.now() + 3 // change 2 to desired number of seconds
DispatchQueue.main.asyncAfter(deadline: when) {
DispatchQueue.main.async {
UIView.animate(
withDuration: 0.5,
delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1,
options: [],
animations: {
item.superview?.bringSubview(toFront: item)
self.view.layoutIfNeeded()
},
}), completion: { (finished: Bool) in
let theStream = self.streams[indexPath.item]["audioUrl"] as? String
item.audioStreamSelected(audioUrl: audioUrl!)
})
)
}
}
}
}
func closeView(_ cell: CollectionViewCell) {
let visibleItems: NSArray = indexPath as NSArray
let currentItem: IndexPath = visibleItems.object(at: 0) as! IndexPath
let nextItem: IndexPath = IndexPath(item: currentItem.item + 1, section: 0)
if nextItem.row == streams.count{
self.cv?.scrollToItem(
at: IndexPath(row: 0, section: 0),
at: .right,
animated: true
)
} else {
self.cv.scrollToItem(at: nextItem, at: .left, animated: true)
let when = DispatchTime.now() + 3
DispatchQueue.main.asyncAfter(deadline: when) {
self.collectionView(self.cv, didSelectItemAt: nextItem as IndexPath)
}
}
}
}
Custom Cell
import UIKit
import AVFoundation
import MediaPlayer
protocol CustomCellDelegate {
func closeView(_ cell:CollectionViewCell)
}
class CollectionViewCell: UICollectionViewCell {
var player = AVPlayer()
var playerItem:AVPlayerItem?
let mpic = MPRemoteCommandCenter.shared()
var delegate: CustomCellDelegate?
var _timeObserver:Any?
#IBOutlet weak var nameLbl: UILabel!
#IBAction func closeBtn(_ sender: Any) {
if let delegate = self.delegate {
delegate.closeView(self)
}
}
override func awakeFromNib() {
super.awakeFromNib()
NotificationCenter.default.addObserver(self,selector: #selector(playerItemDidReachEnd(_:)),name:NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: player.currentItem)
UIApplication.shared.beginReceivingRemoteControlEvents()
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
print("AVAudioSession Category Playback OK")
do {
try AVAudioSession.sharedInstance().setActive(true)
print("AVAudioSession is Active")
} catch let error as NSError {
print(error.localizedDescription)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
func setupNowPlayingInfoCenter() {
UIApplication.shared.beginReceivingRemoteControlEvents()
mpic.skipBackwardCommand.isEnabled = false
mpic.seekBackwardCommand.isEnabled = false
mpic.playCommand.addTarget {event in
self.player.play()
self.setupNowPlayingInfoCenter()
return .success
}
mpic.pauseCommand.addTarget {event in
self.player.pause()
return .success
}
mpic.nextTrackCommand.addTarget {event in
return .success
}
mpic.previousTrackCommand.addTarget {event in
return .success
}
}
func playerItemDidReachEnd(_ notification: Notification) {
if let _: AVPlayerItem = notification.object as? AVPlayerItem {
player.replaceCurrentItem(with: nil)
if let delegate = self.delegate {
delegate.closeView(self)
}
player.pause()
player.seek(to: kCMTimeZero)
if (_timeObserver != nil) {
player.removeTimeObserver(_timeObserver!)
_timeObserver = nil
}
}
}
func audioStreamSelected(audioUrl: String){
if let streamer = URL(string: audioUrl){
let playerItem:AVPlayerItem = AVPlayerItem(url: streamer)
self.player = AVPlayer(playerItem: playerItem)
player.replaceCurrentItem(with: playerItem)
player.actionAtItemEnd = .pause
if (_timeObserver == nil) {
_timeObserver = player.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main) {
[weak self] time in
if self?.player.currentItem?.status == AVPlayerItemStatus.readyToPlay {
}
}
}
}
}
}