Pinning a UIButton to a specific location on a photograph - swift

urbanPan
Hi,
I have a photograph with UI buttons on top. The UI buttons need to be at very specific and precise locations on the photograph.
When I resize the photograph (for example, for different devices), the UI buttons never stay in alignment.
Containers do not work as they are relative to the screen size and orientation rather than the aspect and dimensions of the photograph.
For example, when the photo is uncropped on larger devices such as the iPad, then the container rules are no longer relevant.
Pinning does not work as the absolute values cannot be scaled up and resized.
I know what I’m trying to achieve is possible as it is common and seen in other apps. However, the alignment and constraints in Xcode do not appear to facilitate this?
I’m sure I’m missing something. Any ideas, greatly appreciated.
See urbanPan image below or urbanPan.io for Github files.
urbanPan
Blessup,
UrbanSmash
Although the issue appears to be more directly related to the Main.Storyboard layout, here is the code for the ViewController.swift
//
// ViewController.swift
// urbanPan
//
// Created by URBANSMASH pro on 29/08/2019.
// Copyright © 2019 Play it on Pan. All rights reserved.
//
import UIKit
import AVFoundation
import AudioKit
class ViewController: UIViewController, AVAudioPlayerDelegate {
var player : AVAudioPlayer!
let sounds = ["Rest", "2-F3", "2-FS3", "2-G3", "2-GS3", "2-A3", "2-AS3", "2-B3", "2-C4", "2-CS4", "2-D4", "2-DS4", "2-E4", "2-F4", "2-FS4", "2-G4", "2-GS4", "2-A4", "2-AS4", "2-B4", "2-C5", "2-CS5", "2-D5", "2-DS5", "2-E5", "2-F5", "2-FS5", "2-G5", "2-GS5", "2-A5", "2-AS5", "2-B5", "2-C6"]
var noteNum = 0
let conductor = Conductor.shared
var isPlaying = false
var currentSound = 0
override func viewDidLoad() {
super.viewDidLoad()
conductor.midi.addListener(self)
conductor.loadSamples(byIndex: currentSound)
}
#IBAction func noteReleased(_ sender: UIButton) {
noteNum = sender.tag
// player.currentTime = 0
stopSound()
}
#IBAction func notePlayed(_ sender: UIButton) {
noteNum = sender.tag
// player.currentTime = 0
playSound()
}
func stopSound() {
noteOff(note: MIDINoteNumber(noteNum))
}
func playSound() {
noteOn(note: MIDINoteNumber(noteNum))
}
func noteOn(note: MIDINoteNumber) {
DispatchQueue.main.async {
self.conductor.playNote(note: note, velocity: 100, channel: 0)
}
}
func noteOff(note: MIDINoteNumber) {
DispatchQueue.main.async {
self.conductor.stopNote(note: note, channel: 0)
}
}
}
extension ViewController: AKMIDIListener {
func receivedMIDINoteOn(noteNumber: MIDINoteNumber, velocity: MIDIVelocity, channel: MIDIChannel) {
DispatchQueue.main.async {
self.conductor.playNote(note: noteNumber, velocity: velocity, channel: channel)
}
}
func receivedMIDINoteOff(noteNumber: MIDINoteNumber, velocity: MIDIVelocity, channel: MIDIChannel) {
DispatchQueue.main.async {
self.conductor.stopNote(note: noteNumber, channel: channel)
}
}
// MIDI Controller input
func receivedMIDIController(_ controller: MIDIByte, value: MIDIByte, channel: MIDIChannel) {
AKLog("Channel: \(channel + 1) controller: \(controller) value: \(value)")
//conductor.controller(controller, value: value)
}
// MIDI Pitch Wheel
func receivedMIDIPitchWheel(_ pitchWheelValue: MIDIWord, channel: MIDIChannel) {
//conductor.pitchBend(pitchWheelValue)
}
// After touch
func receivedMIDIAfterTouch(_ pressure: MIDIByte, channel: MIDIChannel) {
conductor.afterTouch(pressure)
}
func receivedMIDISystemCommand(_ data: [MIDIByte]) {
// do nothing: silence superclass's log chatter
}
// MIDI Setup Change
func receivedMIDISetupChange() {
AKLog("midi setup change, midi.inputNames: \(conductor.midi.inputNames)")
let inputNames = conductor.midi.inputNames
inputNames.forEach { inputName in
conductor.midi.openInput(name: inputName)
}
}
func setSpeakersAsDefaultAudioOutput() {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
}
catch {
// hard to imagine how we'll get this exception
let alertController = UIAlertController(title: "Speaker Problem", message: "You may be able to hear sound using headphones.", preferredStyle: UIAlertController.Style.alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default) {
(result: UIAlertAction) -> Void in
}
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)
}
}
}

