I'm trying to make a simple music player. I want the label to display the current position of the AVMIDIPlayer. With my code the label is only updated once at the very beginning:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player:AVMIDIPlayer = AVMIDIPlayer()
var playerTime:Double = 999 {
didSet {
label.text = String(playerTime)
}
}
#IBOutlet var label: UILabel!
#IBAction func Play(_ sender: Any) {
player.play()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
do {
let audioPath = Bundle.main.path(forResource: “song”, ofType: "mid")
try player = AVMIDIPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL, soundBankURL: nil)
playerTime = player.currentPosition
}
catch {
}
}
}
What have I missed, please? Thank you. Scarlett
The reason the label isn’t updating is you’re setting the text in viewDidLoad which is called only once. Use a Timer to update the label.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player:AVMIDIPlayer = AVMIDIPlayer()
var playerTime:Double = 999 {
didSet {
label.text = String(playerTime)
}
}
var timer = Timer()
#IBOutlet var label: UILabel!
#IBAction func Play(_ sender: Any) {
player.play()
// this will execute every 0.1 seconds, allowing you to update the label.
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
self.playerTime += 0.1
let min = self.playerTime.truncatingRemainder(dividingBy: 3600)) / 60
let sec = self.playerTime.truncatingRemainder(dividingBy: 60)
self.label.text = String(format: "%02d:%02d", min, sec)
}
}
func stop() {
// when you stop playback
timer.invalidate()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
do {
let audioPath = Bundle.main.path(forResource: “song”, ofType: "mid")
try player = AVMIDIPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL, soundBankURL: nil)
playerTime = player.currentPosition
}
catch {
}
}
}
I want to generate a .png file from UIView snapshot but it's always nil. Could you give me a hint what's wrong with my code? I'm running Catalina 10.15.2 with Xcode 11.3 and iOS 13.3.
Here's my code's snippet:
extension UIView {
fileprivate func snapShot(view: UIView) -> UIView {
let snapshot = view.snapshotView(afterScreenUpdates: false)
self.addSubview(snapshot!)
return snapshot!
}
}
class ViewController: UIViewController {
var image: UIImage?
var imageView: UIImageView?
#IBAction func take(_ sender: UIButton) {
let oneThird = CGRect(x: 0,
y: 0,
width: view.frame.size.width / 3,
height: view.frame.size.height / 3)
let snap = view.snapShot(view: self.view)
snap.frame = oneThird
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let png = snap.largeContentImage?.pngData()
if let pngData = png {
self.image = UIImage(data: pngData)
}
self.imageView = UIImageView(image: self.image)
}
print(self.view.subviews.indices) // 0..<5
print(imageView?.image as Any) // NIL
}
}
Any help appreciated.
Try the following extension:
extension UIView {
func getImageFromCurrentContext(bounds: CGRect? = nil) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds?.size ?? self.bounds.size, false, 0.0)
self.drawHierarchy(in: bounds ?? self.bounds, afterScreenUpdates: true)
guard let currentImage = UIGraphicsGetImageFromCurrentImageContext() else {
return nil
}
UIGraphicsEndImageContext()
return currentImage
}
}
Pass the bounds if you would like 1/3 of the view or don't provide a bounds to get the image of the entire view.
Tested with sample code:
import UIKit
class ViewController: UIViewController {
#IBOutlet private(set) var testView: UIView!
#IBOutlet private(set) var testImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.testImageView.image = self.testView.getImageFromCurrentContext()
}
}
}
I made UIView on the storyboard have a sample label inside it and changed the background color just to verify it is in fact creating the image. Then added an UIImageView under that. Connected the UIView to testView and the UIImageView to testImageView.
I'm building camera App.
I want to preview and photo frame 1:1.
But how can I do that?
I've tried previewView frame change.
self.previewView?.frame.size = CGSize(width: 300, height: 300)
But It does not working.
class CameraViewController: UIViewController {
// MARK: - Properties
// MARK: Declared
var captureSession: AVCaptureSession?
var captureOutput: AVCapturePhotoOutput?
// MARK: IBOutlet
#IBOutlet weak var previewView: PreviewView!
// MARK: - Methods
// MARK: View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
self.configureInput()
self.configureOutput()
self.configurePreview()
self.runCamera()
}
// MARK: Configure
private func configureInput() {
self.captureSession = AVCaptureSession()
self.captureSession?.beginConfiguration()
self.captureSession?.sessionPreset = .hd4K3840x2160
guard let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { return }
guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice), self.captureSession?.canAddInput(videoDeviceInput) == true else { return }
self.captureSession?.addInput(videoDeviceInput)
}
private func configureOutput() {
let photoOutput = AVCapturePhotoOutput()
self.captureOutput = photoOutput
guard self.captureSession?.canAddOutput(photoOutput) == true else { return }
self.captureSession?.sessionPreset = .photo
self.captureSession?.addOutput(photoOutput)
self.captureSession?.commitConfiguration()
}
private func configurePreview() {
self.previewView?.videoPreviewlayer.session = self.captureSession
}
private func runCamera() {
self.captureSession?.startRunning()
}
}
This is my code.
I made this read after apple's article. (https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/setting_up_a_capture_session)
You could use this to change the preview layer frame to make it fill your preview view:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
previewLayer.frame = cameraView.bounds
previewLayer.videoGravity = .resizeAspectFill
}
I was working on trying to get a background view for a project I'm making and came across a weird instance.
This is how my code is set up.
import Foundation
import UIKit
class MainMenuViewController: UIViewController, CAAnimationDelegate {
#IBOutlet weak var colorView: UIView!
#IBOutlet weak var startLabel: UILabel!
#IBOutlet weak var firstButton: UIButton!
#IBOutlet weak var secondButton: UIButton!
#IBOutlet weak var thirdButton: UIButton!
let gradient = CAGradientLayer()
var gradientSet = [[CGColor]]()
var currentGradient: Int = 0
let gradientOne = gradientColors.lightGrey.cgColor
let gradientTwo = gradientColors.darkGrey.cgColor
let gradientThree = gradientColors.veryDarkGrey.cgColor
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
gradientSet.append([gradientOne, gradientTwo])
gradientSet.append([gradientTwo, gradientThree])
gradientSet.append([gradientThree, gradientOne])
gradient.frame = colorView.bounds
gradient.colors = gradientSet[currentGradient]
gradient.startPoint = CGPoint(x:0, y:0)
gradient.endPoint = CGPoint(x:1, y:1)
gradient.drawsAsynchronously = true
colorView.layer.insertSublayer(gradient, below: thirdButton.layer)
animateGradient()
}
func animateGradient() {
if currentGradient < gradientSet.count - 1 {
currentGradient += 1
} else {
currentGradient = 0
}
let gradientChangeAnimation = CABasicAnimation(keyPath: "colors")
gradientChangeAnimation.duration = 5.0
gradientChangeAnimation.toValue = gradientSet[currentGradient]
gradientChangeAnimation.fillMode = CAMediaTimingFillMode.forwards
gradientChangeAnimation.isRemovedOnCompletion = false
gradient.add(gradientChangeAnimation, forKey: "colorChange")
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if flag == true {
print("animation complete")
gradient.colors = gradientSet[currentGradient]
animateGradient()
}
}
}
The problem I'm having is that when the animation is finished, the 'animationDidStop' never triggers. The first animation runs, but when it's finished it's supposed to run the 'animationDidStop' function and run the 'animateGradient' function on a constant loop. I've looked and looked for solutions online but can't seem to find one. Im running Swift 4 and would really appreciate any help. Thanks!
You left out a line:
gradientChangeAnimation.delegate = self
I've got the segment controller above that is connected to the code bellow.
#IBOutlet weak var coatSegmentController: UISegmentedControl!
#IBAction func coatSelectInput() {
switch (self.unitSegmentController.selectedSegmentIndex) {
case 0:
coatSetter = 1
//println("SWITCHED TO coat 1")
println(unitSegmentController.selectedSegmentIndex)
case 1:
coatSetter = 2
//println("SWITCHED TO coat 2")
println(unitSegmentController.selectedSegmentIndex)
case 2:
coatSetter = 3
//println("SWITCHED TO coat 3")
println(unitSegmentController.selectedSegmentIndex)
default:
println("Coat switch did not work")
}
}
No matter what segment I select it always return that I've selected the first segment.
If I hook it to my unit segment outlet it works. But then the unit one stops working.
#IBOutlet weak var unitSegmentController: UISegmentedControl!
Any idea what could that be? I've tried everything. Delete and create again many times. Nothing seems to work.
Here is the full code for the view.
import UIKit
import CoreData
class PaintViewController: UIViewController, UITextFieldDelegate {
//FIELDS *********************************************************
#IBOutlet weak var widthField: UITextField!
#IBOutlet weak var heigthField: UITextField!
#IBOutlet weak var basecoatPriceField: UITextField!
#IBOutlet weak var basecoatCanSizeField: UITextField!
#IBOutlet weak var topcoatPriceField: UITextField!
#IBOutlet weak var topcoatCanSizeField: UITextField!
//FIELD LABELS ***************************************************
#IBOutlet weak var areaToPaintHeader: UILabel!
#IBOutlet weak var basecoatPriceHeader: UILabel!
#IBOutlet weak var topcoatHeader: UILabel!
#IBOutlet weak var basecoatUnit: UILabel!
#IBOutlet weak var topcoatUnit: UILabel!
//RESULT LABELS
#IBOutlet weak var resultBasecoatUnit: UILabel!
#IBOutlet weak var resultBasecoatAmount: UILabel!
#IBOutlet weak var resultBasecoatCost: UILabel!
#IBOutlet weak var resultTopcoatUnit: UILabel!
#IBOutlet weak var resultTopcoatAmount: UILabel!
#IBOutlet weak var resultTopcoatCost: UILabel!
#IBOutlet weak var basecoatNoCostWarning: UILabel!
#IBOutlet weak var topcoatNoCostWarning: UILabel!
// OTHER CONTROLERS
#IBOutlet weak var whatthehell: UISegmentedControl!
#IBOutlet weak var coatSegmentController: UISegmentedControl!
#IBOutlet weak var mainScrollView: UIScrollView!
#IBOutlet weak var unitSegmentController: UISegmentedControl!
// #IBOutlet weak var coatSegController: UISegmentedControl!
// INSTANCES ******************************************************
var unitSetter = "metric"
var coatSetter = 1
//CREATE CONCRETE CORE DATA OBJECT
var paint = [PaintEntity]()
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
// var calculations = PaintModel(
// width: 0.0,
// heigth: 0.0,
// basecoatPrice: 0.0,
// basecoatCanAmount: 0.0,
// topcoatPrice: 0.0,
// topcoatCanAmount: 0.0,
// unit: "metric",
// coats: 1.0)
// ******************************************************************
// NATIVE METHODS
// ******************************************************************
override func viewDidLoad() {
super.viewDidLoad()
//Empty cost warning label
basecoatNoCostWarning.text = ""
topcoatNoCostWarning.text = ""
// RESET CAN
basecoatCanSizeField.text = "5.0"
topcoatCanSizeField.text = "5.0"
// calculations.basecoatCanAmount = 5.0
// calculations.topcoatCanAmount = 5.0
// APPLY ICON
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 31, height: 36))
imageView.contentMode = .ScaleAspectFit
let image = UIImage(named: "concrete_bag_detail")
imageView.image = image
navigationItem.titleView = imageView
// NAV BAR BG CUSTOM ********************
var navBarColor = navigationController!.navigationBar
// BG COLOR
navBarColor.barTintColor = UIColor(hue: 162/360.0, saturation: 80/100.0, brightness: 45/100.0, alpha: 100.0/100.0)
navBarColor.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.blackColor()]
// STATUS BAR WHITE COLOR ********************
UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent
// DETECT TAP TO TRIGGER HIDE KEYBOARD
let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "hideKeyboard")
tapGesture.cancelsTouchesInView = false
mainScrollView.addGestureRecognizer(tapGesture)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// ******************************************************************
// MY METHODS
// ******************************************************************
// RESET ******************************************************
// func resetDataModel (){
//
//// calculations.width = 0.0
//// calculations.heigth = 0.0
//// calculations.basecoatPrice = 0.0
//// calculations.basecoatCanAmount = 0.0
//// calculations.topcoatPrice = 0.0
//// calculations.topcoatCanAmount = 0.0
//
// }
func resetInputAndLabels(){
widthField.text = ""
heigthField.text = ""
basecoatPriceField.text = ""
topcoatPriceField.text = ""
basecoatNoCostWarning.text = ""
topcoatNoCostWarning.text = ""
resultBasecoatAmount.text = "0.0"
resultBasecoatCost.text = "$0.00"
resultTopcoatAmount.text = "0.0"
resultTopcoatCost.text = "$0.00"
switch unitSetter {
case "metric":
basecoatCanSizeField.text = "5.0"
topcoatCanSizeField.text = "5.0"
basecoatUnit.text = "litre can"
topcoatUnit.text = "litre can"
resultBasecoatUnit.text = "Litres"
resultTopcoatUnit.text = "Litres"
case "imperial":
basecoatCanSizeField.text = "1.0"
topcoatCanSizeField.text = "1.0"
basecoatUnit.text = "gal can"
topcoatUnit.text = "gal can"
resultBasecoatUnit.text = "Gallons"
resultTopcoatUnit.text = "Gallons"
default:
println("Not able to reset labels")
}
}
//ALERT VIEW METHODS ******************************************************
func alertViewLaunch (#message: String){
var alertView = UIAlertView(title: "Ops!", message: message, delegate: self, cancelButtonTitle: "Ok,got it!")
alertView.show()
}
//KEYBOARD RESIZE VIEW ******************************************
// Call this method somewhere in your view controller setup code.
func registerForKeyboardNotifications() {
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self,
selector: "keyboardWillBeShown:",
name: UIKeyboardWillShowNotification,
object: nil)
notificationCenter.addObserver(self,
selector: "keyboardWillBeHidden:",
name: UIKeyboardWillHideNotification,
object: nil)
}
// Called when the UIKeyboardDidShowNotification is sent.
func keyboardWillBeShown(sender: NSNotification) {
let info: NSDictionary = sender.userInfo!
let value: NSValue = info.valueForKey(UIKeyboardFrameBeginUserInfoKey) as! NSValue
let keyboardSize: CGSize = value.CGRectValue().size
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0)
mainScrollView.contentInset = contentInsets
mainScrollView.scrollIndicatorInsets = contentInsets
// If active text field is hidden by keyboard, scroll it so it's visible
// Your app might not need or want this behavior.
var aRect: CGRect = self.view.frame
aRect.size.height -= keyboardSize.height
}
// Called when the UIKeyboardWillHideNotification is sent
func keyboardWillBeHidden(sender: NSNotification) {
let contentInsets: UIEdgeInsets = UIEdgeInsetsZero
mainScrollView.contentInset = contentInsets
mainScrollView.scrollIndicatorInsets = contentInsets
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.registerForKeyboardNotifications()
}
override func viewDidDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
// HIDE KEYBOARD ON TOUCH ****************************************
func hideKeyboard(){
widthField.resignFirstResponder()
heigthField.resignFirstResponder()
basecoatPriceField.resignFirstResponder()
topcoatPriceField.resignFirstResponder()
basecoatCanSizeField.resignFirstResponder()
topcoatCanSizeField.resignFirstResponder()
}
// *********************************************************************************
// ACTIONS
// *********************************************************************************
#IBAction func resetButton() {
resetInputAndLabels()
// resetDataModel()
}
// CHANGE UNIT **************************************************
#IBAction func unitSelectInput() {
switch unitSegmentController.selectedSegmentIndex {
case 0:
unitSetter = "metric"
resetInputAndLabels()
// resetDataModel()
//println("SWITCHED TO metric")
println(unitSegmentController.selectedSegmentIndex)
case 1:
unitSetter = "imperial"
resetInputAndLabels()
// resetDataModel()
//println("SWITCH TO imperial")
println(unitSegmentController.selectedSegmentIndex)
default:
println("Unit switch did not work")
}
}
// CHANGE COAT **********************************************
#IBAction func coatSelectInput() {
switch (self.unitSegmentController.selectedSegmentIndex) {
case 0:
coatSetter = 1
//println("SWITCHED TO coat 1")
println(unitSegmentController.selectedSegmentIndex)
case 1:
coatSetter = 2
//println("SWITCHED TO coat 2")
println(unitSegmentController.selectedSegmentIndex)
case 2:
coatSetter = 3
//println("SWITCHED TO coat 3")
println(unitSegmentController.selectedSegmentIndex)
default:
println("Coat switch did not work")
}
}
// GENERATE RESULTS **********************************************
#IBAction func generateResults() {
//SCROLL VIEW TO SHOW RESULTS - Only for 4s
let screenSize: CGRect = UIScreen.mainScreen().bounds
//println("Screen heigth - \(screenSize.height)")
if screenSize.height <= 480 {
UIScrollView.animateWithDuration(0.5, delay: 0.0, usingSpringWithDamping: 1.5, initialSpringVelocity: 1.5, options: UIViewAnimationOptions.CurveLinear, animations: {
self.mainScrollView.contentOffset.y = 220.0
}, completion: {
Void in
UIView.animateWithDuration(0.6, delay: 0.0, usingSpringWithDamping: 0.1, initialSpringVelocity: 3.0, options: UIViewAnimationOptions.CurveLinear, animations: {}, completion: { Void in })
})
}
//SAVE TO COREDATA
//DETECT IF NO DATA HAS BEEN ENTERED
if widthField.text == "" || heigthField.text == "" || basecoatCanSizeField.text == "" || topcoatCanSizeField.text == "" {
alertViewLaunch(message: "I will need at least width, heigth, basecoat can size and topcoat can size to get the basic calculations done")
} else {
//STORE DATA DEPENDING ON UNIT
switch unitSetter {
case "metric": PaintEntity.createInManagedObjectContext(self.managedObjectContext!,
width: NSString(string: (widthField.text)).doubleValue,
heigth: NSString(string: (heigthField.text)).doubleValue,
basecoatPrice: NSString(string: (basecoatPriceField.text)).doubleValue,
basecoatCanSize: NSString(string: (basecoatCanSizeField.text)).doubleValue,
topcoatPrice: NSString(string: (topcoatPriceField.text)).doubleValue,
topcoatCanSize: NSString(string: (topcoatCanSizeField.text)).doubleValue,
unit: "metric",
coats: coatSetter)
case "imperial":PaintEntity.createInManagedObjectContext(self.managedObjectContext!,
width: NSString(string: (widthField.text)).doubleValue,
heigth: NSString(string: (heigthField.text)).doubleValue,
basecoatPrice: NSString(string: (basecoatPriceField.text)).doubleValue,
basecoatCanSize: NSString(string: (basecoatCanSizeField.text)).doubleValue,
topcoatPrice: NSString(string: (topcoatPriceField.text)).doubleValue,
topcoatCanSize: NSString(string: (topcoatCanSizeField.text)).doubleValue,
unit: "imperial",
coats: coatSetter)
default:
println("No unit detected")
}
printResults()
}
}
func printResults(){
let fetchRequest = NSFetchRequest(entityName: "PaintEntity")
let sortDescriptor = NSSortDescriptor(key: "date", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.fetchLimit = 1
var error : NSError?
if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: &error) as? [PaintEntity] {
resultBasecoatAmount.text = "\(roundNumberToOne(fetchResults[0].resultBasecoatAmount.doubleValue))"
resultTopcoatAmount.text = "\(roundNumberToOne(fetchResults[0].resultTopcoatAmount.doubleValue))"
//println("I am fetching this \(fetchResults[0])")
if basecoatPriceField.text == "" || topcoatPriceField.text == "" {
resultBasecoatCost.textColor = UIColor(hue: 162/360.0, saturation: 65/100.0, brightness: 45/100.0, alpha: 100.0/100.0)
basecoatNoCostWarning.text = "Missing price"
resultBasecoatCost.text = "$0.00"
resultTopcoatCost.textColor = UIColor(hue: 162/360.0, saturation: 65/100.0, brightness: 45/100.0, alpha: 100.0/100.0)
topcoatNoCostWarning.text = "Missing price"
resultTopcoatCost.text = "$0.00"
}else{
if basecoatPriceField == "" {
resultBasecoatCost.textColor = UIColor(hue: 162/360.0, saturation: 65/100.0, brightness: 45/100.0, alpha: 100.0/100.0)
basecoatNoCostWarning.text = "Missing price"
resultBasecoatCost.text = "$0.00"
} else {
resultBasecoatCost.textColor = UIColor.whiteColor()
basecoatNoCostWarning.text = ""
resultBasecoatCost.text = "$\(roundNumberToTwo(fetchResults[0].resultBasecoatCost.doubleValue))"
}
if topcoatPriceField == "" {
resultTopcoatCost.textColor = UIColor(hue: 165/360.0, saturation: 63/100.0, brightness: 53/100.0, alpha: 100.0/100.0)
topcoatNoCostWarning.text = "Missing price"
resultTopcoatCost.text = "$0.00"
}else{
resultTopcoatCost.textColor = UIColor.whiteColor()
topcoatNoCostWarning.text = ""
resultTopcoatCost.text = "$\(roundNumberToTwo(fetchResults[0].resultTopcoatCost.doubleValue))"
}
}
}
}
}