I gave this code, and I want to add highscore to this project. I wa trying to do this on my own but can't get this to working. Can somebody help me with achieve that ?
I was trying to manipulate with timeInterval but with no luck, this is my learning project from GitHub, I Was trying to ask author to help me but he didn't answer.
fileprivate extension ViewController {
func setupPlayerView() {
playerView.bounds.size = CGSize(width: radius * 2, height: radius * 2)
playerView.layer.cornerRadius = radius
playerView.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
view.addSubview(playerView)
}
func startEnemyTimer() {
enemyTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(generateEnemy(timer:)), userInfo: nil, repeats: true)
}
func stopEnemyTimer() {
guard let enemyTimer = enemyTimer,
enemyTimer.isValid else {
return
}
enemyTimer.invalidate()
}
func startDisplayLink() {
displayLink = CADisplayLink(target: self, selector: #selector(tick(sender:)))
displayLink?.add(to: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
}
func stopDisplayLink() {
displayLink?.isPaused = true
displayLink?.remove(from: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
displayLink = nil
}
func getRandomColor() -> UIColor {
let index = arc4random_uniform(UInt32(colors.count))
return colors[Int(index)]
}
func getEnemyDuration(enemyView: UIView) -> TimeInterval {
let dx = playerView.center.x - enemyView.center.x
let dy = playerView.center.y - enemyView.center.y
return TimeInterval(sqrt(dx * dx + dy * dy) / enemySpeed)
}
func gameOver() {
stopGame()
displayGameOverAlert()
}
func stopGame() {
stopEnemyTimer()
stopDisplayLink()
stopAnimators()
gameState = .gameOver
}
func prepareGame() {
removeEnemies()
centerPlayerView()
popPlayerView()
startLabel.isHidden = false
clockLabel.text = "00:00.000"
gameState = .ready
}
func startGame() {
startEnemyTimer()
startDisplayLink()
startLabel.isHidden = true
beginTimestamp = 0
gameState = .playing
}
func removeEnemies() {
enemyViews.forEach {
$0.removeFromSuperview()
}
enemyViews = []
}
func stopAnimators() {
playerAnimator?.stopAnimation(true)
playerAnimator = nil
enemyAnimators.forEach {
$0.stopAnimation(true)
}
enemyAnimators = []
}
func updateCountUpTimer(timestamp: TimeInterval) {
if beginTimestamp == 0 {
beginTimestamp = timestamp
}
elapsedTime = timestamp - beginTimestamp
clockLabel.text = format(timeInterval: elapsedTime)
}
func highscore(timestamp: TimeInterval) {
if beginTimestamp == 0 {
beginTimestamp = timestamp
}
elapsedTime = timestamp - beginTimestamp
highscoreLabel.text = format(timeInterval: elapsedTime)
}
func format(timeInterval: TimeInterval) -> String {
let interval = Int(timeInterval)
let seconds = interval % 60
let minutes = (interval / 60) % 60
let milliseconds = Int(timeInterval * 1000) % 1000
return String(format: "%02d:%02d.%03d", minutes, seconds, milliseconds)
}
func checkCollision() {
enemyViews.forEach {
guard let playerFrame = playerView.layer.presentation()?.frame,
let enemyFrame = $0.layer.presentation()?.frame,
playerFrame.intersects(enemyFrame) else {
return
}
gameOver()
}
}
func movePlayer(to touchLocation: CGPoint) {
playerAnimator = UIViewPropertyAnimator(duration: playerAnimationDuration,
dampingRatio: 0.5,
animations: { [weak self] in
self?.playerView.center = touchLocation
})
playerAnimator?.startAnimation()
}
func moveEnemies(to touchLocation: CGPoint) {
for (index, enemyView) in enemyViews.enumerated() {
let duration = getEnemyDuration(enemyView: enemyView)
enemyAnimators[index] = UIViewPropertyAnimator(duration: duration,
curve: .linear,
animations: {
enemyView.center = touchLocation
})
enemyAnimators[index].startAnimation()
}
}
func displayGameOverAlert() {
let (title, message) = getGameOverTitleAndMessage()
let alert = UIAlertController(title: "Game Over", message: message, preferredStyle: .alert)
let action = UIAlertAction(title: title, style: .default,
handler: { _ in
self.prepareGame()
}
)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
func getGameOverTitleAndMessage() -> (String, String) {
let elapsedSeconds = Int(elapsedTime) % 60
switch elapsedSeconds {
case 0..<10: return ("I try again", "You need more practice")
case 10..<30: return ("Maybe Another try?", "No bad, just play more")
case 30..<60: return ("Play again", "You are like Ninja")
default:
return ("WOW", "You are simply the best")
}
}
right know I managed to have state of score but I can't to save high score whats wrong with that
func updateBest(){
var defaults=UserDefaults()
var highscore=defaults.integer(forKey: "highscore")
if(Int(elapsedTime)) > highscore
{
defaults.set(Int(self.elapsedTime), forKey: "highscore")
}
var highscoreshow=defaults.integer(forKey: "highscore")
highscoreLabel.text = "\(highscoreshow)"
print("hhScore reported: \(highscoreLabel.text)")
// clockLabel.text = "\(elapsedTime)"
// print("play reported: \(clockLabel.text)")
}
For saving highscore to userdefaults :
let highscore : String = "\(Int(self.elapsedTime))"
UserDefaults.standard.set(highscore, forKey: "highscore")
For retrieving highscore from userdefaults :
highscoreLabel.text = (UserDefaults.standard.value(forKey: "highscore") as? String)
I hope it helps. Cheers. :]
Related
I have an application where a button is pressed, the player is paused and then I want to extract the image that is shown on screen. That is to do some processing and display that result.
This was my first attempt: Extracting bitmap from AVPlayer is very uncertain
There is some problem there, I can't get the correct image back at the moment. So I thought maybe a different approach would be to create a bitmap using the view that contain the video.
import Foundation
import UIKit
import AVKit
import TensorFlowLite
class VideoPlayerController : UIViewController {
#IBOutlet weak var videoPlayerView: UIView!
#IBOutlet weak var playButton: UIButton!
#IBOutlet weak var forwardButton: UIButton!
#IBOutlet weak var rewindButton: UIButton!
#IBOutlet weak var playSlowButton: UIButton!
var playbackSlider: UISlider?
var videoDuration: CMTime?
var videoDurationSeconds: Float64?
var movieName: String?
var player: AVPlayer?
var isPlaying = false
var lines = [UIView]()
var playerViewController: AVPlayerViewController?
let delta = 0.02
let slowRate: Float = 0.03
var currentRate: Float = 1.0
var times = [NSValue]()
var timeObserverToken: Any?
func setTimeObserverToken() {
timeObserverToken = player!.addBoundaryTimeObserver(forTimes: times, queue: .main) {
let time = CMTimeGetSeconds(self.player!.currentTime())
self.updateSlider(time: Float(time))
}
}
func addBoundaryTimeObserver() {
// Divide the asset's duration into quarters.
let interval = CMTimeMultiplyByFloat64(videoDuration!, multiplier: 0.0001)
var currentTime = CMTime.zero
// Calculate boundary times
while currentTime < videoDuration! {
currentTime = currentTime + interval
times.append(NSValue(time:currentTime))
}
setTimeObserverToken()
}
func updateSlider(time: Float) {
playbackSlider?.setValue(time, animated: true)
}
func removeBoundaryTimeObserver() {
if let timeObserverToken = timeObserverToken {
player!.removeTimeObserver(timeObserverToken)
self.timeObserverToken = nil
}
}
func setupPlayer(movieName: String) {
let movieUrl = fileURL(for: movieName)
player = AVPlayer(url: movieUrl)
let playerFrame = CGRect(x: 0, y: 0, width: videoPlayerView.frame.width, height: videoPlayerView.frame.height)
playerViewController = AVPlayerViewController()
playerViewController!.player = player
playerViewController?.videoGravity = .resizeAspectFill
playerViewController!.view.frame = playerFrame
playerViewController!.showsPlaybackControls = false
addChild(playerViewController!)
videoPlayerView.addSubview(playerViewController!.view)
playerViewController!.didMove(toParent: self)
}
func setupPlaybackSlider() {
let sliderWidth = 300
let sliderHeight = 20
let sliderX = Int((self.view.frame.width - CGFloat(sliderWidth)) / 2.0)
let sliderY = Int(self.view.frame.height - 2.5 * self.playButton.frame.height)
playbackSlider = UISlider(frame:CGRect(x:sliderX, y: sliderY, width:sliderWidth, height:sliderHeight))
playbackSlider!.minimumValue = 0
videoDuration = (player!.currentItem?.asset.duration)!
videoDurationSeconds = CMTimeGetSeconds(videoDuration!)
playbackSlider!.maximumValue = Float(videoDurationSeconds!)
playbackSlider!.isContinuous = true
playbackSlider!.tintColor = UIColor.green
playbackSlider!.addTarget(self, action: #selector(self.playbackSliderValueChanged(_:)), for: .valueChanged)
self.view.addSubview(playbackSlider!)
addBoundaryTimeObserver()
}
override func viewDidLoad() {
super.viewDidLoad()
if let movieName = movieName {
setupPlayer(movieName: movieName)
setupPlaybackSlider();
}
let rewindText = "- \(delta) s"
let forwardText = "+ \(delta) s"
rewindButton.setTitle(rewindText, for: .normal)
forwardButton.setTitle(forwardText, for: .normal)
}
#objc func playbackSliderValueChanged(_ playbackSlider:UISlider)
{
if (isPlaying) {
pausePlayer()
}
let seconds : Int64 = Int64(playbackSlider.value)
let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 1)
player!.seek(to: targetTime)
}
func getNewLineViews(lines: [LineSegment], color: UIColor) -> [UIView] {
let widthScale = videoPlayerView.frame.width / CGFloat(inputWidth)
let heightScale = videoPlayerView.frame.height / CGFloat(inputHeight)
var lineViews = [UIView]()
for line in lines {
let view = UIView()
let path = UIBezierPath()
let start = CGPoint(x: Double(widthScale) * line.start.x,
y: Double(heightScale) * line.start.y)
let end = CGPoint(x: Double(widthScale) * line.end.x,
y: Double(heightScale) * line.end.y)
path.move(to: start)
path.addLine(to: end)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = color.cgColor
shapeLayer.lineWidth = 2.0
view.layer.addSublayer(shapeLayer)
lineViews.append(view)
}
return lineViews
}
func getLines(lines: [LineSegment]) {
let widthScale = videoPlayerView.frame.width / CGFloat(inputWidth)
let heightScale = videoPlayerView.frame.height / CGFloat(inputHeight)
for line in lines {
let view = UIView()
let path = UIBezierPath()
let start = CGPoint(x: Double(widthScale) * line.start.x,
y: Double(heightScale) * line.start.y)
let end = CGPoint(x: Double(widthScale) * line.end.x,
y: Double(heightScale) * line.end.y)
path.move(to: start)
path.addLine(to: end)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.green.cgColor
shapeLayer.lineWidth = 2.0
view.layer.addSublayer(shapeLayer)
self.lines.append(view)
}
}
func getValidRectangles(rectangles: [LineSegment?]) -> [LineSegment] {
var result = [LineSegment]()
for rectangle in rectangles {
if let rectangle = rectangle {
result.append(rectangle)
}
}
return result
}
func drawNewResults(result: Result) {
for view in self.lines {
view.removeFromSuperview()
}
let lines = getValidRectangles(rectangles: result.inferences)
self.lines = getNewLineViews(lines: lines, color: UIColor.red)
for lineView in self.lines {
videoPlayerView!.addSubview(lineView)
}
}
func pausePlayer() {
player?.pause()
playButton.setTitle("Play", for: .normal)
isPlaying = false
}
func startPlayer() {
setTimeObserverToken()
player!.play()
player!.rate = currentRate
playButton.setTitle("Pause", for: .normal)
isPlaying = true
}
#IBAction func playVideo(_ sender: Any) {
if (!isPlaying) {
startPlayer()
} else {
pausePlayer()
}
let currentTime = player!.currentTime()
updateSlider(time: Float(CMTimeGetSeconds(currentTime)))
}
#IBAction func playSlow(_ sender: Any) {
if (currentRate > 0.99) {
currentRate = slowRate
playSlowButton.setTitle("Normal", for: .normal)
} else {
currentRate = 1.0
playSlowButton.setTitle("Slow", for: .normal)
}
if (isPlaying) {
player!.rate = currentRate
}
}
#IBAction func rewindPlayer(_ sender: Any) {
if (isPlaying) {
pausePlayer()
}
let currentTime = player!.currentTime()
let zeroTime = CMTime(seconds: 0, preferredTimescale: currentTime.timescale)
let deltaTime = CMTime(seconds: delta, preferredTimescale: currentTime.timescale)
let seekTime = max(zeroTime, (currentTime - deltaTime))
player!.seek(to: seekTime, toleranceBefore: deltaTime, toleranceAfter: zeroTime)
updateSlider(time: Float(CMTimeGetSeconds(seekTime)))
}
#IBAction func forwardPlayer(_ sender: Any) {
if (isPlaying) {
pausePlayer()
}
let currentTime = player!.currentTime()
let endTime = player!.currentItem?.duration
let deltaTime = CMTime(seconds: delta, preferredTimescale: currentTime.timescale)
let seekTime = min(endTime!, (currentTime + deltaTime))
let zeroTime = CMTime(seconds: 0, preferredTimescale: currentTime.timescale)
player!.seek(to: seekTime, toleranceBefore: zeroTime, toleranceAfter: deltaTime)
updateSlider(time: Float(CMTimeGetSeconds(seekTime)))
}
override func viewWillDisappear(_ animated: Bool) {
removeBoundaryTimeObserver()
}
#IBAction func analyzeImage(_ sender: Any) {
pausePlayer()
let time = player!.currentTime()
imageFromVideo(url: fileURL(for: movieName!), at: time.seconds) { image in
let result = self.detectLines(image: image!)
if let result = result {
// Display results by handing off to the InferenceViewController.
DispatchQueue.main.async {
self.drawNewResults(result: result)
}
}
}
}
func detectLines(image: UIImage) -> Result?{
let newImage = videoPlayerView!.asImage()
let outputTensor: Tensor
guard let rgbData = newImage.scaledData(
with: CGSize(width: inputWidth, height: inputHeight),
byteCount: inputWidth * inputHeight * inputChannels
* batchSize,
isQuantized: false
)
else {
print("Failed to convert the image buffer to RGB data.")
return nil
}
do {
let interpreter = getInterpreter()!
try interpreter.copy(rgbData, toInputAt: 0)
try interpreter.invoke()
outputTensor = try interpreter.output(at: 0)
} catch {
print("Failed to invoke the interpreter with error: \(error.localizedDescription)")
return nil
}
let outputArray = outputTensor.data.toArray(type: Float32.self)
return extractLinesFromPoints(output: outputArray, outputShape: outputTensor.shape.dimensions)
}
func getInterpreter() -> Interpreter? {
var interpreter: Interpreter
let modelFilename = MobileNet.modelInfo.name
guard let modelPath = Bundle.main.path(
forResource: modelFilename,
ofType: MobileNet.modelInfo.extension
) else {
print("Failed to load the model with name: \(modelFilename).")
return nil
}
let options = Interpreter.Options()
do {
interpreter = try Interpreter(modelPath: modelPath, options: options)
try interpreter.allocateTensors()
} catch {
print("Failed to create the interpreter with error: \(error.localizedDescription)")
return nil
}
return interpreter
}
}
extension UIView {
// Using a function since `var image` might conflict with an existing variable
// (like on `UIImageView`)
func asImage() -> UIImage {
if #available(iOS 10.0, *) {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
} else {
UIGraphicsBeginImageContext(self.frame.size)
self.layer.render(in:UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return UIImage(cgImage: image!.cgImage!)
}
}
}
I have tried this and some other things, but all I get is a black image.
I have TableViewController and AudioPlayerViewController. I have a problem with using MPRemoteCommandCenter. For example: In TableViewController I click on cell and go toAudioPlayerViewController next I lock device and control my music with MPRemoteCommandCenter - all works fine. But if I further unlock device return to TableViewController go again to AudioPlayerViewController lock device and press play/pause button my music will play two times at the same time. If I will repeat the action my music will play three times at the same time. And etc... How to fix it?
code:
import UIKit
import AVFoundation
import MediaPlayer
class ViewController: UIViewController, AVAudioPlayerDelegate, UIAlertViewDelegate {
var audioPlayer: AVAudioPlayer!
let musicOperation = OperationQueue()
var timer: Timer?
#IBOutlet weak var playButton: UIButton!
#IBOutlet var timeElapsed: UILabel!
#IBOutlet var timeDuration: UILabel!
#IBOutlet weak var logo: UIImageView!
#IBOutlet weak var slider: UISlider!
#IBOutlet weak var volumeView: UIView!
var index = 0
var buttonIndex = 0
var endOfChapterSleepTimer = false
override func viewDidLoad() {
super.viewDidLoad()
// Table View Index
buttonIndex = masterIndex
musicOperation.maxConcurrentOperationCount = 1
try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
UserDefaults.standard.set(index, forKey: "index")
if index > 0 {
let fileManager = FileManager.default
let urls = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
if let documentDirectoryURL: NSURL = urls.first as NSURL? {
let soundURL = documentDirectoryURL.appendingPathComponent("/\(masterIndex)/\(index).mp3")
UserDefaults.standard.set(index, forKey: "\(masterIndex)")
do {
audioPlayer = try AVAudioPlayer(contentsOf: soundURL!)
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
play(sender:AnyObject.self as AnyObject)
restorePlayerCurrentTime()
setupMediaPlayerNotificationView()
lockScreen()
} catch {
}
}
} else {
let url = Bundle.main.url(forResource: "\(masterIndex)0", withExtension: "m4a")!
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
play(sender:AnyObject.self as AnyObject)
setupMediaPlayerNotificationView()
lockScreen()
} catch {
}
}
}
// MARK: - Audio player controller
#IBAction func play(sender: AnyObject) {
if !audioPlayer.isPlaying{
audioPlayer.play()
slider.maximumValue = Float(audioPlayer.duration)
timer = Timer(timeInterval: 0.1, target: self, selector: #selector(self.updateTime), userInfo: nil, repeats: true)
RunLoop.main.add(timer!, forMode: .commonModes)
restorePlayerCurrentTime()
playButton.setImage(UIImage(named: "pause.png"), for: UIControlState.normal)
} else {
audioPlayer.pause()
playButton.setImage(UIImage(named: "play.png"), for: UIControlState.normal)
timer?.invalidate()
}
}
#IBAction func fastForward(sender: AnyObject) {
var time: TimeInterval = audioPlayer.currentTime
time += 15.0 // Go Forward by 15 Seconds
if time > audioPlayer.duration {
audioPlayerDidFinishPlaying(audioPlayer, successfully: true)
} else {
audioPlayer.currentTime = time
updateTime()
}
}
#IBAction func fastBackward(sender: AnyObject) {
var time: TimeInterval = audioPlayer.currentTime
time -= 15.0 // Go Back by 15 Seconds
if time < 0 {
audioPlayer.currentTime = 0
updateTime()
} else {
audioPlayer.currentTime = time
updateTime()
}
}
// MARK: - Audio player time
private func restorePlayerCurrentTime() {
let currentTimeFromUserDefaults : Double? = UserDefaults.standard.value(forKey: "currentTime\(masterIndex)\(index)") as! Double?
if let currentTimeFromUserDefaultsValue = currentTimeFromUserDefaults {
audioPlayer.currentTime = currentTimeFromUserDefaultsValue
slider.value = Float.init(audioPlayer.currentTime)
}
}
#objc func updateTime() {
let currentTime = Int(audioPlayer.currentTime)
let minutes = currentTime/60
let seconds = currentTime - minutes * 60
let durationTime = Int(audioPlayer.duration) - Int(audioPlayer.currentTime)
let minutes1 = durationTime/60
let seconds1 = durationTime - minutes1 * 60
timeElapsed.text = NSString(format: "%02d:%02d", minutes,seconds) as String
timeDuration.text = NSString(format: "-%02d:%02d", minutes1,seconds1) as String
UserDefaults.standard.set(currentTime, forKey: "currentTime\(masterIndex)\(index)")
UserDefaults.standard.set(durationTime, forKey: "durationTime\(masterIndex)\(index)")
slider.value = Float.init(audioPlayer.currentTime)
}
func audioPlayerDidFinishPlaying(_ audioPlayer: AVAudioPlayer, successfully flag: Bool) {
playButton.setImage(UIImage(named: "play.png"), for: UIControlState.normal)
let currentTime = 0
let durationTime = 0.1
UserDefaults.standard.set(currentTime, forKey: "currentTime\(masterIndex)\(index)")
UserDefaults.standard.set(durationTime, forKey: "durationTime\(masterIndex)\(index)")
slider.value = Float.init(audioPlayer.currentTime)
timer?.invalidate()
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentDirectoryPath:String = path[0]
let fileManager = FileManager()
let destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appendingFormat("/\(masterIndex)/\(index+1).mp3"))
if fileManager.fileExists(atPath: destinationURLForFile.path){
index = index + 1
viewDidLoad()
} else {
}
}
// MARK: - Audio player lock screen
func lockScreen() {
var albumArtwork : MPMediaItemArtwork!
let image:UIImage = UIImage(named: "infoImage")!
albumArtwork = MPMediaItemArtwork.init(boundsSize: image.size, requestHandler: { (size) -> UIImage in
return image
})
let infotitle = "title"
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
MPMediaItemPropertyArtist : "",
MPMediaItemPropertyTitle : infotitle,
MPMediaItemPropertyArtwork : albumArtwork,
MPMediaItemPropertyAlbumTitle : "",
MPNowPlayingInfoPropertyElapsedPlaybackTime : Int(audioPlayer.currentTime),
MPMediaItemPropertyPlaybackDuration: Int(audioPlayer.duration)]
}
func setupMediaPlayerNotificationView() {
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.addTarget { event in
self.audioPlayer.play()
self.lockScreen()
self.playButton.setImage(UIImage(named: "pause.png"), for: UIControlState.normal)
print("play")
return .success
}
commandCenter.pauseCommand.addTarget { event in
self.audioPlayer.pause()
self.lockScreen()
self.playButton.setImage(UIImage(named: "play.png"), for: UIControlState.normal)
print("pause")
return .success
}
commandCenter.skipBackwardCommand.preferredIntervals = [15]
commandCenter.skipBackwardCommand.addTarget { event in
self.fastBackward(sender: self)
self.lockScreen()
print("fastBackward")
return .success
}
commandCenter.skipForwardCommand.preferredIntervals = [15]
commandCenter.skipForwardCommand.addTarget { event in
self.fastForward(sender: self)
self.lockScreen()
print("fastForward")
return .success
}
commandCenter.changePlaybackPositionCommand.addTarget(self, action: #selector(self.changedThumbSlider(_:)))
#objc func changedThumbSlider(_ event: MPChangePlaybackPositionCommandEvent) -> MPRemoteCommandHandlerStatus {
let time = event.positionTime
audioPlayer.currentTime = TimeInterval(time)
self.lockScreen()
return .success
}
// MARK: - Audio player slider
#IBAction func slide(_ slider: UISlider) {
musicOperation.cancelAllOperations()
let operation = BlockOperation()
audioPlayer.currentTime = TimeInterval(slider.value)
self.lockScreen()
musicOperation.addOperation(operation)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
audioPlayer.pause()
timer?.invalidate()
musicOperation.cancelAllOperations()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIView.setAnimationsEnabled(true)
// Navigation Bar
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.largeTitleDisplayMode = .always
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.barTintColor = UIColor(red: 55/255, green: 60/255, blue: 65/255, alpha: 1.0)
self.navigationController?.navigationBar.isTranslucent = true
self.navigationController?.navigationBar.tintColor = .white
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
whats happening is that everytime you goto AudioPlayerViewController you enable MPRemoteCommandCenter, however when you go back to TableViewController you are not calling removeTarget. this is your issue.
calling something like the below in viewWillDisappear to removeTarget
func setupMediaPlayerNotificationView(_ enable: Bool) {
let commandCenter = MPRemoteCommandCenter.shared()
if enable {
commandCenter.playCommand.addTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.pauseCommand.addTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.nextTrackCommand.addTarget(self, action: #selector(self.lockScreenNextTrack(_:)))
commandCenter.previousTrackCommand.addTarget(self, action: #selector(self.lockScreenPreviousTrack(_:)))
} else {
commandCenter.playCommand.removeTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.pauseCommand.removeTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.nextTrackCommand.removeTarget(self, action: #selector(self.lockScreenNextTrack(_:)))
commandCenter.previousTrackCommand.removeTarget(self, action: #selector(self.lockScreenPreviousTrack(_:)))
}
}
example to follow up question:
#objc func lockScreenPlay(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
self.playButton(AnyObject.self)
return .success
}
#objc func lockScreenNextTrack(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
self.playerDidFinishPlaying()
return .success
}
I want to check if device is having very slow internet connection.
I have used Reachability class to check for internet connection is available or not.
But here i want to check after every few sec that internet speed is not poor.
Is this possible to find and yes than how can i do it.
I have founded solution and adding it here.
Simply create class and paste it and use it where you want.
protocol NetworkSpeedProviderDelegate: class {
func callWhileSpeedChange(networkStatus: NetworkStatus)
}
public enum NetworkStatus :String
{case poor; case good; case disConnected}
class NetworkSpeedTest: UIViewController {
weak var delegate: NetworkSpeedProviderDelegate?
var startTime = CFAbsoluteTime()
var stopTime = CFAbsoluteTime()
var bytesReceived: CGFloat = 0
var testURL:String?
var speedTestCompletionHandler: ((_ megabytesPerSecond: CGFloat, _ error: Error?) -> Void)? = nil
var timerForSpeedTest:Timer?
func networkSpeedTestStart(UrlForTestSpeed:String!){
testURL = UrlForTestSpeed
timerForSpeedTest = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(testForSpeed), userInfo: nil, repeats: true)
}
func networkSpeedTestStop(){
timerForSpeedTest?.invalidate()
}
#objc func testForSpeed()
{
testDownloadSpeed(withTimout: 2.0, completionHandler: {(_ megabytesPerSecond: CGFloat, _ error: Error?) -> Void in
print("%0.1f; KbPerSec = \(megabytesPerSecond)")
if (error as NSError?)?.code == -1009
{
self.delegate?.callWhileSpeedChange(networkStatus: .disConnected)
}
else if megabytesPerSecond == -1.0
{
self.delegate?.callWhileSpeedChange(networkStatus: .poor)
}
else
{
self.delegate?.callWhileSpeedChange(networkStatus: .good)
}
})
}
}
extension NetworkSpeedTest: URLSessionDataDelegate, URLSessionDelegate {
func testDownloadSpeed(withTimout timeout: TimeInterval, completionHandler: #escaping (_ megabytesPerSecond: CGFloat, _ error: Error?) -> Void) {
// you set any relevant string with any file
let urlForSpeedTest = URL(string: testURL!)
startTime = CFAbsoluteTimeGetCurrent()
stopTime = startTime
bytesReceived = 0
speedTestCompletionHandler = completionHandler
let configuration = URLSessionConfiguration.ephemeral
configuration.timeoutIntervalForResource = timeout
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
guard let checkedUrl = urlForSpeedTest else { return }
session.dataTask(with: checkedUrl).resume()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
bytesReceived += CGFloat(data.count)
stopTime = CFAbsoluteTimeGetCurrent()
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
let elapsed = (stopTime - startTime) //as? CFAbsoluteTime
let speed: CGFloat = elapsed != 0 ? bytesReceived / (CGFloat(CFAbsoluteTimeGetCurrent() - startTime)) / 1024.0 : -1.0
// treat timeout as no error (as we're testing speed, not worried about whether we got entire resource or not
if error == nil || ((((error as NSError?)?.domain) == NSURLErrorDomain) && (error as NSError?)?.code == NSURLErrorTimedOut) {
speedTestCompletionHandler?(speed, nil)
}
else {
speedTestCompletionHandler?(speed, error)
}
}
}
After That how to use it.So implement delegate and use it.
class ViewController: UIViewController, NetworkSpeedProviderDelegate {
func callWhileSpeedChange(networkStatus: NetworkStatus) {
switch networkStatus {
case .poor:
break
case .good:
break
case .disConnected:
break
}
}
let test = NetworkSpeedTest()
override func viewDidLoad() {
super.viewDidLoad()
test.delegate = self
test.networkSpeedTestStop()
test.networkSpeedTestStart(UrlForTestSpeed: "Paste Your Any Working URL ")
// Do any additional setup after loading the view.
}
}
You can call the Reachability class every time after a fixed interval by using method given below:
override func viewDidLoad() {
scheduledTimerWithTimeInterval()
}
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function "updateCounting" with the interval of 'x' seconds
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateCounting), userInfo: nil, repeats: true)
}
#objc func updateCounting(){
\\Do your stuff here(Check Reachabilty Here)
}
EDIT:
This is how you can check signal strength for cellular networks.
func getSignalStrength() -> Int {
let application = UIApplication.shared
let statusBarView = application.value(forKey: "statusBar") as! UIView
let foregroundView = statusBarView.value(forKey: "foregroundView") as! UIView
let foregroundViewSubviews = foregroundView.subviews
var dataNetworkItemView:UIView? = nil
for subview in foregroundViewSubviews {
if subview.isKind(of: NSClassFromString("UIStatusBarSignalStrengthItemView")!) {
dataNetworkItemView = subview
break
}
}
if dataNetworkItemView == nil
{
return 0
}
return dataNetworkItemView?.value(forKey: "signalStrengthBars") as! Int
}
For Wifi Network this is how you can get signal strength
private func getWiFiRSSI() -> Int? {
let app = UIApplication.shared
var rssi: Int?
let exception = tryBlock {
guard let statusBar = app.value(forKey: "statusBar") as? UIView else { return }
if let statusBarMorden = NSClassFromString("UIStatusBar_Modern"), statusBar .isKind(of: statusBarMorden) { return }
guard let foregroundView = statusBar.value(forKey: "foregroundView") as? UIView else { return }
for view in foregroundView.subviews {
if let statusBarDataNetworkItemView = NSClassFromString("UIStatusBarDataNetworkItemView"), view .isKind(of: statusBarDataNetworkItemView) {
if let val = view.value(forKey: "wifiStrengthRaw") as? Int {
rssi = val
break
}
}
}
}
if let exception = exception {
print("getWiFiRSSI exception: \(exception)")
}
return rssi
}
EDIT 2: Add this extension to access your status bar view
extension UIApplication {
var statusBarUIView: UIView? {
if #available(iOS 13.0, *) {
let tag = 38482458385
if let statusBar = self.keyWindow?.viewWithTag(tag) {
return statusBar
} else {
let statusBarView = UIView(frame: UIApplication.shared.statusBarFrame)
statusBarView.tag = tag
self.keyWindow?.addSubview(statusBarView)
return statusBarView
}
} else {
if responds(to: Selector(("statusBar"))) {
return value(forKey: "statusBar") as? UIView
}
}
return nil
}
}
I have an issue.
I get an error below in the code saying: "Cannot convert value type 'Node' to expected argument type 'UIViewController' How to solve this?
the code for the document view controller is below:
import UIKit
import WebKit
import AEXML
import STPopup
import Kanna
import DropDownMenuKit
import Toaster
typealias DocumentOpenAction = (_ node: Node) -> ()
class DocumentViewController: BaseViewController {
var currentNode:Node! {
didSet {
if popupController == nil {
navigationItem.titleView = UILabel.forTitleView(withText: currentNode.title)
if titleView != nil {
navigationItem.titleView = titleView
}
} else {
navigationItem.titleView = UILabel.forTitleView(withText: currentNode.title)
}
}
}
//ausgegebene HTML's
var sections:[[String:String]] = []
var changeMarksVisible:Bool? {
didSet {
self.updateChangeMarking()
}
}
var nightModeOn:Bool? {
didSet {
self.updateNightMode()
}
}
var readAccessFolder:URL?
lazy var statusBarHeight: CGFloat = {
let statusBarSize = UIApplication.shared.statusBarFrame.size
return min(statusBarSize.width, statusBarSize.height)
}()
//
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(DocumentViewController.taskGotDeleted(withNotification:)), name: PackageManager.shared.tasksDeletedNotification, object: nil)
contentSizeInPopup = CGSize(width: 600, height: 500)
landscapeContentSizeInPopup = CGSize(width: 600, height: 500)
changeMarksVisible = UserDefaults.standard.bool(forKey: "ChangeMarkings")
nightModeOn = UserDefaults.standard.bool(forKey: "nightTheme")
if popupController == nil {
PackageManager.shared.setCurrentNodeAndAddToHistory(currentNode)
setupNavBar()
// create the drop down menu
createDropDownMenu()
//
jumpToGraphicsViewButton.addTarget(self, action: #selector(DocumentViewController.showGraphicsView), for: .touchUpInside)
jumpToGraphicsViewButtonLeadingConstraint.constant = 8.0
jumpToGraphicsViewButtonWidthConstraint.constant = 48.0
jumpToGraphicsViewButton.isEnabled = false
jumpToGraphicsViewButton.isHidden = false
} else {
setupNavBarForPreview()
//
jumpToGraphicsViewButton.isHidden = true
jumpToGraphicsViewButtonLeadingConstraint.constant = 0.0
jumpToGraphicsViewButtonWidthConstraint.constant = 0.0
}
// webview
setupWebview()
// process html of current node
splitHTML(forCurrentNode: currentNode)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
PackageManager.shared.currentDisplayedModuleNode = currentNode
navigationBarMenu?.container = view
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (ctx) in
self.show3dModelButtonLeadConstraint?.constant = self.view.frame.width - 50
self.show3dModelButton?.updateConstraintsIfNeeded()
self.webview?.scrollView.updateConstraintsIfNeeded()
// If we put this only in -viewDidLayoutSubviews, menu animation is
// messed up when selecting an item
self.updateMenuContentOffsets()
}) { (ctx) in
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
URLCache.shared.removeAllCachedResponses()
}
deinit {
NotificationCenter.default.removeObserver(self)
webview?.scrollView.delegate = nil
}
func showListOfContent(_ sender: AnyObject) {
_ = navigationController?.popViewController(animated: true)
}
}
// MARK: - UI
extension DocumentViewController {
func setupWebview() {
//Content Controller object
let controller = WKUserContentController()
//Add message handler reference
controller.add(self, name: "shoppingcart")
//Create configuration
let configuration = WKWebViewConfiguration()
configuration.userContentController = controller
var f = webViewView.frame
f.origin.y = 0
webview = WKWebView(frame: f, configuration: configuration)
webview?.configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
webview?.backgroundColor = .white
webview?.scrollView.bounces = true
webview?.scrollView.delegate = self
webview?.navigationDelegate = self
webview?.uiDelegate = self
webViewView.addSubview(webview!)
webview?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
func setupNavBar() {
//loads nav bar if not presented in popup
settingsButton = UIBarButtonItem(image: UIImage(named: "Settings"), style: .plain, target: self, action: #selector(DocumentViewController.showSettings(_:)))
navigationItem.rightBarButtonItems = [noticeButton,settingsButton]
listOfContentButton = UIBarButtonItem(image: UIImage(named: "List"), style: .plain, target: self, action: #selector(DocumentViewController.showListOfContent(_:)))
shoppingCartButton = UIBarButtonItem(image: UIImage(named: "Checkout"), style: .plain, target: self, action: #selector(DocumentViewController.shoppingCartButtonPressed(_:)))
taskListButton = ZABarButtonItem(image: UIImage(named: "TaskList"), style: .plain, target: self, action: #selector(DocumentViewController.taskListButtonPressed(_:)))
taskListButton.isSelected = PackageManager.shared.nodeIsInTasks(currentNode)
taskListButton.tintColor = taskListButton.isSelected ? navigationController?.navigationBar.tintColor : .lightGray
favoritesButton = ZABarButtonItem(image: UIImage(named: "Star"), style: .plain, target: self, action: #selector(DocumentViewController.favoritesButtonPressed(_:)))
favoritesButton.isSelected = PackageManager.shared.nodeIsInFavorites(currentNode)
let img = favoritesButton.isSelected ? UIImage(named: "StarFilled"): UIImage(named: "Star")
favoritesButton.image = img
backToManualStructure = UIBarButtonItem(image: UIImage(named: "List"), style: .plain, target: self, action: #selector(DocumentViewController.structureButtonPressed(_:)))
var items:[UIBarButtonItem] = [backToManualStructure,shoppingCartButton, favoritesButton]
if currentNode.canBeAddedToTasks() {
items.append(taskListButton)
}
navigationItem.leftBarButtonItems = items
}
func setupNavBarForPreview() {
//loads nav bar if presented in popup
closeButton = UIBarButtonItem(title: NSLocalizedString("Close", tableName: "Localizable", bundle: Bundle.main, value: "", comment: ""), style: .plain, target: self, action: #selector(DocumentViewController.cancel))
navigationItem.leftBarButtonItems = [closeButton]
navigationItem.leftItemsSupplementBackButton = false
openButton = UIBarButtonItem(title: NSLocalizedString("Open in new tab", tableName: "Localizable", bundle: Bundle.main, value: "", comment: ""), style: .plain, target: self, action: #selector(DocumentViewController.openDocument))
navigationItem.rightBarButtonItems = [openButton]
}
}
// MARK: - DropDownMenu
extension DocumentViewController {
func createDropDownMenu() {
// create the drop down menu
let title = prepareNavigationBarMenuTitleView()
prepareNavigationBarMenu(title)
updateMenuContentOffsets()
}
func prepareNavigationBarMenuTitleView() -> String {
// Both title label and image view are fixed horizontally inside title
// view, UIKit is responsible to center title view in the navigation bar.
// We want to ensure the space between title and image remains constant,
// even when title view is moved to remain centered (but never resized).
titleView = DropDownTitleView(frame: CGRect(x: 0, y: 0, width: 150, height: 40))
titleView.addTarget(self,
action: #selector(DocumentViewController.willToggleNavigationBarMenu(_:)),
for: .touchUpInside)
titleView.addTarget(self,
action: #selector(DocumentViewController.didToggleNavigationBarMenu(_:)),
for: .valueChanged)
titleView.titleLabel.textColor = UIColor.black
titleView.title = currentNode.title
navigationItem.titleView = titleView
return titleView.title!
}
func prepareNavigationBarMenu(_ currentChoice: String) {
navigationBarMenu = DropDownMenu(frame: view.bounds)
navigationBarMenu?.delegate = self
var cells:[DropDownMenuCell] = []
for (index, doc) in PackageManager.shared.openNodes.enumerated() {
let cell = DropDownMenuCell()
cell.textLabel!.text = doc.title
cell.tag = index
cell.menuAction = #selector(DocumentViewController.choose(_:))
cell.menuTarget = self
if currentChoice == doc.title {
cell.accessoryType = .checkmark
}
cells.append(cell)
}
navigationBarMenu?.menuCells = cells
// If we set the container to the controller view, the value must be set
// on the hidden content offset (not the visible one)
navigationBarMenu?.visibleContentOffset = (navigationController?.navigationBar.frame.size.height ?? 44.0) + statusBarHeight
// For a simple gray overlay in background
navigationBarMenu?.backgroundView = UIView(frame: view.bounds)
navigationBarMenu?.backgroundView!.backgroundColor = UIColor.black
navigationBarMenu?.backgroundAlpha = 0.7
}
func updateMenuContentOffsets() {
navigationBarMenu?.visibleContentOffset = (navigationController?.navigationBar.frame.size.height ?? 44.0) + statusBarHeight
}
#IBAction func choose(_ sender: AnyObject) {
let cell = (sender as! DropDownMenuCell)
titleView.title = cell.textLabel!.text
let index = cell.tag
let node = PackageManager.shared.openNodes[index]
if node != currentNode {
initalSegment = 0
currentNode = node
setupNavBar()
splitHTML(forCurrentNode: currentNode)
}
if let menu = navigationBarMenu {
didTapInDropDownMenuBackground(menu)
}
}
#IBAction func willToggleNavigationBarMenu(_ sender: DropDownTitleView) {
sender.isUp ? navigationBarMenu?.hide() : navigationBarMenu?.show()
}
#IBAction func didToggleNavigationBarMenu(_ sender: DropDownTitleView) {
}
}
// MARK: - DropDownMenuDelegate
extension DocumentViewController : DropDownMenuDelegate {
func didTapInDropDownMenuBackground(_ menu: DropDownMenu) {
if menu == navigationBarMenu {
titleView.toggleMenu()
}
else {
menu.hide()
}
}
}
// MARK: - data module
extension DocumentViewController {
func splitHTML(forCurrentNode node:Node) {
node.deleteTemporaryHtmls()
if let nodePath = node.path {
let pathComponents = URL(fileURLWithPath: nodePath).pathComponents
if pathComponents.count >= 2 {
let rootFolder = "\(pathComponents[1])"
readAccessFolder = URL(fileURLWithPath: "\(PackageManager.shared.packageFolder)\(rootFolder)")
}
}
// segmented control
segmentedControl.removeAllSegments()
// process html of current node
do {
sections = try node.processHTMLSections()
if sections.count > 0 {
var index = 0
for sec in sections {
segmentedControl.insertSegment(withTitle: sec["title"], at: index, animated: false)
index += 1
}
//
let hasWCN = sections.first(where: { (sec) -> Bool in
return sec["contenttype"] == "safety"
})
if let _ = hasWCN {
} else {
let index = sections.index(where: { (sec) -> Bool in
return sec["contenttype"] == "container"
})
if let i = index {
initalSegment = i
}
}
//
if initalSegment > 5 {
initalSegment = 0
loadHTML(forSection: initalSegment)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
self.show3DModel(self.show3dModelButton as Any)
})
}
else {
if initalSegment > sections.count {
initalSegment = 0
}
loadHTML(forSection: initalSegment)
}
segmentedControl.selectedSegmentIndex = initalSegment
// configure show 3d model button
let button = UIButton(forAutoLayout: ())
button.imageView?.contentMode = .scaleAspectFit
button.imageView?.tintColor = .gray
button.setImage(UIImage(named: "Next"), for: .normal)
button.backgroundColor = UIColor.lightGray.withAlphaComponent(0.3)
button.borderColor = .black
button.borderWidth = 1.0
button.addTarget(self, action: #selector(DocumentViewController.show3DModel(_:)), for: .touchUpInside)
webViewView.addSubview(button)
button.autoAlignAxis(.horizontal, toSameAxisOf: webViewView)
button.autoSetDimension(.height, toSize: 150.0)
button.autoSetDimension(.width, toSize: 50.0)
show3dModelButtonLeadConstraint = button.autoPinEdge(.trailing, to: .trailing, of: webViewView, withOffset: 0)
show3dModelButton = button
// we dont need the button any longer
show3dModelButton?.isHidden = true
}
} catch {
loadPDF()
segmentedControlHeight.constant = 0
segmentedControlMarginTop.constant = 0
segmentedControlMarginBottom.constant = 0
}
if let dmc = node.moduleCode {
dmcLabel.text = dmc
}
if let issno = node.issueNumber {
issnoLabel.text = issno
}
if let issdate = node.issueDate {
issdateLabel.text = DateFormatter.stringFromDate(issdate, format: "yyyy-MM-dd")
}
let edgePan = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(returnToGraphicView))
edgePan.edges = .right
view.addGestureRecognizer(edgePan)
}
func loadHTML(forSection section: Int) {
URLCache.shared.removeAllCachedResponses()
URLCache.shared.diskCapacity = 0
URLCache.shared.memoryCapacity = 0
if section > sections.count {
return
}
if webview?.isLoading == true {
webview?.stopLoading()
}
if let urlString = sections[section]["url"], let contenttype = sections[section]["contenttype"], let readAccessFolder = readAccessFolder {
var url = URL(fileURLWithPath: urlString)
if var components = URLComponents(url: url, resolvingAgainstBaseURL: false), contenttype == "requirements" {
let sc = URLQueryItem(name: "shoppingcart", value: "true")
let domain = URLQueryItem(name: "domain", value: "*")
components.queryItems = [sc, domain]
if let newUrl = components.url {
url = newUrl
}
}
_ = webview?.loadFileURL(url, allowingReadAccessTo: readAccessFolder)
}
}
func loadPDF() {
URLCache.shared.removeAllCachedResponses()
URLCache.shared.diskCapacity = 0
URLCache.shared.memoryCapacity = 0
if webview?.isLoading == true {
webview?.stopLoading()
}
let fileStr = "\(PackageManager.shared.packageFolder)\(currentNode.path ?? "")"
let url = URL(fileURLWithPath: fileStr)
let request = URLRequest(url: url)
if let readAccessFolder = readAccessFolder {
_ = webview?.loadFileURL(url, allowingReadAccessTo: readAccessFolder)
} else {
_ = webview?.load(request)
}
}
}
// MARK: - Illustrations
extension DocumentViewController {
func showFullscreenFigure(with boardNo: String, and components: URLComponents?) {
if let gv = self.storyboard?.instantiateViewController(withIdentifier: "GraphicsViewController") as? GraphicsViewController {
gv.url = URL(fileURLWithPath: "\(PackageManager.shared.packageFolder)\(currentNode.path ?? "").\(boardNo).$tmp.htm")
gv.caption = components?.queryItems?.filter({ (item) in item.name == "caption"}).first?.value
gv.javascript = components?.queryItems?.filter({ (item) in item.name == "javascript"}).first?.value
gv.readAccessFolder = readAccessFolder
graphicsView = gv
}
if popupController != nil {
popupController?.push(graphicsView!, animated: true)
} else {
navigationController?.pushViewController(graphicsView!, animated: true)
}
}
func showGraphicsView() {
if let graphicsView = graphicsView {
if popupController != nil {
popupController?.push(graphicsView, animated: true)
} else {
navigationController?.pushViewController(graphicsView, animated: true)
}
}
}
func returnToGraphicView(_ recognizer: UIScreenEdgePanGestureRecognizer) {
if recognizer.state == .recognized {
showGraphicsView()
}
}
}
// MARK: - Preview
extension DocumentViewController {
func cancel() {
onClose?()
}
func openDocument() {
onOpen?(currentNode)
}
func showPreview(for node:Node) {
let onOpen:DocumentOpenAction = { node in
self.currentNode = node
PackageManager.shared.setCurrentNodeAndAddToHistory(self.currentNode)
self.initalSegment = 0
self.splitHTML(forCurrentNode: self.currentNode)
self.createDropDownMenu()
}
let contentView = self.storyboard?.instantiateViewController(withIdentifier: "DocumentViewController") as! DocumentViewController
contentView.initalSegment = 0
contentView.currentNode = node
//
if popupController != nil {
contentView.onOpen = self.onOpen
contentView.onClose = self.onClose
self.popupController?.push(contentView, animated: true)
} else {
let popup = STPopupController(rootViewController: contentView)
popup.style = .formSheet
popup.hidesCloseButton = false
popup.backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
popup.containerView.layer.cornerRadius = 4
contentView.onOpen = { node in
popup.dismiss(completion: {
onOpen(node)
})
}
contentView.onClose = {
popup.dismiss(completion: {
// we dont need to split the html again for now
// self.splitHTML(forCurrentNode: self.currentNode)
})
}
popup.present(in: self, completion: nil)
}
contentView.navigationItem.titleView = UILabel.forTitleView(withText: node.title)
}
func showProcedureStepCheckboxes() {
if popupController == nil {
self.webview?.evaluateJavaScript("showProcedureStepCheckboxes()", completionHandler: {(result, error) in
guard let err = error else {return}
print(err)
})
}
}
func setLastScrollingPosition() {
if let code = currentNode.contentobjectcode {
webview?.restoreContentOffset(forKey: code)
}
}
}
// MARK: - Settings
extension DocumentViewController {
func showSettings(_ sender: AnyObject) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "SetSettingsViewController") as! SetSettingsViewController
vc.modalPresentationStyle = .popover
vc.onChange = {
self.changeMarksVisible = (UserDefaults.standard.bool(forKey: "ChangeMarkings"))
self.nightModeOn = (UserDefaults.standard.bool(forKey: "nightTheme"))
}
if let popover = vc.popoverPresentationController {
popover.barButtonItem = settingsButton
popover.permittedArrowDirections = .any
}
present(vc, animated: true, completion: nil)
}
func updateChangeMarking() {
if (self.changeMarksVisible ?? false) {
self.webview?.evaluateJavaScript("handleChangemarks()", completionHandler: {(result, error) in
})
}
else {
self.webview?.evaluateJavaScript("deleteChangemarks()", completionHandler: {(result, error) in
})
}
}
func updateNightMode() {
let mode = self.nightModeOn ?? false
self.webview?.evaluateJavaScript("s1000d.setNightMode(\(mode))", completionHandler: {(result, error) in
})
}
}
// Mark: - Structure Manual
extension DocumentViewController {
func structureButtonPressed(_ sender: AnyObject) {
PackageManager.shared.structureButtonPressed(currentNode)
if let root = self.parent?.parent as? RootTabBarViewController {
root.selectedIndex = 0
if let navController = root.viewControllers?.first as? UINavigationController, let firstController = navController.viewControllers.first as? ManualsViewController {
PackageManager.shared.strucutreArrayNode.removeLast()
for var i in PackageManager.shared.strucutreArrayNode {
print("\(i.title)")
// get next child node
if let next = i.nextChild() {
i = next
}
if i.type == "document" || i.subnodes.count == 0 {
firstController.showDocumentViewController(for: i)
} else {
firstController.navigationController?.pushViewController(i, animated: false)
}
}
}
}
}
}
I have a form in which I am displaying User existing data in Textfield and there is a profile image upload option as well. So lets say If I edit some fields in the textbox and then upload image from using imagePicker, then the data resets and it shows the old values. I don't know how can I fix this
My Code is this :-
class ProfileVC: UIViewController, TopViewDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate,UITextFieldDelegate {
#IBOutlet weak var topView: WATopView!
#IBOutlet weak var firstName: WATextField!
#IBOutlet weak var lastName: WATextField!
#IBOutlet weak var email: WATextField!
// #IBOutlet weak var password: WATextField!
#IBOutlet weak var university: WATextField!
#IBOutlet weak var graduationDate: WATextField!
#IBOutlet weak var major: WATextField!
#IBOutlet weak var homeTown: WATextField!
#IBOutlet weak var industry: WATextField!
#IBOutlet var profileImageView: WAImageView!
#IBOutlet weak var imagePickerButton:WAButton!
var pickerView = UIPickerView()
var uniList = [University]()
var industryList = [Industry]()
var selectedIndustry: Industry?
var selectedUni: University?
let imagePicker = UIImagePickerController()
var base64StringOf_my_image = String()
var prepCompressedImage: UIImage!
var isDismissing: Bool = false
let currentUser = User.getCurrentUser()
var inputViews = UIView()
let datePickerView = UIDatePicker()
let profileLoader = ProfileLoader()
override func viewDidLoad() {
super.viewDidLoad()
if fireNotification.pickedImage != nil {
profileImageView.image = fireNotification.pickedImage!.circle
self.prepCompressedImage = UIImage.compressImage(fireNotification.pickedImage, compressRatio: 0.9)
base64StringOf_my_image = appUtility.base64StringOfImage(self.prepCompressedImage)
fireNotification.pickedImage = nil
}else {
profileImageView.sd_setImageWithURL(NSURL(string: currentUser!.userImageURL), placeholderImage: UIImage(named:"no-image"))
self.prepCompressedImage = UIImage.compressImage(profileImageView.image, compressRatio: 0.9)
base64StringOf_my_image = appUtility.base64StringOfImage(self.prepCompressedImage)
}
pickerView.delegate = self
pickerView.dataSource = self
loadIndAndUni()
topView.delegate = self
disableEditing(false)
if !(currentUser?.firstName.isEmpty)! {
firstName.text = currentUser?.firstName
}
if !(currentUser?.lastName.isEmpty)! {
lastName.text = currentUser?.lastName
}
if !(currentUser?.email.isEmpty)! {
email.text = currentUser?.email
}
if !(currentUser?.uni_name.isEmpty)! {
university.text = currentUser?.uni_name
}
if !(currentUser?.startGradDate.isEmpty)! {
graduationDate.text = currentUser?.startGradDate
}
if !(currentUser?.major.isEmpty)! {
major.text = currentUser?.major
}
if !(currentUser?.homeTown.isEmpty)! {
homeTown.text = currentUser?.homeTown
}
if !(currentUser?.ind_name.isEmpty)! {
industry.text = currentUser?.ind_name
}
imagePickerButton.userInteractionEnabled = false
}
func loadIndAndUni(){
if fireNotification.uniList != nil {
uniList = fireNotification.uniList!
}else { self.loadUni()}
if fireNotification.indList != nil {
industryList = fireNotification.indList!
}else {self.loadIndustries()}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func editDoneButtonAction(sender: UIButton) {
if sender.titleLabel?.text == "Edit" {
topView.doneText = "Done"
// PrepUtility.showAlertWithTitle("Edit", error: "You are going to submit data", OKButton: "Edit", VC: self)
imagePickerButton.userInteractionEnabled = true
disableEditing(true)
}
if sender.titleLabel?.text == "Done" {
// PrepUtility.showAlertWithTitle("Done", error: "You are going to submit data", OKButton: "Done", VC: self)
submitRequest()
topView.doneText = "Edit"
disableEditing(false)
}
}
func submitRequest(){
let (isValid , error) = validateFields()
if !isValid {
appUtility.showAlert(error!,VC: self)
return
}
if (base64StringOf_my_image.isEmpty) {
appUtility.showAlert("Please Select Image",VC: self)
return
}
if PrepUtility.connectedToNetwork() {
appUtility.loadingView(self, isHide: false)
if selectedUni != nil {
let indText = industry.text
var selectedI: Industry?
for t in industryList {
if t.ind_name == indText {
selectedI = t
}
}
let params = ["std_id":currentUser!.std_id,"std_f_name":firstName.text!,"std_l_name":lastName.text!,"std_grad_date":graduationDate.text!,"std_hometown":homeTown.text!,"std_major":major.text!,"uni_id":selectedUni!.uni_id,"std_img":["content_type": "image/png", "filename":"test.png", "file_data": base64StringOf_my_image],"ind_id":selectedI!.ind_id]
editProfile(params)
}else {
let uniText = university.text
var selectedU: University?
for t in uniList {
if t.uni_name == uniText {
selectedU = t
}
}
let indText = industry.text
var selectedI: Industry?
for t in industryList {
if t.ind_name == indText {
selectedI = t
}
}
if selectedU!.uni_id.boolValue {
let params = ["std_id":currentUser!.std_id,"std_f_name":firstName.text!,"std_l_name":lastName.text!,"std_grad_date":graduationDate.text!,"std_hometown":homeTown.text!,"std_major":major.text!,"uni_id":selectedU!.uni_id,"std_img":["content_type": "image/png", "filename":"test.png", "file_data": base64StringOf_my_image],"ind_id":selectedI!.ind_id]
editProfile(params)
}
}
}else {
appUtility.loadingView(self, isHide: true)
appUtility.showNoNetworkAlert()
}
}
func editProfile(params:[String:AnyObject]) {
profileLoader.tryStudentProfileEdit(params, successBlock: { (user) in
appUtility.loadingView(self, isHide: true)
}, failureBlock: { (error) in
// appUtility.showAlert((error?.userInfo["NSLocalizedDescription"])! as! String, VC: self)
if let err = error?.userInfo["NSLocalizedDescription"] {
appUtility.showAlert(err as! String, VC: self)
appUtility.loadingView(self, isHide: true)
}else {
appUtility.loadingView(self, isHide: true)
appUtility.showAlert("Something went wrong", VC: self)
}
})
}
func openMenu() {
firstName.resignFirstResponder()
lastName.resignFirstResponder()
// email.resignFirstResponder()
// password.resignFirstResponder()
university.resignFirstResponder()
graduationDate.resignFirstResponder()
major.resignFirstResponder()
homeTown.resignFirstResponder()
industry.resignFirstResponder()
self.mainSlideMenu().openLeftMenu()
}
//MARK:- Private Methods
private func validateFields() -> (Bool , String?) {
if !firstName.isValid() {
hideView()
return (false , "Please enter your First Name")
}
if !lastName.isValid() {
hideView()
return (false , "Please enter your Last Name")
}
// if !email.isValid() {
// hideView()
// return (false , "Please enter email")
// }
//
// if email.isValid() {
// hideView()
// let text = email.text
// if !(text!.hasSuffix(".edu")) {
// return (false , "Please enter email that having .edu")
// }
// if !email.text!.isValidEmail() {
// return (false , "Please Enter a valid email...")
// }
// }
if !university.isValid() {
hideView()
// return (false , "Please select University")
}
if !homeTown.isValid() {
hideView()
// return (false , "Please enter address")
}
// if !password.isValid() {
// return (false , "Please enter password")
// }
//
return (true , nil)
}
private func showGradDatePicker() {
//Create the view
graduationDate.inputView = inputViews
inputViews = UIView(frame: CGRectMake(0, self.view.frame.height, self.view.frame.width, 240))
inputViews.backgroundColor = UIColor.whiteColor()
let datePickerView : UIDatePicker = UIDatePicker(frame: CGRectMake(0, 40, 0, 0))
datePickerView.datePickerMode = UIDatePickerMode.Date
inputViews.addSubview(datePickerView) // add date picker to UIView
let doneButton = UIButton(frame: CGRectMake((self.view.frame.size.width/2) - (100/2), 0, 100, 50))
doneButton.setTitle("Done", forState: UIControlState.Normal)
doneButton.setTitle("Done", forState: UIControlState.Highlighted)
doneButton.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
doneButton.setTitleColor(UIColor.grayColor(), forState: UIControlState.Highlighted)
inputViews.addSubview(doneButton) // add Button to UIView
doneButton.addTarget(self, action: #selector(ScheduleVC.doneButton(_:)), forControlEvents: UIControlEvents.TouchUpInside) // set button click event
// sender.inputView = inputView
datePickerView.addTarget(self, action: #selector(ScheduleVC.datePickerValueChanged(_:)), forControlEvents: UIControlEvents.ValueChanged)
self.view.addSubview(inputViews)
datePickerValueChanged(datePickerView) // Set the date on start.
UIView.animateWithDuration(0.5) {
self.inputViews.frame = CGRect(x: 0, y: self.view.frame.height - 270, width: self.view.frame.width, height: 240)
}
}
func doneButton(sender:UIButton) {
hideView()
}
func hideView() {
UIView.animateWithDuration(0.9) {
self.inputViews.frame = CGRect(x: 0, y:0, width: self.view.frame.width, height: 240)
self.inputViews.removeFromSuperview()
}
}
func datePickerValueChanged(sender:UIDatePicker) {
// isDateSet = true
let dateFormatter = NSDateFormatter()
// dateFormatter.dateStyle = NSDateFormatterStyle.ShortStyle
dateFormatter.dateFormat = "yyyy-MM-dd"
// dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
graduationDate.text = dateFormatter.stringFromDate(sender.date)
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
if textField == university {
self.resignFirstResponder(university)
industry.tag = 1
university.tag = 0
graduationDate.tag = 1
pickerViewToolBar(textField)
}
if textField == industry {
self.resignFirstResponder(industry)
industry.tag = 0
university.tag = 1
graduationDate.tag = 1
pickerViewToolBar(textField)
}
if textField == graduationDate{
industry.tag = 1
university.tag = 1
graduationDate.tag = 0
self.resignFirstResponder(graduationDate)
showGradDatePicker()
}
return true
}
private func disableEditing(boo: Bool) {
firstName.enabled = boo
lastName.enabled = boo
// email.enabled = boo
// password.enabled = boo
university.enabled = boo
graduationDate.enabled = boo
major.enabled = boo
homeTown.enabled = boo
industry.enabled = boo
}
private func resignFirstResponder(textField:UITextField) {
if textField != firstName {
// hideView()
textField.resignFirstResponder()
}
if textField != lastName {
// hideView()
textField.resignFirstResponder()
}
// if textField != email{
//// hideView()
// textField.resignFirstResponder()
// }
// if textField != password {
//// hideView()
// textField.resignFirstResponder()
// }
if textField != university {
// hideView()
textField.resignFirstResponder()
}
if textField != graduationDate {
textField.resignFirstResponder()
}
if textField != major {
// hideView()
textField.resignFirstResponder()
}
if textField != homeTown {
// hideView()
textField.resignFirstResponder()
}
if textField != industry {
// hideView()
textField.resignFirstResponder()
}
}
private func resignAllTextFields() {
firstName.resignFirstResponder()
}
//MARK:- Public Methods
func pickerViewToolBar(sender: UITextField) {
var doneButton:UIBarButtonItem?
if university.tag == 0 {
university.inputView = pickerView
}
if industry.tag == 0 {
industry.inputView = pickerView
}
if graduationDate.tag == 0 {
graduationDate.inputView = pickerView
}
// departureDatePickerView.datePickerMode = UIDatePickerMode.Date
// Sets up the "button"
// Creates the toolbar
let toolBar = UIToolbar()
toolBar.barStyle = .Default
toolBar.translucent = true
toolBar.tintColor = UIColor(red: 92/255, green: 216/255, blue: 255/255, alpha: 1)
toolBar.sizeToFit()
// Adds the buttons
if(sender.tag == 0){
// self.nextButton?.hidden = true
doneButton = UIBarButtonItem(title: "Done", style: .Plain, target: self, action: #selector(PrepInfoVC.doneClick(_:)))
doneButton!.tag = 0
}
// else if(sender.tag == 2){
//// self.nextButton?.hidden = true
// doneButton = UIBarButtonItem(title: "Done", style: .Plain, target: self, action: #selector(PrepInfoVC.doneClick(_:)))
// doneButton!.tag = 2
// }
let spaceButton = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: .Plain, target: self, action: #selector(PrepInfoVC.cancelClick))
toolBar.setItems([cancelButton, spaceButton, doneButton!], animated: false)
toolBar.userInteractionEnabled = true
// Adds the toolbar to the view
if university.tag == 0 {
university.inputAccessoryView = toolBar
}
if industry.tag == 0 {
industry.inputAccessoryView = toolBar
}
if graduationDate.tag == 0 {
graduationDate.inputAccessoryView = toolBar
}
}
func doneClick(sender:UIBarButtonItem) {
if industry.tag == 0 {
selectedIndustry = industryList[pickerView.selectedRowInComponent(0)]
industry.text = selectedIndustry!.ind_name
industry.tag = 1
industry.resignFirstResponder()
}else{
selectedUni = uniList[pickerView.selectedRowInComponent(0)]
university.text = selectedUni!.uni_name
university.tag = 1
university.resignFirstResponder()
}
}
func cancelClick() {
if industry.tag == 0 {
industry.tag = 1
industry.resignFirstResponder()
}else {
university.tag = 1
university.resignFirstResponder()
}
}
//MARK:- ImagePicker
#IBAction func loadImageButtonTapped(sender: UIButton) {
imagePicker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
imagePicker.allowsEditing = true
imagePicker.delegate = self
self.hidesBottomBarWhenPushed = true
self.navigationController?.presentViewController(imagePicker, animated: true, completion: nil)
self.hidesBottomBarWhenPushed = false
}
// MARK: - UIImagePickerControllerDelegate Methods
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
print(info)
if let pickedImage = info[UIImagePickerControllerEditedImage] as? UIImage {
profileImageView.image = pickedImage.circle
self.prepCompressedImage = UIImage.compressImage(pickedImage, compressRatio: 0.9)
base64StringOf_my_image = appUtility.base64StringOfImage(self.prepCompressedImage)
// self.viewWillAppear(false)
fireNotification.pickedImage = pickedImage
isDismissing = true
}else{}
imagePicker.delegate = nil
self.dismissViewControllerAnimated(true,completion: nil)
isDismissing = true
}
}
extension ProfileVC: UIPickerViewDelegate,UIPickerViewDataSource {
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
if university.tag == 0 {
return 1
}else {
return 1
}
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if university.tag == 0 {
return uniList.count
}
else {
return industryList.count
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if university.tag == 0 {
let uni = uniList[row]
return String(uni.uni_name)
}else {
let ind = industryList[row]
return String(ind.ind_name)
}
}
}
//MARK:- SearchVC
extension ProfileVC {
func loadUni() {
let userLoader = UserLoader()
let params = ["":""]
if PrepUtility.connectedToNetwork() {
userLoader.tryUniversity(params, successBlock: { (university) in
self.uniList = university!
}) { (error) in
if let err = error?.userInfo["NSLocalizedDescription"] {
appUtility.showAlert(err as! String, VC: self)
}else {
appUtility.showAlert("Something went wrong", VC: self)
}
}
}else {
appUtility.showNoNetworkAlert()
}
}
func loadIndustries() {
let userLoader = UserLoader()
if PrepUtility.connectedToNetwork() {
userLoader.tryIndustry(["":""], successBlock: { (industry) in
self.industryList = industry!
}) { (error) in
if let err = error?.userInfo["NSLocalizedDescription"] {
appUtility.showAlert(err as! String, VC: self)
}else {
appUtility.showAlert("Something went wrong", VC: self)
}
}
}else {
appUtility.showNoNetworkAlert()
}
}
}
Since you don't initialize your UI in a viewWillLoad or similar method, I would say that your issue is coming from your device trying to get some more space.
You are probably out of memory and the system kill what it can (in your case your previous controller).
When you dismiss the image picker, your controller is reloading its views (with your initial values)
i personally would save the .text of the label into variables previously declared (you are going to need them anyway) and in the viewDidAppear method assign the variables values to the textField.Text, when the view appear the first time they will appear empty, when you call the image picker you save the new values and then when the original view appears again it will assing the values you saved before.