For this layout, I think FrameLayout is preferred over AutoLayout.
When the viewDidLayoutSubviews method is called, you calculate and update the view size and position according to the screen size.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let screenSize = UIScreen.main.bounds
// ...
d4View.frame = ...
}

Related

Not Receiving Remote Command Center events while using Music Player Controller

I am trying to receive notifications from the remote command center for when the play/pause button is tapped so that I can appropriately update the image for my play/pause button. However, I am not receiving the notification of when the playcommand/pausecommand is pressed (ie. when it should print "tapped play" or "tapped pause"). This is my first time using this library, and so I followed Apples docs and it says to implement a music controller using either a applicationMusicPlayer or applicationQueuePlayer to receive these events, but so far I am not able to. I don't know if there is anything else I need to do besides just setting it up as a applicationQueuePlayer.
Here is my code for a very plain music player controller that produces this situation:
let musicPlayer = MPMusicPlayerController.applicationQueuePlayer
override func viewDidLoad() {
super.viewDidLoad()
setupRemoteControl()
musicPlayer.setQueue(with: .songs())
}
func setupRemoteControl() {
UIApplication.shared.beginReceivingRemoteControlEvents()
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = true
commandCenter.playCommand.addTarget { (_) -> MPRemoteCommandHandlerStatus in
print("tapped play")
return .success
}
commandCenter.pauseCommand.isEnabled = true
commandCenter.pauseCommand.addTarget {(_) -> MPRemoteCommandHandlerStatus in
print("tapped pause")
return .success
}
#IBAction func selectSongs(_ sender: UIButton) {
let controller = MPMediaPickerController(mediaTypes: .music)
controller.allowsPickingMultipleItems = true
controller.popoverPresentationController?.sourceView = sender
controller.delegate = self
present(controller, animated: true, completion: nil)
}
func mediaPicker(_ mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
musicPlayer.setQueue(with: mediaItemCollection)
mediaPicker.dismiss(animated: true, completion: nil)
musicPlayer.play()
}

Get selected image in imagePickerController and pass it to coreML

