I am using .animationImages for UIImageViews in my project (it's a game). When the user presses a hint button I programmatically create a variable number of UIImageViews that will appear on screen and play the same image sequence once. I need to remove the UIImageViews from the view when the animation has stopped but I can't figure out how to do this.
I've been through the swift documentation for both .animationImages and .startAnimating but it's literally one paragraph and one line, neither of which is in relation to a completion or finish notifications etc. Any help will be much appreciated.
private func hideAnswerButtonsHint() {
// Clear all of the answer boxes that aren't hint locked
resetAnswerBoxes()
var imageArray = [UIImage]()
var remainingLetters = [String]()
for imageCount in 1...8 {
let imageName = "green_smoke_puff_0\(imageCount)"
let image = UIImage(named: imageName)!
imageArray.append(image)
}
for answerBox in answerBoxArray where answerBox.hintLock == false {
if let letter = answerBoxDict[answerBox.tag] {
remainingLetters.append(letter)
}
}
for answerButton in answerButtonArray where answerButton.hintLock == false {
if let index = remainingLetters.index(of: answerButton.titleText) {
answerButton.decompress()
remainingLetters.remove(at: index)
} else {
let frame = answerButton.superview?.convert(answerButton.frame.origin, to: nil)
let imageView = UIImageView()
imageView.animationImages = imageArray
imageView.animationDuration = 0.5
imageView.animationRepeatCount = 1
imageView.frame = CGRect(x: frame!.x, y: frame!.y, width: answerButton.frame.width, height: answerButton.frame.height)
view.addSubview(imageView)
imageView.startAnimating()
answerButton.compress()
answerButton.hintLock = true
}
}
}
UIImageView is an NSObject so you can always use KVO to watch its properties:
var observation: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
observation = imageView.observe(\.isAnimating) { [weak self] imageView, change in
if let value = change.newValue,
value == true {
imageView.removeFromSuperview()
self?.observation = nil
}
}
}
Related
I've tried building a simple HomeKit app to stream video from a HomeKit Camera and while it appears to be working - the stream is not showing on the View Controller. Any help appreciated.
#IBOutlet weak var liveStreamView: HMCameraView!
// var liveStreamView: HMCameraView?
func startCameraStream(for accessory: HMAccessory) {
// Ensure this is a camera accessory
guard let cameraStreamControl = accessory.cameraProfiles?.first?.streamControl else
{ return }
cameraStreamControl.delegate = self
cameraStreamControl.startStream()
let liveStreamView = HMCameraView()
self.view.addSubview(liveStreamView)
self.liveStreamView = liveStreamView
self.liveStreamView.cameraSource = cameraStreamControl.cameraStream
self.liveStreamView?.setNeedsDisplay()
self.view.layoutIfNeeded()
}
extension ViewController: HMCameraStreamControlDelegate {
func cameraStreamControlDidStartStream(_ cameraStreamControl: HMCameraStreamControl) {
liveStreamView?.cameraSource = cameraStreamControl.cameraStream
}
}
So silly. I forgot to define the liveStreamView attributes including the frame. See below where I set the background color, alpha, tag, etc:
func startCameraStream( for accessory: HMAccessory) {
// Ensure this is a camera accessory
guard let cameraStreamControl = accessory.cameraProfiles?.first?.streamControl else
{ return }
cameraStreamControl.delegate = self
cameraStreamControl.startStream ( )
let liveStreamView = HMCameraView(frame: CGRect(x: 0, y: 0, width: 320, height: 568))
liveStreamView.backgroundColor = .clear
liveStreamView.alpha = 0.5
liveStreamView.tag = 100
liveStreamView.isUserInteractionEnabled = true
self.view.addSubview(liveStreamView)
self.liveStreamView = liveStreamView
}
I am currently making a Swift application with multiple UIImage elements. I am at the point where I need to add animations into my game. In order to reduce the projects fie size I have made a single shatter animation for every colored glass object. I have successfully changed the Images tint color for the first image in the animation but not the proceeding images. What is the best way to achieve this with the following code?
shatterImages = createImagesArray(total: 26, imagePrefix: "Shatter")
func createImagesArray(total: Int, imagePrefix: String) -> [UIImage] {
var imageArray: [UIImage] = []
for imageCount in 1..<total {
let imageName = "\(imagePrefix)-\(imageCount).png"
let image = UIImage(named: imageName)!
imageArray.append(image)
}
return imageArray
}
func animate(imageView: UIImageView, images: [UIImage]) {
let templateImage = imageView.image?.withRenderingMode(.alwaysTemplate)
imageView.image = templateImage
imageView.tintColor = UIColor.blue
imageView.animationImages = images
imageView.animationDuration = 0.7
imageView.animationRepeatCount = 1
imageView.startAnimating()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
if self.game.correctValue == self.game.rightDiamondValue {
self.animate(imageView: self.rightGlass, images: self.shatterImages)
self.updateLabel()
} else {
self.gameOver()
}
}
I have a UICollectionView that is basically a chat log. I have an imageView in some of the cells and added the ability to expand an image to full screen on tap.
///
ChatLogMessageCell.swift
/**
*
* I add the target to the UIButton with an image as a background
*/
messageImage.addTarget(self, action: #selector(fullscreenImage), for: .touchUpInside)
/*
* Full screen code
*/
#objc func fullscreenImage() {
if let chatlog = parentViewController as? ChatLogController {
let imageScroll = UIScrollView()
imageScroll.delegate = self
imageScroll.minimumZoomScale = 1.0
imageScroll.maximumZoomScale = 5.0
imageScroll.frame = UIScreen.main.bounds
let newImageView = UIImageView(image: messageImage.backgroundImage(for: .normal))
newImageView.frame = UIScreen.main.bounds
newImageView.backgroundColor = .black
newImageView.contentMode = .scaleAspectFit
newImageView.isUserInteractionEnabled = true
imageScroll.addSubview(newImageView)
chatlog.view.addSubview(imageScroll)
chatlog.navigationController?.isNavigationBarHidden = true
chatlog.tabBarController?.tabBar.isHidden = true
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissFullscreenImage))
newImageView.addGestureRecognizer(tap)
}
}
#objc func dismissFullscreenImage(_ sender: UITapGestureRecognizer) {
if let chatlog = parentViewController as? ChatLogController {
chatlog.navigationController?.isNavigationBarHidden = false
chatlog.tabBarController?.tabBar.isHidden = false
sender.view?.removeFromSuperview()
}
}
When The fullscreen image is removed the ChatLogController is no longer interactable. I can't scroll or re-enter fullscreen mode on an image.What am I missing here? I simply want to dismiss the full screen image and allow the user to choose another image or just scroll through the messages.
Here you remove the imageView
sender.view?.removeFromSuperview()
while you need to remove the scrollView like
sender.view?.superview?.removeFromSuperview()
In a game I'm developing with Swift, I have a SKScene in which the player can view different backgrounds to choose from and select one. The backgrounds fill the back and there are buttons that allow one to see the next or previous backgrounds. I've already tested a 'select' button that saves the current background and makes a transition to the Game Scene. Now I want to show different 'select' buttons depending on the background, each button will show a different price and will subtract a different amount to the player's coins.
My code can currently change the back when the player taps the 'next' and 'previous' buttons. But I'm having trouble showing the 'select' buttons for each back. Here's the relevant parts of my code:
import SpriteKit
class ShopScene: SKScene {
var backNumber = 100
var backRemainder = 0
var background = SKSpriteNode()
var coinNumber = UserDefaults.standard.integer(forKey: "coinSaved")
var backName:String? = UserDefaults.standard.string(forKey: "backSaved")
override func didMove(to view: SKView) {
if backName != nil {
backName = UserDefaults.standard.string(forKey: "backSaved")
} else {
backName = "back1"
}
background.texture = SKTexture(imageNamed: "\(backName!)")
self.addChild(background)
let nextButton: NButton = NButton(defaultButtonImage: "next", activeButtonImage: "nextP", buttonAction: nextAction)
addChild(nextButton)
let previousButton: PButton = PButton(defaultButtonImage: "previous", activeButtonImage: "previousP", buttonAction: previousAction)
addChild(previousButton)
let selectButton: SButton = SButton(defaultButtonImage: "select", activeButtonImage: "selectP", buttonAction: selectAction)
addChild(selectButton)
func nextAction() {
backNumber += 1
backRemainder = backNumber % 2
switch backRemainder {
case 0:
backName = "back1"
case 1:
backName = "back2"
selectButton.isHidden = true
default:
backName = "back1"
}
UserDefaults.standard.set(backName, forKey: "backSaved")
background.texture = SKTexture(imageNamed: "\(backName!)")
}
func previousAction() {
backNumber -= 1
backRemainder = backNumber % 2
switch backRemainder {
case 0:
backName = "back1"
case 1:
backName = "back2"
selectButton.isHidden = true
default:
backName = "back1"
}
UserDefaults.standard.set(backName, forKey: "backSaved")
background.texture = SKTexture(imageNamed: "\(backName!)")
}
As you can see, I'm trying to use the isHidden property but I'm getting an error: "use of unresolved identifier 'selectButton'". I've tried initializing the button before didMove(toView) but it just messes things up as the selectAction() must be after the didMove(toView) block. I hope what I just wrote is not too confusing or wrong in some ways, I'm just learning to code with SpriteKit.
How can I hide and unhide buttons in a SKScene?
The error is because you are declaring your buttons in the didMove(to view: SKView) func. PreviousAction() won't know that those variables exist. You need to move their declarations up inside of the class not the func
class ShopScene: SKScene {
let nextButton: NButton!
let previousButton: PButton!
let selectButton: SButton!
override func didMove(to view: SKView) {
nextButton = NButton(defaultButtonImage: "next", activeButtonImage: "nextP", buttonAction: nextAction)
addChild(nextButton)
previousButton = PButton(defaultButtonImage: "previous", activeButtonImage: "previousP", buttonAction: previousAction)
addChild(previousButton)
selectButton = SButton(defaultButtonImage: "select", activeButtonImage: "selectP", buttonAction: selectAction)
addChild(selectButton)
}
}
hello there i am trying to figure out what the issue is why i cant get a cross on the game board when a tap is made. the TTT class is the one below the first code and it is called in the main code. i will appreciate if some can help me on this
#IBOutlet var fields: [TTTImageView]!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func fieldTapped (recognizer:UITapGestureRecognizer) {
let tappedField = recognizer.view as! TTTImageView
tappedField.setPlayer(_player: "x.png")
}
func setupField () {
for index in 0 ... fields.count - 1 {
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(fieldTapped))
gestureRecognizer.numberOfTapsRequired = 1
fields[index].addGestureRecognizer(gestureRecognizer)
}
}
import UIKit
class TTTImageView: UIImageView {
var player:String?
var activated:Bool! = false
func setPlayer (_player:String) {
self.player = _player
if activated == false {
if _player == "x.png" {
self.image = UIImage (named: "x.png")
}else{
self.image = UIImage (named: "o.png")
}
activated = true
}
}
}
Make sure when you set fields[index].addGestureRecognizer(gestureRecognizer) to set isUserInteractionEnabled to true.
fields[index].isUserInteractionEnabled = true
Whenever you want to detect user activity (e.g. UITapGestureRecognizer on a UIImageView, you need to set this to true
EDIT:
I also wanted to mention something I noticed after looking at your code again: the method you are using for selectors has been deprecated. If you are using Swift 3 (which I highly recommend you do), you shouldn't call selectors like this: "functionName:" anymore. Now we use selectors like so: #selector(functionName(_:)). You should start updating your code to the most current syntax sooner rather than later.