Hi im making an app with uikit and i want to set a profile image(received from url) on Tabbar item. I am expecting the updated profile image on TabBarItem. Also i want to add a border around the image when the tab is selected.
I found this solution, but the image does not appear in the tabitem.
Set user's profile picture on Tabbar item
extension UITabBarController {
func addSubviewToLastTabItem(_ image: UIImage) {
if let lastTabBarButton = self.tabBar.subviews.last, let tabItemImageView = lastTabBarButton.subviews.first {
if let accountTabBarItem = self.tabBar.items?.last {
accountTabBarItem.selectedImage = nil
accountTabBarItem.image = nil
}
let imgView = UIImageView()
imgView.frame = tabItemImageView.frame
imgView.layer.cornerRadius = tabItemImageView.frame.height/2
imgView.layer.masksToBounds = true
imgView.contentMode = .scaleAspectFill
imgView.clipsToBounds = true
imgView.image = image
self.tabBar.subviews.last?.addSubview(imgView)
}
}
}
Could someone help me to do that?
Did you add the second part of code:
let yourImage = UIImage(named: "your-image")
override func viewDidLayoutSubviews() { // add to any vc
super.viewDidLayoutSubviews()
tabBarController?.addSubviewToLastTabItem(yourImage)
}
When you call addSubviewToLastTabItem("String") it is critical print correctly parameter without mistakes.
Could you post this part of code?
One more thing viewDidLayoutSubviews will work if you rotate iPhone, double check please that you are calling addSubviewToLastTabItem() in viewDidLoad()
Related
I've created a customized keyboard using UIView. I'm trying to auto-fill the letters using a for loop but with no success.
func initializeSubviews() {
let xibFileName = "keyboardView" // xib extension not included b
let view = Bundle.main.loadNibNamed(xibFileName, owner: self, options: nil)![0] as! UIView
self.addSubview(view)
view.frame = self.bounds
setKeyboardText()
}
#IBOutlet var keyboardLetters: [myKeyboardBtn]!
func setKeyboardText() {
let str = "abcdefghijklmnopqrstuvwxyz"
let characterArray = Array(str)
for (Index, key) in keyboardLetters.enumerated() {
key.titleLabel?.text = String(characterArray[Index])
}
// [a,b,c,d,...]
}
what am I doing wrong?
According to Apple
"To set the actual text of the label, use setTitle(_:for:)
(button.titleLabel.text does not let you set the text)."
I'm building a simple video editor for macOS: A movie file is loaded as an AVAsset, transformed by a series of CIFilters in a AVVideoComposition, and played by an AVPlayer. I present UI controls for some of the parameters of the CIFilters.
When video is playing everything is working great, I slide sliders and effects change! But when the video is paused the AVPlayerView doesn't redraw after the controls in the UI are changed.
How can I encourage the AVPlayerView to redraw the contents of the videoComposition of it's current item when it's paused?
class ViewController: NSViewController {
#objc #IBAction func openDocument(_ file: Any) { ... }
#IBOutlet weak var moviePlayerView: AVPlayerView!
var ciContext:CIContext? = CIContext(mtlDevice: MTLCreateSystemDefaultDevice()!)
var sliderValue: Double = 0.0
#IBAction func sliderMoved(_ sender: NSSlider) {
self.sliderValue = sender.doubleValue
// need to update the view here when paused
self.moviePlayerView.setNeedsDisplay(self.moviePlayerView.bounds)
// setNeedsDisplay has no effect.
}
func loadMovie(file: URL) {
let avMovie = AVMovie(url: file)
let avPlayerItem = AVPlayerItem(asset: avMovie)
avPlayerItem.videoComposition = AVVideoComposition(asset: avMovie) { request in
let output = request.sourceImage.applyingGaussianBlur(sigma: self.sliderValue)
request.finish(with: output, context: self.ciContext)
}
self.moviePlayerView.player = AVPlayer(playerItem: avPlayerItem)
}
}
It turns out re-setting the AVPlayerItem's videoComposition property will trigger the item to redraw. This works even if you set the property to it's current value: item.videoComposition = item.videoComposition. The property setter appears to have undocumented side effects.
To fix the sample code above, do this:
#IBAction func sliderMoved(_ sender: NSSlider) {
self.sliderValue = sender.doubleValue
// update the view when paused
if self.moviePlayerView.player?.rate == 0.0 {
self.moviePlayerView.player?.currentItem?.videoComposition = self.moviePlayerView.player?.currentItem?.videoComposition
}
}
Hopefully someone finds this useful!
I have tried to make an app scanning the QR code within ios 10 and Swift 3. However, my QRScannerController could not detect the QR Code but showing the camera view.
I don't understand what's wrong with the code. Here is the implement of the controller:
import UIKit
import AVFoundation
class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
#IBOutlet var messageLabel:UILabel!
#IBOutlet var topbar: UIView!
//TESTING
var captureSession: AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var qrCodeFrameView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Get an instance of the AVCaptureDevice class to initialize a device object and provide the video as the media type parameter
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
do {
view.bringSubview(toFront: messageLabel)
view.bringSubview(toFront: topbar)
// Get an instance of the AVCaptureDeviceInput class using the previous deivce object
let input = try AVCaptureDeviceInput(device: captureDevice)
// Initialize the captureSession object
captureSession = AVCaptureSession()
// Set the input devcie on the capture session
captureSession?.addInput(input)
// Initialize a AVCaptureMetadataOutput object and set it as the input device
let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOutput)
// Set delegate and use the default dispatch queue to execute the call back
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureMetadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode]
//Initialise the video preview layer and add it as a sublayer to the viewPreview view's layer
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds
view.layer.addSublayer(videoPreviewLayer!)
//start video capture
captureSession?.startRunning()
//Initialize QR Code Frame to highlight the QR code
qrCodeFrameView = UIView()
if let qrCodeFrameView = qrCodeFrameView {
qrCodeFrameView.layer.borderColor = UIColor.green.cgColor
qrCodeFrameView.layer.borderWidth = 2
view.addSubview(qrCodeFrameView)
view.bringSubview(toFront: qrCodeFrameView)
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
// Check if the metadataObjects array is not nil and it contains at least one object.
if metadataObjects == nil || metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRect.zero
messageLabel.text = "No QR code is detected"
return
}
// Get the metadata object.
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if metadataObj.type == AVMetadataObjectTypeQRCode {
// If the found metadata is equal to the QR code metadata then update the status label's text and set the bounds
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
qrCodeFrameView?.frame = barCodeObject!.bounds
if metadataObj.stringValue != nil {
messageLabel.text = metadataObj.stringValue
}
}
}
} catch {
//If any error occurs, simply print it out
print(error)
return
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Your message label is going under your view controller view. Just bring the message label to front at the end of view did load would help.
I have created a sample project and it was working fine on iPhone. Please take a look at here
First of all I would like to thank anyone in advance for any help I get.
I have searched far and wide across the net and cannot find a solution to my issue. My issue is with an iOS game I am building using the SpriteKit framework. I have added a background song using an SKAudioNode and it works fine initially, but when I pause and play the game within a few seconds, the music does not begin playing again. I have tried lots of things like removing the SKAudioNode when the game is paused and adding it again when the game is resumed, but nothing has worked. I have posted a snippet of my code below keeping it as relevant as possible:
class GameScene: SKScene, SKPhysicsContactDelegate {
var backgroundMusic = SKAudioNode(fileNamed: "bg.mp3")
let pauseImage = SKTexture(imageNamed: "pause.png")
var pauseButton:SKSpriteNode!
let playImage = SKTexture(imageNamed: "play2.png")
var playButton:SKSpriteNode!
override func didMoveToView(view: SKView) {
self.addChild(backgroundMusic)
// create pause button
pauseButton = SKSpriteNode(texture: pauseImage)
pauseButton.position = CGPoint(x: self.size.width - pauseButton.size.width, y: pauseButton.size.height)
pauseButton.zPosition = 1
pauseButton.name = "pauseButton"
self.addChild(pauseButton)
// create play button
playButton = SKSpriteNode(texture: playImage)
playButton.position = CGPoint(x: self.size.width - playButton.size.width, y: -playButton.size.height)
playButton.zPosition = 1
playButton.name = "playButton"
self.addChild(playButton)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
if pauseButton.containsPoint(touch.locationInNode(self)) {
let blockOne = SKAction.runBlock({
self.pauseButton.position.y = - self.pauseButton.size.height
self.playButton.position.y = self.playButton.size.height
})
let blockTwo = SKAction.runBlock({
self.view?.paused = true
})
self.runAction(SKAction.sequence([blockOne, blockTwo]))
}
else if(playButton.containsPoint(touch.locationInNode(self))) {
self.playButton.position.y = -self.playButton.size.height
self.pauseButton.position.y = self.pauseButton.size.height
self.view?.paused = false
}
}
}
}
You'll want to look into using AVAudioPlayer and NSNotificationCenter to pass data around the game.
Start the background audio player in your actual GameViewController class.
It's better to do it this way then use SKAudioNode... That's more for sounds that are like sound effects relating to something that happened in gameplay.
By using AVAudioPlayer, the advantage is when the music is paused it's still cued up to play in it's previous spot.
This is one of the few things that will be running regardless of what's going on. So we put it in the GameViewController.
So here's an example of GameViewController code we'd need to start
import AVFoundation
var bgMusicPlayer:AVAudioPlayer?
Then in the GameViewController we make these functions as such
override func viewDidLoad() {
super.viewDidLoad()
// PlayBackgroundSound , PauseBackgroundSound will be your code to send from other "scenes" to use the audio player //
// NSNotificationCenter to pass data throughout the game //
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.playBackgroundSound(_:)), name: "PlayBackgroundSound", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.pauseBackgroundSound), name: "PauseBackgroundSound", object: nil)
}
func playBackgroundSound(notification: NSNotification) {
let name = notification.userInfo!["fileToPlay"] as! String
if (bgSoundPlayer != nil){
bgSoundPlayer!.stop()
bgSoundPlayer = nil
}
if (name != ""){
let fileURL:NSURL = NSBundle.mainBundle().URLForResource(name, withExtension: "mp3")!
do {
bgSoundPlayer = try AVAudioPlayer(contentsOfURL: fileURL)
} catch _{
bgSoundPlayer = nil
}
bgSoundPlayer!.volume = 1
bgSoundPlayer!.numberOfLoops = -1
// -1 will loop it forever //
bgSoundPlayer!.prepareToPlay()
bgSoundPlayer!.play()
}
}
func pauseBackgroundSound() {
if (bgSoundPlayer != nil){
bgSoundPlayer!.pause()
}
}
Then when you want to use the audio player in your pause or resume button functions.
Remember you need to have the player used in each scene.
import AVFoundation.AVAudioSession
override func didMoveToView(view: SKView) {
try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
}
Then if you want to pause or play something just use NSNotificationCenter.
// To Pause in a scene use this line of code in the function you need to pause the music
NSNotificationCenter.defaultCenter().postNotificationName("PauseBackgroundSound", object: self)
/// To Play initially or a new song .. use this line of code in the function you need to play the music
NSNotificationCenter.defaultCenter().postNotificationName("PlayBackgroundSound", object: self, userInfo: "FILE NAME OF BACKGROUND MUSIC")
I am using this code for my background in the gameviewcontroller:
let yourImage = UIImage(named: welkeLevel)
let imageview = UIImageView(image: yourImage)
self.view?.addSubview(imageview)
But I have a begin (menu) scene where I do not want this background and is still showing up.
How do I detect which scene I am (Gamescene / Menuscene etc.)?
Or is there another way?
SKScene is a subclass of SKNode. You can use the name property to do this. Just set the name of Gamescene or Menuscene to 'game' or 'menu' like this:
scene.name = "game"
And check the property like so:
if self.name == "game"{
//Do something.
println("game")
} else if self.name == "menu
//Do something else.
println("menu")
}
I have worked out the same issue with a replay button. I have to identify which scene the user is in and reload the same when user clicks replay button.
1) Add a extension function to detect current game level
extension SKScene {
static func sceneWithClassNamed(className: String, fileNamed fileName: String) -> SKScene? {
if let SceneClass = NSClassFromString("JumperCrab.\(className)") as? SKScene.Type,
let scene = SceneClass(fileNamed: fileName) {
return scene
}
return nil
}
func replayCurrentScene(currentLevelName: String,currentGameSceneIndex:Int)
{
GameViewController().getReplayCurrentSceneDetailsToVC(currentLevelName, currentGameSceneIndex: currentGameSceneIndex)
}
}
2) Call the above extension method in every game scene.
override func didMoveToView(view: SKView) {
self.replayCurrentScene( "Round 1", currentGameSceneIndex: 0)
}
3) Set up a similar method at the View Controller.
func getReplayCurrentSceneDetailsToVC(currentLevelName: String,currentGameSceneIndex:Int)
{
NSUserDefaults.standardUserDefaults().setInteger(currentGameSceneIndex, forKey: "LevelInt")
NSUserDefaults.standardUserDefaults().synchronize()
NSUserDefaults.standardUserDefaults().setObject(currentLevelName, forKey: "LevelName")
NSUserDefaults.standardUserDefaults().synchronize()
}
4) Create variables
//GAME LEVEL
var gameLevelSceneNameArray = NSArray()
var currentGameSceneAtVC:String?
var currentGameSceneIndexAtVC:Int?
5) Make my level list array
override func viewDidLoad() {
super.viewDidLoad()
//Game Scenes
gameLevelSceneNameArray = ["GameScene","SecondRound","ThirdRound", "FourthRound","FifthRound","SixthRound","SeventhRound", "EighthRound","NinethRound"]
}
6) Apply on the Replay Game button .
#IBAction func replayButtonClicked(sender: AnyObject) {
currentGameSceneIndexAtVC = NSUserDefaults.standardUserDefaults().integerForKey("LevelInt")
currentGameSceneAtVC = NSUserDefaults.standardUserDefaults().objectForKey("LevelName") as? String
self.moveToSelectedGameLevel(currentGameSceneAtVC!, gameSceneName: self.gameLevelSceneNameArray[currentGameSceneIndexAtVC!] as! String)
}
7) Set up a game scene navigation too (using switch case )
func moveToSelectedGameLevel(levelNameString:String, gameSceneName:String)
{
let skView = self.view as! SKView
switch(levelNameString)
{
case "Round 1":
if let gameScene = SKScene.sceneWithClassNamed(gameSceneName, fileNamed: gameSceneName) {
skView.presentScene(gameScene)
}
case "Round 2" :
if let gameScene = SKScene.sceneWithClassNamed(gameSceneName, fileNamed: gameSceneName) {
skView.presentScene(gameScene)
}
case "Round 3":
if let gameScene = SKScene.sceneWithClassNamed(gameSceneName, fileNamed: gameSceneName) {
skView.presentScene(gameScene)
default:
print("")
}
}