I'm trying to do the recognition by using coreML, the function working and showing the result correctly. But I want to call the method into a button, like when I pressed the catDog button and it runs the method. But since the finalResult() and identifyCatOrDog() is its own function, so that I can't call it into the button. I tried to copy and paste the method inside the button, but it doesn't show me anything. How can I edit the code so that findResult() only work when I pressed the button not running automatically?
import UIKit
import CoreML
import Vision
import Photos
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet var loadImage: UIImageView!
#IBOutlet var Result: UILabel!
#IBAction func photoBtn(_ sender: UIButton) {
getPhoto()
}
#IBAction func cameraBtn(_ sender: UIButton) {
}
#IBAction func catDog(_ sender: UIButton) {
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getPhoto() {
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .photoLibrary
present(picker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
guard let gotImage = info[.originalImage] as? UIImage else {
fatalError("No picture chosen")
}
loadImage.image = gotImage
identifyCatOrDog(image: gotImage)
}
func identifyCatOrDog(image: UIImage) {
let modelFile = ImageClassifier()
let model = try! VNCoreMLModel(for: modelFile.model)
let handler = VNImageRequestHandler(cgImage: image.cgImage!, options: [ : ])
let request = VNCoreMLRequest(model: model, completionHandler: findResults)
try! handler.perform([request])
}
func findResults(request: VNRequest, error: Error?) {
guard let results = request.results as? [VNClassificationObservation] else {
fatalError("Unable to get results")
}
var bestGuess = ""
var bestConfidence: VNConfidence = 0
for classification in results {
if (classification.confidence > bestConfidence) {
bestConfidence = classification.confidence
bestGuess = classification.identifier
}
}
Result.text = "Image is: \(bestGuess) with confidence \(bestConfidence) out of 1"
}
I take it that the problem is that sometimes when the image picker is dismissed, you want to call identifyCatOrDog, but other times you don’t.
One rather crude possibility is this: In the button action method, raise a bool instance property flag so that when didFinishPickingMedia is called you know whether or not to call identifyCatOrDog.
A more sophisticated way would be to divide things off into helper classes so that the operation of the image picker after pressing the catDog button takes place within a completely different code world.

Zero amplitude and frequency from audiokit tracker on device but works on simulator

AudioKit
I have two view controllers that are doing different audio related function. The first view viewController, needs to record audio and also do real-time processing of the amplitude and frequency of the sound being recorded. The second view controller needs to play the recorded audio, and do some stuff with the playhead.
My problem is that when I go back and forth a few times between the two screens on my iphone 6 device, the amplitude and frequency of the tracker only equal 0. This behavior does not happen on my simulator. Similarly, I've tried different combinations of starting + stopping the tracker and/or the audio engine at different points in the workflow but then I just get crashes along the lines of https://github.com/audiokit/AudioKit/issues/643. One thing I have been trying to figure out is how to make a singleton instance of the aukiokit so I can only start the engine once, but I can't get it from examples.
Here are the key sections of code that use the audiokit, microphone and tracker:
First view controller in the workflow
class ViewController: UIViewController {
#IBAction func analyzeSpeechAction(_ sender: Any) {
//Audiokit.stop()
stopTimers()
let vc = AnalysisViewController(nibName: "AnalysisViewController", bundle: nil)
print("ANALYZING")
do {
try player.reloadFile()
} catch { print("Errored reloading.") }
let recordedDuration = player != nil ? player.audioFile.duration : 0
if recordedDuration > 0.0 {
recorder.stop()
}
tracker.stop()
navigationController?.pushViewController(vc, animated: true)
}
func stopTimers(){
if timerTest != nil {
timerTest!.invalidate()
timerTest = nil
}
if timer != nil {
timer!.invalidate()
timer = nil
}
}
var mic: AKMicrophone!
var tracker: AKFrequencyTracker!
var silence: AKBooster!
var mainMixer: AKMixer!
var timerTest : Timer?
var micMixer: AKMixer!
var recorder: AKNodeRecorder!
var player: AKAudioPlayer!
var tape: AKAudioFile!
var micBooster: AKBooster!
override func viewDidLoad() {
super.viewDidLoad()
AKAudioFile.cleanTempDirectory()
AKSettings.bufferLength = .medium
do {
try AKSettings.setSession(category: .playAndRecord, with: .allowBluetoothA2DP)
} catch {
AKLog("Could not set session category.")
}
AKSettings.defaultToSpeaker = true
AKSettings.audioInputEnabled = true
mic = AKMicrophone()
tracker = AKFrequencyTracker(mic)
tracker.start()
silence = AKBooster(tracker, gain: 0)
micMixer = AKMixer(mic)
micBooster = AKBooster(micMixer)
// Will set the level of microphone monitoring
micBooster.gain = 0
recorder = try? AKNodeRecorder(node: micMixer)
if let file = recorder.audioFile {
player = try? AKAudioPlayer(file: file)
}
mainMixer = AKMixer(micBooster)
do {
try recorder.record()
} catch { print("Errored recording.") }
AudioKit.output = mainMixer
}
viewDidAppear(){
AudioKit.stop()
AudioKit.output = silence
AudioKit.start()
if timer == nil{
timer = Timer.scheduledTimer(timeInterval: 1, target:self,
selector: #selector(ViewController.updateCounter), userInfo: nil, repeats: true)
}
}
#objc func updateUI() {
frame = frame + 1
print("AMP")
print(tracker.amplitude)
print(tracker.frequency)
}
}
Second View Controller
class AnalysisViewController: UIViewController {
#IBOutlet weak var barChartView: LineChartView!
#IBAction func recordSpeechNavigation(_ sender: Any) {
let vc = ViewController(nibName: "ViewController", bundle: nil)
//AudioKit.stop()
player.stop()
navigationController?.pushViewController(vc, animated: true)
}
#IBAction func playButtonAction(_ sender: UIButton) {
playAudio()
}
override func viewDidLoad() {
super.viewDidLoad()
//move to completion handler
player.completionHandler = {() -> Void in
self.playerStatusButton.setTitle("Play", for: .normal)
self.timerTest!.invalidate()
self.relativePlayheadPosition = 0.0
self.movePlayhead()
print("complete")
}
}
}
I kind of mashed up a bunch of examples to get this far so I apologize if I am improperly using some of the AudioKit methods. The key behavior occurs between viewDidLoad(), viewDidAppear() and updateUI() of the first view and recordSpeechNavigation() of the second view.
Does anyone have any insight into what might be happening or how I can properly implement this stuff?

