How to make tappable UIImages that run functions?(Swift) - swift

(I just started using Swift a few days ago and am relatively new to programming, so please bear with me.) I am trying to make random blocks appear on the screen, and the user must tap them to make them disappear. I have been able to create the blocks, but I have no idea how to actually make them tappable. Can someone please help me? This is my code so far:
func createBlock(){
let imageName = "block.png"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: xPosition, y: -50, width: size, height: size)
self.view.addSubview(imageView)
UIView.animateWithDuration(duration, delay: delay, options: options, animations: {
imageView.backgroundColor = UIColor.redColor()
imageView.frame = CGRect(x: self.xPosition, y: 590, width: self.size, height: self.size)
}, completion: { animationFinished in
imageView.removeFromSuperview()
self.life-=1
})
}
I want to make the block disappear when tapped; any suggestions on how I can do that?

It's extremely simple to do, just use a UITapGestureRecognizer. I typically initialize the recognizer as a global variable.
let tapRecognizer = UITapGestureRecognizer()
Then in your viewDidLoad:
// Replace with your function
tapRecognizer.addTarget(self, action: "removeBlock")
imageView.addGestureRecognizer(tapRecognizer)

func removeImage(gesture: UIGestureRecognizer) {
gesture.view?.removeFromSuperview()
}
func createBlock() {
let imageName = "block.png"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: xPosition, y: -50, width: size, height: size)
imageView.userInteractionEnabled = true // IMPORTANT
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "removeImage:"))
self.view.addSubview(imageView)
UIView.animateWithDuration(duration, delay: delay, options: options, animations: {
imageView.backgroundColor = UIColor.redColor()
imageView.frame = CGRect(x: self.xPosition, y: 590, width: self.size, height: self.size)
}, { _ in
imageView.removeFromSuperview()
self.life-=1
})
}
Notice imageView.userInteractionEnabled = true it is really important because that will allow touch events on that view, it is set to false as defaul in UIImgageView.

Related

Issue reusing the same UI Image code across multiple view controllers | Swift 5

I have the following function I use to customize the navigation bar across almost all the apps view controllers and table view controllers - instead of replicating the code numerous times I am looking for way to easily call the function on those view controllers needing it.
I have tried wrapping in extension UIViewController { } but run into a selector issue saying the following:
Argument of '#selector' cannot refer to local function
'Tapped(tapGestureRecognizer:)'
Code:
func navBar(){
// Profile Image
let containView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
imageView.image = UIImage(url: URL(string: "test.com"))
imageView.contentMode = UIView.ContentMode.scaleAspectFit
imageView.layer.cornerRadius = 20
imageView.layer.masksToBounds = true
containView.addSubview(imageView)
let rightBarButton = UIBarButtonItem(customView: containView)
self.navigationItem.rightBarButtonItem = rightBarButton
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
}
#objc func imageTapped(tapGestureRecognizer: UITapGestureRecognizer) {
print("Profile Tapped")
}
How can this UIImage be seen in the navigation bar across various view controller without needing to rewrite the same code across all.
Lot a way to do it. I'll usually istance and personalize an UIViewController and use it around the whole app.
class baseController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//call your navBar here
navbar()
}
func navBar(){
// Profile Image
let containView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
imageView.image = UIImage(url: URL(string: "test.com"))
imageView.contentMode = UIView.ContentMode.scaleAspectFit
imageView.layer.cornerRadius = 20
imageView.layer.masksToBounds = true
containView.addSubview(imageView)
let rightBarButton = UIBarButtonItem(customView: containView)
self.navigationItem.rightBarButtonItem = rightBarButton
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
}
}
Now instance this class whenever you want and your controller will get your navBar() every time with this
class mineController:baseController {
//your code here...
}

Show multiple images in ViewController in Swift

