How can I transfer UISlider value on SecondViewController to Alarm on FirstViewController? - swift

I have two ViewControllers, FirstViewController and SecondViewController.
And there is UISlider on SecondViewController. There is an alarm on FirstViewController.
Now I want to transfer UISlider value, which is volume to alarm on FirstViewController.
ps. The function what I want to make is totally same as default iPhone setting slider.
I would be so happy so if you guys give me your knowledge, please.
import UIKit
import AVFoundation
class FirstViewController: UIViewController, AVAudioPlayerDelegate,
UITableViewDelegate, UITableViewDataSource{
let TODO = ["A", "B", "C"]
let notificationCenter = NotificationCenter.default
var volume = Float()
var counter = 0
var timer = Timer()
var startTime:Double = 0.0
var audioPlayer: AVAudioPlayer!
#IBOutlet weak var tableView: UITableView!
#IBAction func firstSwitch(_ sender: UISwitch)
{
if (sender).isOn
{
timer = Timer.scheduledTimer(withTimeInterval: 1 * 1, repeats: false, block: { timer in
self.audioPlayer.play()
self.audioPlayer.numberOfLoops = -1
print(self.audioPlayer.isPlaying)
})
}else{
timer.invalidate()
print("switch1stopped")
self.audioPlayer.stop()
}
}
notificationCenter.addObserver(self, selector: #selector(catchNotification(notification:)), name: NSNotification.Name(rawValue: "test"), object: nil)
}
#objc func catchNotification(notification: Notification) -> Void {
print("Catch notification")
audioPlayer.volume = volumeChane.value
//Use of unresolved identifier 'volumeChane'
}
///////////////////////////////////////////////////////////////////////////
import UIKit
import AVFoundation
class SecondViewController: UIViewController {
var audioPlayer: AVAudioPlayer!
let notificationCenter = NotificationCenter.default
#IBOutlet weak var volumeSlider: UISlider!
#IBOutlet weak var volumeLabel: UILabel!
#IBAction func volumeChange(_ sender: UISlider)
{
volumeLabel.text = String(Int(sender.value))
volumeSlider.value = sender.value
audioPlayer.volume = volumeSlider.value
notificationCenter.post(name: NSNotification.Name(rawValue: "test"), object: nil)
audioPlayer.play()
}
override func viewDidLoad()
{
super.viewDidLoad()
if let url=Bundle.main.url(forResource:"Alarm",withExtension:".mp3" )
{
do {
audioPlayer = try AVAudioPlayer(contentsOf:url)
audioPlayer?.play(atTime:1 * 10)
}catch{
audioPlayer = nil
}
}else{
fatalError("Url is nil")
}
}
extension Notification.Name
{
static let myNotificationName = Notification.Name("test")
}

Using NSNotificationCenter for a simple task such as this might be an overkill. You usually use closures to catch data changes in your components or view controllers.
In the SecondViewController create a variable containing a closure:
var onVolumeChange: ((value: Float) -> Void)?
Call it in the IBAction that monitors slider's onChange event.
#IBAction func volumeChange(_ sender: UISlider)
{
self.onVolumeChange?(sender.value)
}
Pass the onVolumeChange closure from the FirstViewController when navigating to the second one. I'm not sure how you perform navigation so I'll assume you do it programmatically.
let vc = UIStoryboard(name: "main", bundle: nil).instantiateViewController(withIdentifier: "SecondViewController")
vc.onVolumeChange = { value in
audioPlayer.volume = value
}
self.navigationController?.pushViewController(vc, animated: true)

Related

How to update Counter's value inside a Label of another class?

I have a counting-upward object in StopWatch class and a label showing its value in ViewController class. I used #Published and #ObservedObject property wrappers for sharing and observing counter's value.
How could I automatically update counter's value in a label?
ViewController.swift
import UIKit
import SwiftUI
class ViewController: UIViewController {
#ObservedObject var stopWatch = StopWatch()
#IBOutlet var label: UILabel!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
label.text = "\(self.stopWatch.counter)"
}
#IBAction func start(_ sender: UIButton) { self.stopWatch.start() }
#IBAction func stop(_ sender: UIButton) { self.stopWatch.stop() }
}
StopWatch.swift
class StopWatch: ObservableObject {
#Published var counter: Int = 0
var timer = Timer()
func start() {
self.timer = Timer.scheduledTimer(withTimeInterval: 1.0,
repeats: true) { _ in
self.counter += 1
}
}
func stop() {
self.timer.invalidate()
}
}
The #ObservedObject works only inside SwiftUI view. In this case it is possible to observe published property directly via Publisher, like
import Combine
class ViewController: UIViewController {
let stopWatch = StopWatch()
#IBOutlet var label: UILabel!
private var cancellable: AnyCancellable!
override func viewDidLoad() {
super.viewDidLoad()
cancellable = stopWatch.$counter.sink { [weak self] newValue in
self?.label.text = "\(newValue)"
}
}
// ... other code

Show ViewController programmatically (not working)

I got a main VC called "ViewController.swift", which is bond to my ViewController Scene in Main.storyboard.
Then I want another screen, which shows when a condition in my ViewController.swift is fulfilled.
Therefore I got a Gameover.storyboard, which has a VC Scene with Storyboard ID "overID" as well as a "GameoverViewController.swift" to handle the GameOver Scene.
Here is my code in ViewController.swift, which should open Gameover.storyboards VC Scene when the condition you see below is fulfilled:
import UIKit
class ViewController: UIViewController {
var wantedLiter = 10.00
var timer : Timer?
var actualLiter = 0.00
var timerReset = true
var preisProLiter = 1.26
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
let storyBoard: UIStoryboard = UIStoryboard(name: "Gameover", bundle: nil)
let gameoverViewController = storyBoard.instantiateViewController(withIdentifier: "overID") as! GameoverViewController
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#IBOutlet weak var preisliterLabel: UILabel!
#IBOutlet weak var euroLabel: UILabel!
#IBOutlet weak var numberLabel: UILabel!
#IBOutlet weak var numberButton: UIButton!
#IBOutlet weak var confirmButton: UIButton!
#IBAction func confirm(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
numberLabel.text = String(round(1000*actualLiter)/1000) //initial value
preisliterLabel.text = String(preisProLiter)
// Do any additional setup after loading the view.
}
#IBAction func holdingTheButton(_ sender: Any) {
print("I am holding")
timerReset = false // reset to false since you are holding the button
guard timer == nil else { return }
timer = Timer.scheduledTimer(timeInterval: 0.005, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
#IBAction func buttonReleased(_ sender: Any) {
print("button released")
if (wantedLiter == actualLiter){
print("WIN!")
actualLiter = 0;
}
//startTime = 0.00
timer?.invalidate()
timer = nil
// timerReset = true // reset to true since you released.
}
#objc func updateTime(){
//update label every second
print("updating label ")
if (wantedLiter <= actualLiter){
print("Ooops")
actualLiter = 0;
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
self.present(gameoverViewController, animated: true, completion: nil)
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
actualLiter += 0.01
numberLabel.text = String(round(1000*actualLiter)/1000)
euroLabel.text = String((round(100*actualLiter*preisProLiter)/100))
}
}
And now got you some Screenshots, which might help.
If you need more Screens, please ask.
Right now, I got following errors:
1) ViewController.swift in the upper part marked with "!!!!..."
Cannot use instance member 'storyBoard' within property initializer; property initializers run before 'self' is available
2) ViewController.swift
Cannot convert value of type 'gameoverViewController.Type' to expected argument type 'UIViewController'
3) ViewController.swift upper Part:
Use of undeclared type 'GameoverViewController'
This is how I changed the code:
import UIKit
class ViewController: UIViewController {
var wantedLiter = 10.00
var timer : Timer?
var actualLiter = 0.00
var timerReset = true
var preisProLiter = 1.26
let storyBoard: UIStoryboard = UIStoryboard(name: "Gameover", bundle: nil)
var gameoverViewController: GameoverViewController!
#IBOutlet weak var preisliterLabel: UILabel!
#IBOutlet weak var euroLabel: UILabel!
#IBOutlet weak var numberLabel: UILabel!
#IBOutlet weak var numberButton: UIButton!
#IBOutlet weak var confirmButton: UIButton!
#IBAction func confirm(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
gameoverViewController = storyBoard.instantiateViewController(withIdentifier: "overID") as? GameoverViewController
numberLabel.text = String(round(1000*actualLiter)/1000) //initial value
preisliterLabel.text = String(preisProLiter)
// Do any additional setup after loading the view.
}
#IBAction func holdingTheButton(_ sender: Any) {
print("I am holding")
timerReset = false // reset to false since you are holding the button
guard timer == nil else { return }
timer = Timer.scheduledTimer(timeInterval: 0.005, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
#IBAction func buttonReleased(_ sender: Any) {
print("button released")
if (wantedLiter == actualLiter){
print("WIN!")
actualLiter = 0;
}
//startTime = 0.00
timer?.invalidate()
timer = nil
// timerReset = true // reset to true since you released.
}
#objc func updateTime(){
//update label every second
print("updating label ")
if (wantedLiter <= actualLiter){
print("Ooops")
actualLiter = 0;
self.present(gameoverViewController as! UIViewController, animated: true, completion: nil)
}
actualLiter += 0.01
numberLabel.text = String(round(1000*actualLiter)/1000)
euroLabel.text = String((round(100*actualLiter*preisProLiter)/100))
}
}
(not working)
And I changed the GameoverViewController type to UIViewController.
But where was my Typo?? (G instead of g)?
Move the initializer for gameoverViewController into viewDidLoad or the like and make gameoverViewController an optional or implicitly unwrapped optional (that's the ! after a type)
Should be fixed by 1.
Please show the source for GameoverViewController if this is not fixed by 1. too.

swift how to update first controller from logic in second one vai protocol and delegate pattern?

I have a label in first view controller ViewController, and a func getting date avery second in second vc. I'd like to update label in first after timer starts in second. is it good to use protocol-delegate pattern? at this moment it is not working, time is going but not updating the view in first VC
my struct for protocol
protocol ViewControllerDelegate: class {
func changeLabelText(textToPass: String)
}
in first viewController
class ViewController: UIViewController, ViewControllerDelegate {
#IBOutlet weak var mainLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func changeLabelText(textToPass: String) {
self.mainLabel.text = textToPass
self.view.layoutIfNeeded()
}
#IBAction func buttonTapped(_ sender: UIButton) {
let nextVC = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
nextVC.delegateSubscriber = self
present(nextVC, animated: true, completion: nil)
}
}
in secondVC
class SecondViewController: UIViewController {
//MARK: speed timer feature 1/3
private weak var timer: Timer?
private var timerDispatchSourceTimer : DispatchSourceTimer?
weak var delegateSubscriber : ViewControllerDelegate?
#IBOutlet weak var myTxtField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
startTimer(every: 1)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("appeared")
stopTimer()
}
private func startTimer(every timeInterval: TimeInterval) {
if #available(iOS 10.0, *) {
timer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: true) { [weak self] _ in
let dateToPass = Date().description
print(dateToPass)
self?.delegateSubscriber?.changeLabelText(textToPass: dateToPass)
}
}
}
//MARK: speed timer feature 3/3
private func stopTimer() {
timer?.invalidate()
//timerDispatchSourceTimer?.suspend() // if you want to suspend timer
timerDispatchSourceTimer?.cancel()
}
#IBAction func buttonTapped(_ sender: UIButton) {
// delegateSubscriber?.changeLabelText(textToPass: self.myTxtField.text ?? "error")
dismiss(animated: true, completion: nil)
}
}
Just remove [weak self] from Timer closure
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
let dateToPass = Date().description
print(dateToPass)
self.delegateSubscriber?.changeLabelText(textToPass: dateToPass)
}
... then self isn't optional

