How to run a loop in ViewDidAppear in Swift? - ios

I want to display a blue and black rectangle alternatively on the launch of the app. I wrote this code. Its compiling fine without any error, but there is no result.
import UIKit
class ViewController: UIViewController {
var mybool = true
override func viewDidAppear(_ animated: Bool) {
while(mybool){
let firstFrame = CGRect(x: 160, y: 240, width: 100, height: 150)
let firstView = UIView(frame: firstFrame)
firstView.backgroundColor = UIColor.blue
view.addSubview(firstView)
let secondView = UIView(frame: firstFrame)
secondView.backgroundColor = UIColor.black
view.addSubview(secondView)
}
}
}
Can anyone tell me where I am going wrong?
Thanks in advance

Do not run an while waiting for a property to change on the main thread, ever! It's the thread that your whole UI relies on. The app will look like it has crashed and iOS will kill it sooner or later.
Use Core Animations:
weak var loadingView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let frame = CGRect(x: 160, y: 240, width: 100, height: 150)
let subview = UIView(frame: frame)
subview.backgroundColor = .black
self.view.addSubview(subview)
UIView.animate(withDuration: 1, delay: 0, options: [.autoreverse, .repeat, .curveEaseInOut], animations: {
subview.backgroundColor = .blue
}, completion: nil)
self.loadingView = subview
}
When you want to stop the animation (for example, when loading has finished) :
func stopAnimation() {
loadingView.layer.removeAllAnimations()
}

Related

self dismiss does not work on iOS 12 but works on higher versions

I have a small snippet of code to dismiss the presented VC:
UIView.animate(withDuration: 0.3, animations: { [weak self] in
guard let self = self else {
return
}
DispatchQueue.main.async {
self.stackViewMain.frame = CGRect(origin: CGPoint(x: self.stackViewMain.frame.origin.x, y: self.view.bounds.height), size: self.stackViewMain.bounds.size)
}
}) { _ in
weak var weakSelf = self
self.dismiss(animated: true, completion: {
weakSelf?.viewModel.animationdDidFinishControllerWillDismiss()
})
}
So the issue is in this part:
self.dismiss(animated: true, completion: {
weakSelf?.viewModel.animationdDidFinishControllerWillDismiss()
})
I just found out that self.dismiss is not being run and completionHandler is not being fired on iOS 12 but everything works as expected on iOS 13.
Can someone explain me why it does not work and how can I fix it?
Animation part is wrong.
You don't need to use DispatchQueue. Animation is always on main thread, Read this
Remove all weak self. Animations and completion are not retained by self so there is no risk of strong retain cycle. Read this
I have tested this on iOS 12 and everything works fine. See below example.
You just show a snipper of code, I guess there is retain cycle exist so you cannot dismiss current VC. You may use Debug Memory graph in Xcode to check it.
import UIKit
class ViewControllerA: UIViewController {
lazy var dismissButton: UIButton = {
let bt = UIButton(frame: CGRect(x: 0, y: 0, width: view.frame.size.width / 4, height: view.frame.size.width / 6.5))
bt.setTitle("Click", for: .normal)
bt.backgroundColor = .systemRed
bt.layer.cornerRadius = 12
bt.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
return bt
}()
lazy var aView: UIView = {
let v = UIView(frame: CGRect(x: 0, y: 50, width: view.frame.size.width / 2, height: view.frame.size.width / 2))
v.backgroundColor = .brown
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGreen
view.addSubview(dismissButton)
view.addSubview(aView)
dismissButton.center = view.center
aView.center.x = self.view.center.x
}
#objc func buttonTapped() {
UIView.animate(withDuration: 2, animations: {
self.aView.frame.size.width /= 2
self.aView.frame.size.height /= 2
}) { finished in
self.dismiss(animated: true, completion: {
print("dismiss")
})
}
}
}

Why does my UI button stop responding after a certain amount of time has elapsed?