I have tried the following code to load multiple images in sequence in my ViewController.
import SwiftUI
class ViewController: UIViewController {
var ctrl = MainPageController()
override func viewDidLoad() {
super.viewDidLoad()
Thread.sleep(forTimeInterval: 4.0)
let width = UIScreen.main.bounds.size.width
let height = UIScreen.main.bounds.size.height
var backgroundImageView = UIImageView(frame: CGRect(x:0, y:0, width: width, height: height))
backgroundImageView.image = UIImage(named: "loading")
backgroundImageView.contentMode = .scaleAspectFill
self.view.addSubview(backgroundImageView)
Thread.sleep(forTimeInterval: 5.0)
backgroundImageView.removeFromSuperview()
Thread.sleep(forTimeInterval: 5.0)
backgroundImageView = UIImageView(frame: CGRect(x:0, y:0, width: width, height: height))
backgroundImageView.image = UIImage(named: "loading1")
backgroundImageView.contentMode = .scaleAspectFill
self.view.addSubview(backgroundImageView)
Thread.sleep(forTimeInterval: 5.0)
backgroundImageView.removeFromSuperview()
self.performSegue(withIdentifier: "MainViewSegue", sender: self)
But I see only the second image. I want to load the first image, show it for about 5 seconds, then replace it with the second image, display that for 5 seconds. I have removed the image from the Launch.storyboard. Currently I cannot see any image. The self.performSegue works. How do I fix this problem?
Thanks
Vyasa
It looks like you are creating new image views. If you goal is to simply change the image after 5 seconds you can simplify this code to be something more like:
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
imageView.image = UIImage(named: "Load 1")
imageView.contentMode = .scaleAspectFill
self.view.addSubview(imageView)
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
// the code in this block will only execute after 5 seconds
imageView.image = UIImage(named: "Load 2")
}

Animation of pageControl in viewController from nowhere

In my viewController which is the delegate of my scrollView
override func viewWillAppear(_ animated: Bool) {
slides = loadSlides()
setupSlideScrollView(slides: slides)
pageControl.numberOfPages = slides.count
pageControl.currentPage = 0
view.bringSubview(toFront: pageControl)
textField.becomeFirstResponder()
}
func loadSlides() -> [Slide] {
let slide1: Slide = Bundle.main.loadNibNamed("Slide", owner: self, options: nil)?.first as! Slide
slide1.quizLabel.text = "This is slide1"
slide1.hintLabel.text = "yeah"
let slide2: Slide = Bundle.main.loadNibNamed("Slide", owner: self, options: nil)?.first as! Slide
slide2.quizLabel.text = "This is slide2"
slide2.hintLabel.text = "YEAH"
let slides = [slide1, slide2]
return slides
}
func setupSlideScrollView(slides : [Slide]) {
let width = view.frame.width
let height = view.frame.height
scrollView.frame = CGRect(x: 0,
y: 0,
width: width,
height: height)
scrollView.contentSize = CGSize(width: CGFloat(slides.count) * width, height: height)
scrollView.isPagingEnabled = true
for slide in slides {
slide.quizLabel.textColor = UIColor.white
slide.quizLabel.font = UIFont(name: (textField.font?.fontName)!, size: 20)
slide.hintLabel.textColor = UIColor.lightGray
slide.hintLabel.font = UIFont(name: (textField.font?.fontName)!, size: 18)
slide.frame = CGRect(x: CGFloat(slides.index(of: slide)!) * width,
y: 0,
width: width,
height: height)
scrollView.addSubview(slide)
}
}
When running, the pageControl shows up with a sliding animation. It seems it slides from the origin of the view. I have no idea why it comes out like that.
The pageControl is at the top
My segue settings as followed
p.s. Also, anyway to solve the similar problem for the sliding labels and the cursor?
I would like to appreciate your help in advance!

Programatically added UIButton doesn't go away after switching scenes

I'm using a game app template in Swift 3 and when I transition from my 'start' screen to my 'Game' scene, the button from the 'start' screen doesn't go away. I read other people posts similar to this but nothing helped. My button is a programatically added uibutton with a uibezierpath rounded rectangle behind the button to make it look nice. The problem is, it(the button, and the UIBezierpath) won't go away when I change scenes - it's in the exact same spot as the 'start' screen.
My Button code with the UIBezierpath:
let playAgain = UIButton()
playAgain.frame = CGRect(x: 225, y: 247, width: 115, height: 36)
playAgain.backgroundColor = SKColor.lightGray
playAgain.setTitle("Play", for: .normal)
playAgain.setTitleColor(.black, for: .normal)
self.view?.addSubview(playAgain)
playAgain.addTarget(self, action: #selector(playAgainTapped(_:)), for: .touchUpInside)
//now for the bezierpath/ rounded rect
//let doYourPath = UIBezierPath(rect: CGRect(x: 20, y: 20, width: 100, height: 36))
//this also works
let roundRect = UIBezierPath(roundedRect: CGRect(x: 218, y: 240, width: 130, height: 50), cornerRadius: 18)
let layer = CAShapeLayer()
layer.path = roundRect.cgPath
layer.strokeColor = UIColor.black.cgColor
layer.fillColor = UIColor.lightGray.cgColor
self.view?.layer.addSublayer(layer)
func playAgainTapped(_ sender: Any?) -> Void {
print("***********")
backToGame()
}
Switch scenes code:
func backToGame(){
removeAllChildren()
run(SKAction.sequence([
SKAction.wait(forDuration: 3.0),
SKAction.run() {
// 5
let reveal = SKTransition.flipHorizontal(withDuration: 0.5)
let scene = GameScene(size: self.size)
self.view?.presentScene(scene, transition:reveal)
}
]))
}
Any ideas?
You are presenting the scene on the same view as the button's superview.
Since the scene is independent from the views that are on the scene, your button will remain untouched, so you should explicitly remove the button if you want it to be removed.
Declare the button and the rounded rectangle globally and remove them from their superview/superlayer in backToGame.
let playAgain = UIButton()
let layer = CAShapeLayer()
func backToGame(){
removeAllChildren()
playAgain.removeFromSuperview()
layer.removeFromSuperlayer()
run(SKAction.sequence([
SKAction.wait(forDuration: 3.0),
SKAction.run() {
// 5
let reveal = SKTransition.flipHorizontal(withDuration: 0.5)
let scene = GameScene(size: self.size)
self.view?.presentScene(scene, transition:reveal)
}
]))
}

How to give UIImageView attributes using if/else function?

How can I trim this function down to be as lean as possible? The only difference between the if and else blocks is the height of the UiImageView--if true, height is 300, else 150. I can't seem to figure out the best way to refactor this function without either redundant code (like in the example) or with a really long function that just seems way too long for something that seems rather simple.
I left the example crude to make the intention clear.
class ProfileViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
banner(profileHasCustomBanner: false)
}
// banner
func banner(profileHasCustomBanner: Bool) {
if profileHasCustomBanner == true {
let bannerImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 300))
bannerImageView.image = #imageLiteral(resourceName: "the-banner")
view.contentMode = .scaleAspectFit
view.addSubview(bannerImageView)
} else {
let bannerImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 150))
bannerImageView.image = #imageLiteral(resourceName: "the-banner")
view.contentMode = .scaleAspectFit
view.addSubview(bannerImageView)
}
}
}
Define a height variable based on the condition.
func banner(profileHasCustomBanner: Bool) {
let height: CGFloat = profileHasCustomerBanner ? 300 : 150
let bannerImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: height))
bannerImageView.image = #imageLiteral(resourceName: "the-banner")
view.contentMode = .scaleAspectFit
view.addSubview(bannerImageView)
}