Custom keyboard is crashing the app - Swift

I'm doing a test of a custom keyboard. This is what I need:
It has to have two UITextFields. Cannot be labels.
The keyboard is an embedded UIView.
The default keyboard should be disabled.
It cannot be a keyboard extension.
Not sure why the app is crashing. PS: Not all the keys are on the code yet. Here is an image of what I'm trying to do and the two View Controllers.
Edit: The error is: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
First ViewController:
import UIKit
class HomeVC: UIViewController, ButtonTapDelegate {
#IBOutlet var textField1: UITextField!
#IBOutlet var textField2: UITextField!
#IBOutlet var keyboardView: UIView!
var buttonPressed = [String]()
override func viewDidLoad() {
addKeyboard(view: keyboardView)
buttonPressed = [String]()
textField1.inputView = UIView()
textField2.inputView = UIView()
}
func addKeyboard(view: UIView) {
let keyboard = KeyboardVC(nibName: "KeyboardVC", bundle: nil)
view.addSubview(keyboard.view)
addChild(keyboard)
}
func didTapButton(sender: UIButton) {
if sender.tag == 5 {
textField1.text?.append(contentsOf: " ")
} else if sender.tag == 6 {
textField1.text?.removeAll()
buttonPressed = [String]()
} else {
let val = sender.titleLabel?.text
textField1.text?.append(contentsOf: val!)
}
self.textField1.text = buttonPressed.joined(separator: "")
}
}
Here is the second View Controller:
import UIKit
protocol ButtonTapDelegate {
func didTapButton(sender: UIButton)
}
class KeyboardVC: UIViewController {
var delegate: ButtonTapDelegate!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttons(_ sender: UIButton) {
delegate.didTapButton(sender: sender)
print(sender)
}
}
var delegate: ButtonTapDelegate!
An implicitly unwrapped optional is essentially a promise that you're definitely going to give the variable a value before you try to access it. The problem in this case is that you haven't done that. Most likely, you want to do this in your first view controller:
func addKeyboard(view: UIView) {
let keyboard = KeyboardVC(nibName: "KeyboardVC", bundle: nil)
keyboard.delegate = self // Now "delegate" will have a value before the function gets called
view.addSubview(keyboard.view)
addChild(keyboard)
}

