I have a music app which have a table view with custom cell, which have labels and info which already in my JSON file:
[
{
"loop_name" : "Come Back",
"Instrument" :"Guitar",
"loop_link" : "local link to wav file", (need to figure out)
"producer" : "Bernulli"
}
]
The main problem is, I don't know how to implement local audio file into JSON file and after this load this data to my app. Here is my custom cell with labels (which already in JSON file and button, which should play songs from JSON file in each of the row in table view):
import UIKit
import AVFoundation
protocol CustomSongDelegate: AnyObject {
func btnUseTap(cell: CustomLoopsCell)
}
class CustomSongCell: UITableViewCell {
weak var delegate: CustomSongDelegate?
var songs: [String] = []
var audioPlayer = AVAudioPlayer()
#IBOutlet weak var loopNameLabel: UILabel!
#IBOutlet weak var producerLabel: UILabel!
#IBOutlet weak var instrumentLabel: UILabel!
#IBOutlet weak var playButtonOutlet: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func btnUse(_ sender: Any) {
delegate?.btnUseTap(cell: self)
}
}
Get the loop local file's name from your JSON file and you may use it to play your audio like that:
var player: AVAudioPlayer!
func playLoop(loopLocalFileName: String) {
let url = Bundle.main.url(forResource: loopLocalFileName, withExtension: "wav") // you should check it for errors
player = try! AVAudioPlayer(contentsOf: url!) // of course you should catch the error and deal with it...
player.play()
}
In order to parse your JSON more easily try using a struct like this:
struct Loop: Codable{
var name: String
var instrumentName: String
var localFileName: String
var producerName: String
enum CodingKeys: String, CodingKey {
case name = "loop_name"
case instrumentName = "Instrument"
case loopLocalFileName = "loop_link"
case producerName = "producer"
}
}
Related
I have been trying to pass data back to my "HostRideViewController" from my "ProfileViewController" but the data I try to pass back creates a fatal error when unwrapping it, but i dont understand why as the console print out before it shows the correct data
import UIKit
import Foundation
import Firebase
protocol RiderSelectionDelegate {
func selectedRideLeader(firstName:String,lastName:String,uid:String)
}
class ProfileViewController: UIViewController {
#IBOutlet weak var ProfilePictureImageView: UIImageView!
#IBOutlet weak var AddasLeaderBtn: UIButton!
#IBOutlet weak var FirstNameFeild: UILabel!
#IBOutlet weak var LastNameFeild: UILabel!
#IBOutlet weak var UserBioFeild: UILabel!
#IBOutlet weak var HiddenEmail: UILabel!
#IBOutlet weak var HiddenUID: UILabel!
var user: MyUser?
var selectedFirstName: String?
var selectedLastName: String?
var selectedUID: String?
var selectedEmail: String?
var selectionDelegate: RiderSelectionDelegate!
override func viewDidLoad() {
super.viewDidLoad()
/// make profile picture circular
ProfilePictureImageView.layer.cornerRadius = ProfilePictureImageView.frame.size.width/2
ProfilePictureImageView.clipsToBounds = true
/// load user data into view
FirstNameFeild?.text = user?.firstName
LastNameFeild?.text = user?.lastName
HiddenUID?.text = user?.uid
HiddenEmail?.text = user?.email
}
#IBAction func SelectedLeaderpressed(_ sender: Any) {
selectedFirstName = FirstNameFeild.text
selectedLastName = LastNameFeild.text
selectedUID = user?.uid
selectedEmail = user?.email
print(selectedUID!,selectedLastName!,selectedFirstName!)
/// where the error is happening
selectionDelegate.selectedRideLeader(firstName:selectedFirstName!, lastName:selectedLastName!, uid:selectedUID!)
dismiss(animated: true, completion: nil)
}
}
My console output and error from passing data
49s64wH2g7hjiMAKp0uvADkzP0k2 bloggs joe
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file
My intended use of the delegate is to set a label up with the user's name
extension HostRideViewController:RiderSelectionDelegate{
func selectedRideLeader(firstName: String, lastName: String, uid: String) {
OtherRideLeaderLbl.text = firstName
}
}
where am I going wrong as I'm trying to force unwrap the data which is there and present in the print but crashes when I'm trying to delegate it
It seems selectionDelegate is nil you need to set it when you present/push/segue to the destination vc
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 3 years ago.
I want to send data from TextField from second view controller to first view controller and add this data to an array
I have a struct which I will save to array:
struct ContactsModel {
var name : String
var surname : String
var phoneNumber : String
}
first VC:
class FirstViewController: UIViewController {
var contacts : [ContactsModel] = []
}
second VC:
class SecondViewController: UIViewController {
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var surnameTextField: UITextField!
#IBOutlet weak var phoneNumberTextField: UITextField!
#IBAction func saveAndClose(_ sender: UIButton) {
// here i want to send this objects (nameTextField, surnameTextField, phoneNumberTextField) in array in first VC when i press this button
}
}
You can accomplish this using a delegate:
struct ContactsModel {
var name : String
var surname : String
var phoneNumber : String
}
protocol SecondViewControllerDelegate: class {
func savedAndClosed(with model: ContactsModel)
}
class FirstViewController: UIViewController {
var contacts : [ContactsModel] = []
// Whereever you create and present your instance of SecondViewController make it conform to the delegate
func showSecondVC() {
let secondVC = SecondViewController()
secondVC.delegate = self
present(secondVC, animated: true, completion: nil)
}
}
extension FirstViewController: SecondViewControllerDelegate {
func savedAndClosed(with model: ContactsModel) {
contacts.append(model)
}
}
class SecondViewController: UIViewController {
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var surnameTextField: UITextField!
#IBOutlet weak var phoneNumberTextField: UITextField!
weak var delegate: SecondViewControllerDelegate?
#IBAction func saveAndClose(_ sender: UIButton) {
// here i want to send this objects (nameTextField, surnameTextField, phoneNumberTextField) in array in first VC when i press this button
guard let name = nameTextField.text, let surname = surnameTextField.text, let phoneNumber = phoneNumberTextField.text else { return }
let new = ContactsModel(name: name, surname: surname, phoneNumber: phoneNumber)
delegate?.savedAndClosed(with: new)
}
}
First be sure to make var contacts in FirstViewController static:
class FirstViewController: UIViewController {
static var contacts : [ContactsModel] = []
}
Then in SecondViewController you can edit variable "contacts" like this:
class SecondViewController: UIViewController {
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var surnameTextField: UITextField!
#IBOutlet weak var phoneNumberTextField: UITextField!
#IBAction func saveAndClose(_ sender: UIButton) {
// here i want to send this objects (nameTextField, surnameTextField, phoneNumberTextField) in array in first VC when i press this button
FirstViewController.contacts.append(ContactsModel(name: nameTextField.text ?? "defaultName", surname: surnameTextField.text ?? "defaultSurname", phoneNumber: phoneNumberTextField.text ?? "defaultPhone"))
}
}
You need to define default values so even if text from field would be nil your app won't crush, in example we set default values here:
name: nameTextField.text ?? "defaultName"
I am trying to pass a news ID of type string to the second VC and load the object based on it from Realm. When I debugged, I found that the prepare for segue is correctly setting the detailNewsVC.newsID to the primary key of my news item but the second VC is not receiving it. Any help on this?
Checks I have made:
Made sure that the detail VC identifier is correct
detailNewsVC.newsID in VC 1 is correctly setting the news ID .. This is to make sure that realm is correctly sending the newsID and it is working fine.
Changed the viewDidLoad in VC 2 to viewWillLoad..Just to make sure that second vc is not loaded before for any reason but no luck on that.
Restarted xcode
Replaced newsID in VC 2 with an actual news primary key and it's correctly pulling the related news. I think the culprit is that the VC2 property: newsID is not updating when prepare for segue is called.
First VC code for prep for segue:
extension HomeVC: UICollectionViewDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == SegueIdentifier.gotodetail.rawValue, let sendNewsID = sender as? String {
let navVC = segue.destination as? UINavigationController
let detailNewsVC = navVC?.viewControllers.first as! DetailNewsVC
detailNewsVC.newsID = sendNewsID
print("Detail News ID = \(detailNewsVC.newsID)")
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let newsID = newsArray[indexPath.row].newsId
performSegue(withIdentifier: SegueIdentifier.gotodetail.rawValue, sender: newsID)
}
}
Second VC Code:
class DetailNewsVC: UIViewController {
#IBOutlet private weak var scrollView: UIScrollView!
#IBOutlet private weak var newsTitle: UILabel!
#IBOutlet private weak var newsImage: UIImageView!
#IBOutlet private weak var newsDescription: UILabel!
var newsID = ""
override func viewDidLoad() {
super.viewDidLoad()
let realm = try! Realm()
print("News ID: \(newsID)")
guard let news = realm.object(ofType: News.self, forPrimaryKey: newsID as AnyObject) else {
print("Cannot load news")
return
}
print(news)
newsTitle.text = news.newsTitle
if let url = URL(string: news.urlToImage), let data = try? Data.init(contentsOf: url) {
newsImage.image = UIImage(data: data)
}
newsDescription.text = news.newsDescription
}
}
Move your prepare function out of the extension and put it in HomeVC. According to Apple's Swift Guide extensions cannot override existing functionality.
Extensions can add new functionality to a type, but they cannot override existing functionality.
Apple Developer Guide
It's hard to tell in which order UIKit calls the UIViewController methods, but it might be possible that viewDidLoad is getting called before you get the chance to set the value of newsID.
The following might be overkill, but it'll guarantee the views will be updated during viewDidLoad, or otherwise if newsID is set after the fact:
class DetailNewsVC: UIViewController {
#IBOutlet private weak var scrollView: UIScrollView!
#IBOutlet private weak var newsTitle: UILabel!
#IBOutlet private weak var newsImage: UIImageView!
#IBOutlet private weak var newsDescription: UILabel!
public var newsID = "" {
didSet {
updateUIForNews()
}
}
override func viewDidLoad() {
super.viewDidLoad()
updateUIForNews()
}
private func updateUIForNews() {
guard !newsID.isEmpty else {
return
}
let realm = try! Realm()
print("News ID: \(newsID)")
guard let news = realm.object(ofType: News.self, forPrimaryKey: newsID as AnyObject) else {
print("Cannot load news")
return
}
print(news)
newsTitle.text = news.newsTitle
if let url = URL(string: news.urlToImage), let data = try? Data.init(contentsOf: url) {
newsImage.image = UIImage(data: data)
}
newsDescription.text = news.newsDescription
}
}
Usual AVAudioPlayer tutorials online creates a AVAudioPlayer within a function to where the play and stop functions of the AVAudioPlayer object aren't available to the object directly from other functions. The problem is that I would like another function to stop the sound from the AVAudioPlayer. This seems pretty simple by initializing the objects at the top of the class in hopes it would be accessible however in Swift3 the init function for AVAudioPlayer includes a throw and a parameter for the sound file. Swift doesn't allow us to use an instance member within a property initializer so I'm stuck on my thought process of how this could be written.
The only error I'm running into at this point is not being allowed to use an instance member in the property initializer when creating "backgroundMusicPlayer":
import UIKit
import AVFoundation
class MadLibOneViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var thePlace: UITextField!
#IBOutlet weak var theVerb: UITextField!
#IBOutlet weak var theNumber: UITextField!
#IBOutlet weak var theTemplate: UITextView!
#IBOutlet weak var theStory: UITextView!
#IBOutlet weak var generateStoryButton: UIButton!
#IBOutlet weak var proceedToNextMadLib: UIButton!
//var backgroundMusicPlayer = AVAudioPlayer()
var error:NSError?
var path = Bundle.main.path(forResource: "bensound-cute", ofType: "mp3")
var url: NSURL {
return NSURL(fileURLWithPath: path!)
}
var backgroundMusicPlayer: AVAudioPlayer = try AVAudioPlayer(contentsOf: url as URL, error: &error)
#IBAction func createStory(_ sender: AnyObject) {
theStory.text=theTemplate.text
theStory.text=theStory.text.replacingOccurrences(of: "<place>", with: thePlace.text!)
theStory.text=theStory.text.replacingOccurrences(of: "<verb>", with: theVerb.text!)
theStory.text=theStory.text.replacingOccurrences(of: "<number>", with: theNumber.text!)
generateStoryButton.isHidden=true
proceedToNextMadLib.isHidden=false
}
#IBAction func showNextStory(_ sender: AnyObject) {
view.backgroundColor=UIColor.green
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let resultViewController = storyBoard.instantiateViewController(withIdentifier: "MadLibTwoViewController") as! MadLibTwoViewController
self.present(resultViewController, animated:true, completion:nil)
}
#IBAction func hideKeyboard(_ sender: AnyObject) {
thePlace.resignFirstResponder()
theVerb.resignFirstResponder()
theNumber.resignFirstResponder()
theTemplate.resignFirstResponder()
}
override func viewDidLoad() {
super.viewDidLoad()
proceedToNextMadLib.isHidden=true
view.backgroundColor = UIColor.purple
// Do any additional setup after loading the view.
self.theVerb.delegate = self
self.thePlace.delegate = self
self.theNumber.delegate = self
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You need to use Lazy initialisation/instantiation for that. In your case this is all you need to do.
lazy var player: AVAudioPlayer = {
[unowned self] in
do {
return try AVAudioPlayer.init(contentsOf: self.url)
}
catch {
return AVAudioPlayer.init()
}
}()
For more about Lazy Initialisation this is a good read. The interesting thing in your case is that the initialiser throws. I think this forum discussion is helpful to have a slight idea.
this is my code but its give me nil in output.
import AVFoundation
import AVKit
import UIKit
var userName :String?
class ViewController: UIViewController {
var movieUrl:NSURL? = NSURL(string: "http://streamapp.ir:1935/lahzenegar/\(userName)/playlist.m3u8")
#IBOutlet var inpt: UITextField!
#IBOutlet var scView: UIScrollView!
let avPlayerViewController = AVPlayerViewController()
var avPlayer: AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
}
and my button code:
#IBAction func playButtonTapped3(sender: UIButton) {
userName = "\(self.inpt.text)"
self.presentViewController(self.avPlayerViewController, animated: true) { () -> Void in
self.avPlayerViewController.player?.play()
}
print(movieUrl)
}
so i want when press button (username) change value to whatever users entered in textBox
already output:
thanks for help
When you first create the movieURL the userName is not set. That is fine, however, when you press the button you need to update the movieURL as well as the username. Something like this:
userName = "\(self.inpt.text)"
movieURL = NSURL(string: "http://streamapp.ir:1935/lahzenegar/\(userName)/playlist.m3u8")