My code is supposed to print out the current number of taps recorded, which it does do starting out, but then it randomly stops. Any tips on fixing this?
I think it has something to do with the animation, but I'm not sure what about it is causing the app to stop responding.
import UIKit
class ViewController: UIViewController {
var taps = 0 // tracks number of screen taps
override func viewDidLoad() {
super.viewDidLoad()
// get screen dimensions (in points) for current device
let screenBounds = UIScreen.main.bounds
let screenWidth = screenBounds.width
let screenHeight = screenBounds.height
// create rect
let rect = CGRect(x: 0, y: screenHeight, width: screenWidth, height: screenHeight)
let view = UIView(frame: rect)
view.backgroundColor = .red
self.view.addSubview(view)
// rising animation
UIView.animate(withDuration: 5.0, delay: 0.0, options: .curveEaseInOut, animations: {
view.frame.origin.y = CGFloat(0)
}, completion: { finished in
})
}
// when user taps screen
#IBAction func screenTapped(_ sender: Any) {
taps += 1 // increment number of taps
print(taps)
}
}
Try this
import UIKit
class ViewController: UIViewController {
var taps = 0 // tracks number of screen taps
override func viewDidLoad() {
super.viewDidLoad()
// get screen dimensions (in points) for current device
let screenBounds = UIScreen.main.bounds
let screenWidth = screenBounds.width
let screenHeight = screenBounds.height
// create rect
let rect = CGRect(x: 0, y: screenHeight, width: screenWidth, height: screenHeight)
let view = UIView(frame: rect)
view.backgroundColor = .red
self.view.addSubview(view)
//Add Tap gesture
let tap = UITapGestureRecognizer(target: self, action: #selector(screenTapped))
self.view.addGestureRecognizer(tap)
// rising animation
UIView.animate(withDuration: 5.0, delay: 0.0, options: .curveEaseInOut, animations: {
view.frame.origin.y = CGFloat(0)
}, completion: { finished in
})
}
// when user taps screen
#objc func screenTapped() {
taps += 1 // increment number of taps
print(taps)
}
}

Tapping a UIImage while it's being animated