Swift delegate beetween two VC without segue

I have 3 classes:
ChatLogControoller
GetImageFromLibraty(NSObject class)
ImagePreviewViewController
I want to press a clip from the first VC, then open the media library to pick an image. Then the selected image is passed to the third VC as a previewController. Then if I select 'done' I want to pass it to the first VC.
1st VC
class ChatLogControoller: UICollectionViewController, UICollectionViewDelegateFlowLayout, NSFetchedResultsControllerDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate, DataSentDelegate {
func recievePhoto(data: UIImage) {
imageFromView = data
print("-------\(imageFromView = data)")
}
override func viewDidLoad() {
super.viewDidLoad()
let vc = ImagePreviewController()
self.vc.delegate = self
}
2nd class its just picker of image, so i pass image to 3rd VC and this image appears on imageView of 3rd VC successfully!
my 3rd VC
protocol DataSentDelegate {
func recievePhoto(data: UIImage)
}
class PreviewController: UIViewController, UIScrollViewDelegate {
var delegate : DataSentDelegate? = nil
var aImageView: UIImageView!
var aImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Add", style: .plain, target: self, action: #selector(actionSend))
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(actionBack))
}
#objc func actionBack() {
dismiss(animated: false, completion: nil)
}
#objc func actionSend() {
let data = aImageView.image
delegate?.recievePhoto(data: data!)
dismiss(animated: true, completion: nil)
}
You need to create one more protocol in your SecondViewController to Pass that delegate from ThirdViewController to FirstViewController.
FirstViewController:
import UIKit
class ViewController: UIViewController, DataSentDelegate, dataSentDelegate {
#IBOutlet weak var imagefromThirdVC: UIImageView!
var thirdVCImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonTapped(_ sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "ViewController2") as! ViewController2
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
func goToThirdVC() {
let vc = storyboard?.instantiateViewController(withIdentifier: "ViewController3") as! ViewController3
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
func recievePhoto(data: UIImage) {
thirdVCImage = data
imagefromThirdVC.image = thirdVCImage
}
}
SecondViewController:
import UIKit
protocol dataSentDelegate {
func goToThirdVC()
}
class ViewController2: UIViewController {
#IBOutlet weak var passingImage: UIImageView!
var delegate: dataSentDelegate? = nil
var images: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
images = UIImage(named: "screen")
}
#IBAction func actionButton(_ sender: Any) {
self.delegate?.goToThirdVC()
}
}
ThirdViewController:
import UIKit
protocol DataSentDelegate {
func recievePhoto(data: UIImage)
}
class ViewController3: UIViewController {
var delegate: DataSentDelegate? = nil
#IBOutlet weak var passedImageView: UIImageView!
var passedImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
passedImage = UIImage(named: "screen")
passedImageView.image = passedImage
}
#IBAction func action(_ sender: Any) {
let data = passedImageView.image
delegate?.recievePhoto(data: data!)
// delegate?.goToFirstVC()
guard let viewControllers = self.navigationController?.viewControllers else {
return
}
for firstViewController in viewControllers {
if firstViewController is ViewController {
self.navigationController?.popToViewController(firstViewController, animated: true)
break
}
}
}
}