Interstitial iAd delayed after pressing button

I'm trying to setup iAd after clicking cancel button on JSSAlert. I have in JSSAlert function that set alpha for full view 0.7.. And in view controller I have function for iAd and set back alpha to 1.0...
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Vyhodnotenie testu"
self.showAlert()
}
func showAlert() {
func callback(){}
if numberOfPoints > 49 {
let customIcon = UIImage(named: "smile")
let alertview = JSSAlertView().show(self, title: "Gratulujeme! Uspeli ste.", text: "Dokončili ste test s počtom bodov \(numberOfPoints + 1) z \(maximumNumberOfPoints)!", buttonText: "OK!", color: UIColorFromHex(0x22c411, alpha: 1), iconImage: customIcon)
alertview.setTextTheme(.Light)
alertview.addAction(myCancelCallback)
self.navigationController?.navigationBar.alpha = 0.7
} else {
let customIcon = UIImage(named: "sad")
let alertview = JSSAlertView().show(self, title: "Ľutujeme! Neuspeli ste.", text: "Dokončili ste test s počtom bodov \(numberOfPoints + 1) z \(maximumNumberOfPoints)!", buttonText: "OK!", color: UIColorFromHex(0xd20606, alpha: 1), iconImage: customIcon)
alertview.addAction(myCancelCallback)
alertview.setTextTheme(.Light)
self.navigationController?.navigationBar.alpha = 0.7
}
}
func myCancelCallback() {
self.navigationController?.navigationBar.alpha = 1.0
self.interstitialPresentationPolicy = ADInterstitialPresentationPolicy.Automatic
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
view.addSubview(interstitialAdView)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func interstitialAdActionDidFinish(var interstitialAd: ADInterstitialAd!) {
interstitialAd = nil
interstitialAdView.removeFromSuperview()
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
}
func interstitialAdDidUnload(var interstitialAd: ADInterstitialAd!) {
interstitialAd = nil
interstitialAdView.removeFromSuperview()
}
Alpha back to 1.0 in function myCancelCallback is working but iAd is delayed.. What can cause that delay? Or how can I deal with it?
I want to show iAd immediately after pressing OK!.
Video how it's working:
https://www.youtube.com/watch?v=r6LKN-cjaz8&feature=youtu.be
Update:
iAd App Network Shutdown As of December 31, 2016, the iAd App Network
is no longer available. If you'd like to promote your apps, you can
advertise using Search Ads, Apple News, or third party networks and
advertising sellers.
Reference: https://developer.apple.com/support/iad/
Here is what your gonna do, you have to create the interstitial including close button programmatically , i just made you a sample :
Add row in info.plist : View controller-based status bar appearance -> NO
in AppDelegate didFinishLaunchingWithOptions method add the following line to insure that status bar will be not hidden :
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.None)
Here is a full sample view controller of how your gonna add the interstitial programmatically , but you only need to write animation when the interstitial is showing instead of just using addSubView. you can use animate transform translation you will find a lot of samples about that.
import UIKit
import iAd
class ViewController: UIViewController, ADInterstitialAdDelegate {
var interstitialAd:ADInterstitialAd!
var interstitialAdView: UIView = UIView()
var closeButton:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "loadInterstitialAd", userInfo: nil, repeats: false)
}
func loadInterstitialAd() {
interstitialAd = ADInterstitialAd()
interstitialAd.delegate = self
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
print("interstitialAdWillLoad")
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
print("interstitialAdDidLoad")
UIApplication.sharedApplication().statusBarHidden = true
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
self.navigationController?.navigationBar.addSubview(interstitialAdView)
closeButton = UIButton(frame: CGRect(x: 15, y: 15, width: 20, height: 20))
//add a cross shaped graphics into your project to use as close button
closeButton.setBackgroundImage(UIImage(named: "close"), forState: UIControlState.Normal)
closeButton.addTarget(self, action: Selector("close"), forControlEvents: UIControlEvents.TouchDown)
self.navigationController?.navigationBar.addSubview(closeButton)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func close() {
interstitialAdView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitialAd = nil
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
}
func interstitialAdActionDidFinish(interstitialAd: ADInterstitialAd!) {
print("interstitialAdActionDidFinish")
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
print("didFailWithError")
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
print("interstitialAdDidUnload")
close()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
}
Update: It might be you just have to call : UIViewController.prepareInterstitialAds() in viewDidLoad of your class to download the content of the interstitial so whenever you ask to present it will be ready and then might be no delay.

Game Center Not Properly Working. Swift, Sprite Kit

I recently tried to add Game Center to my Sprite Kit Game, but it's not working properly.
When the game starts in the simulator, the Game Center login page does show up. When I start the game on my phone it does not. Can someone tell me what I am doing wrong.
//GameViewController.Swift
import GameKit
class GameViewController: UIViewController, ADBannerViewDelegate, GKGameCenterControllerDelegate {
var bannerView:ADBannerView?
override func viewDidLoad() {
super.viewDidLoad()
// Presenting scene without using GameScene.sks
let skView = view as! SKView
let myScene = GameScene(size: skView.frame.size)
myScene.scaleMode = .ResizeFill
skView.presentScene(myScene)
authenticateLocalPlayer()
}
//initiate gamecenter
func authenticateLocalPlayer(){
var localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(viewController, error) -> Void in
if (viewController != nil) {
self.presentViewController(viewController, animated: true, completion: nil)
}
else {
println((GKLocalPlayer.localPlayer().authenticated))
}
}
}
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController!)
{
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
//GameScene.Swift
import GameKit
class GameScene: SKScene, SKPhysicsContactDelegate{
var playerScore = 0
func playerScoreUpdate() {
playerScorelabel.text = "\(playerScore)"
}
func saveHighScore(high:Int) {
NSUserDefaults.standardUserDefaults().setInteger(high, forKey: "highscore")
//check if user is signed in
if GKLocalPlayer.localPlayer().authenticated {
var scoreReporter = GKScore(leaderboardIdentifier: "TF1G002ID") //leaderboard id here
scoreReporter.value = Int64(playerScore) //score variable here (same as above)
var scoreArray: [GKScore] = [scoreReporter]
GKScore.reportScores(scoreArray, withCompletionHandler: {(error : NSError!) -> Void in
if error != nil {
println("error")
}
})
}
}
//GameOver.Swift
import GameKit
class GameOverScene: SKScene, GKGameCenterControllerDelegate {
//shows leaderboard screen
func showLeader() {
var vc = self.view?.window?.rootViewController
var gc = GKGameCenterViewController()
gc.gameCenterDelegate = self
vc?.presentViewController(gc, animated: true, completion: nil)
}
// Press Finger
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if node.name == "replay" {
playSound(sound)
}
if node.name == "leaderboard" {
showLeader()
}
}
}
//hides leaderboard screen
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController!)
{
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
Did you import the GameKit framework into your app?
Go to your project -> General -> scroll until you see linked frameworks -> click the plus sign -> add GameKit framework by searching