I've been trying to be able to tap a UIImage as it animates to the top of my screen and print("Image Tapped"), yet to no success.
override func viewDidLoad() {
super.viewDidLoad()
redBalloon.image = UIImage(named: "redBalloon")
redBalloon.contentMode = .scaleAspectFit
redBalloon.frame = CGRect(x: Int(xOrigin), y: 667, width: Int(redBalloon.frame.size.width), height: Int(redBalloon.frame.size.height))
UIView.animate(withDuration: 5, delay: 0, options: UIImageView.AnimationOptions.allowUserInteraction, animations: {
self.redBalloon.frame = CGRect(x: Int(self.xEnding), y: -192, width: 166, height: 192)
}, completion: {(finished:Bool) in
self.endGame()
})
let imageTap = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
redBalloon.isUserInteractionEnabled = true
redBalloon.addGestureRecognizer(imageTap)
}
#objc func imageTapped(_ sender: UITapGestureRecognizer) {
// do something when image tapped
print("image tapped")
}
The problem is that the image view is not in the spot where you see it during animation (it's at the endpoint of the animation). So you are not tapping on the image view at the point where it is, and thus the tap is not detected.
Therefore, either you must hit-test the presentation layer or, if you don't want to do that, you must use a UIViewPropertyAnimator instead of calling UIView.animate.
As an example of the first approach, I'll subclass UIImageView. Make your UIImageView an instance of this subclass:
class TouchableImageView : UIImageView {
override func hitTest(_ point: CGPoint, with e: UIEvent?) -> UIView? {
let pres = self.layer.presentation()!
let suppt = self.convert(point, to: self.superview!)
let prespt = self.superview!.layer.convert(suppt, to: pres)
return super.hitTest(prespt, with: e)
}
}
However, personally I think it's a lot simpler to use UIViewPropertyAnimator. In that case, do not make your UIImageView a TouchableImageView! You don't want to do extra hit-test munging. Just let the property animator do all the work:
redBalloon.image = UIImage(named: "redBalloon")
redBalloon.contentMode = .scaleAspectFit
redBalloon.frame = CGRect(x: Int(xOrigin), y: 667, width: Int(redBalloon.frame.size.width), height: Int(redBalloon.frame.size.height))
let anim = UIViewPropertyAnimator(duration: 5, timingParameters: UICubicTimingParameters(animationCurve: .easeInOut))
anim.addAnimations {
self.redBalloon.frame = CGRect(x: Int(xEnding), y: -192, width: 166, height: 192)
}
anim.addCompletion { _ in
self.endGame()
}
let imageTap = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
redBalloon.isUserInteractionEnabled = true
redBalloon.addGestureRecognizer(imageTap)
anim.startAnimation()

UIGestureTap to dismiss view

I am trying to enable UIGestureTap on a custom view. I have a view controller, and in that view controller, when I press a button, a custom view pops up.
var transparentBackground = UIView()
#IBAction func UserViewImage(_ sender: UIButton) -> Void {
self.transparentBackground = UIView(frame: UIScreen.main.bounds)
self.transparentBackground.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
UIApplication.shared.keyWindow!.addSubview(self.transparentBackground)
self.opaqueView = self.setupOpaqueView()
self.transparentBackground.addSubview(opaqueView)
UIApplication.shared.keyWindow!.bringSubview(toFront: self.transparentBackground)
self.view.bringSubview(toFront: transparentBackground)
}
I want to be able to tap on the transparentBackground view and dismiss it. So I have a dismiss function called removeAnimate()
func removeAnimate()
{
UIView.animate(withDuration: 0.25, animations: {
self.transparentBackground.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.transparentBackground.alpha = 0.0;
}, completion:{(finished : Bool) in
if (finished)
{
self.transparentBackground.removeFromSuperview()
}
});
}
So, in viewdidload I enabled the UITapGesture:
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(removeAnimate))
self.transparentBackground.addGestureRecognizer(gestureRecognizer)
self.transparentBackground.isUserInteractionEnabled = true
I know the function removeAnimate works because I used it on a button in the transparentBackground view and it works perfectly. But when I tap on the transparentBackground view it does not dismiss and I am not sure what I am doing wrong
func setupOpaqueView() -> UIView{
let mainView = UIView(frame: CGRect(x: 16, y: 132, width: Int(UIScreen.main.bounds.width-32), height: 403))
mainView.backgroundColor = UIColor.clear
mainView.layer.cornerRadius = 6
self.imageView = UIImageView(frame: CGRect(x: 29, y: 18, width: 274, height: 350))
mainView.addSubview(OKbutton)
mainView.addSubview(self.imageView)
OKbutton.addTarget(self, action: #selector(ThirdWheelViewController.handleOKButtonTapped(_:)), for: .touchUpInside)
return mainView
}
This is an example and hope it helps you:
First of all create a variable:
var customView:UIView!
This is going to be our function for adding a custom view:
#IBAction func customAction(_ sender: AnyObject) {
self.customView = UIView.init(frame: CGRect.init(x: self.view.bounds.width / 2, y: self.view.bounds.height / 2, width: 100, height: 100))
self.customView.backgroundColor = UIColor.red
self.view.addSubview(self.customView)
let tap = UITapGestureRecognizer.init(target: self, action: #selector(self.removeFromSuperView))
tap.numberOfTapsRequired = 1
self.customView.addGestureRecognizer(tap)
}
And finally:
func removeFromSuperView() {
self.customView.alpha = 1.0
self.customView.transform = .identity
UIView.animate(withDuration: 0.3, animations: {
self.customView.alpha = 0.0
self.customView.transform = .init(scaleX: 1.5, y: 1.5)
}) { (finished) in
if !finished {
} else {
self.customView.removeFromSuperview()
}
}
}

UIImageView is moving back after new UIView is created

Hi I am trying to make a game using swift but am currently very stuck. My game has two buttons that move a UIImageView left and right which works perfectly although when my NSTimer calls to my animateBalls function every 2.5 seconds the UIImageView that was moved goes back to its original position. How would I be able to still be able to create a New UIView every 2.5 seconds but keep the UIIMageViews current position? Thanks for any help that you can give me, Here is my code:
import UIKit
class ViewController: UIViewController {
let speed = 10.0
#IBOutlet weak var mainBar: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
_ = NSTimer.scheduledTimerWithTimeInterval(2.5, target: self, selector: Selector("animateBalls"), userInfo: nil, repeats: true)
}
#IBAction func moveRightIsTapped(sender: AnyObject) {
if(mainBar.frame.origin.x < -158.5)
{
let xPosition = mainBar.frame.origin.x + 38.5
let yPosition = mainBar.frame.origin.y
let height = mainBar.frame.height
let width = mainBar.frame.width
mainBar.frame = CGRectMake(xPosition, yPosition, width, height)
}
}
#IBAction func moveLeftIsTapped(sender: AnyObject) {
if(mainBar.frame.origin.x > -389.5)
{
let xPosition = mainBar.frame.origin.x - 38.5
let yPosition = mainBar.frame.origin.y
let height = mainBar.frame.height
let width = mainBar.frame.width
mainBar.frame = CGRectMake(xPosition, yPosition, width, height)
}
}
#IBAction func playButtonTapped(sender: AnyObject) {
animateBalls()
}
func animateBalls() {
randomNum = Int(arc4random_uniform(1))
if(randomNum == 0)
{
let ball1 = UIView()
ball1.frame = CGRect(x: 121, y: -20, width: 20, height: 20)
ball1.layer.cornerRadius = 10
ball1.clipsToBounds = true
ball1.backgroundColor = UIColor.purpleColor()
self.view.addSubview(ball1)
UIView.animateWithDuration(speed, animations:{ ball1.frame = CGRect(x: 121, y: 705, width: ball1.frame.width, height: ball1.frame.height) })
}
if(randomNum == 1)
{
let ball2 = UIView()
ball2.frame = CGRect(x: 159, y: -20, width: 20, height: 20)
ball2.layer.cornerRadius = 10
ball2.clipsToBounds = true
ball2.backgroundColor = UIColor.greenColor()
self.view.addSubview(ball2)
UIView.animateWithDuration(speed, animations:{ ball2.frame = CGRect(x: 159, y: 705, width: ball2.frame.width, height: ball2.frame.height) })
}

Resources