Popup view close button isn't working correctly - ios

I am creating a popup view that displays on top of a blurred background. It seems to work fine apart from the fact that at the moment, occasionally when it appears, the close button doesn't work at all. This often seems to happen on the second use.
Here is my code:
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent) {
if motion == .MotionShake {
println("Shake")
if randomImageContainer.alpha == 0 {
var randomIndex = Int(arc4random_uniform(UInt32(randomImages.count)))
lightBlur = UIBlurEffect(style: UIBlurEffectStyle.Light)
blurRandomView = UIVisualEffectView(effect: lightBlur)
blurRandomView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
view.addSubview(blurRandomView)
randomImageContainer.frame = CGRect(x: 0, y: 0, width: 264, height: 308)
randomImageContainer.center.x = view.frame.width/2
randomImageContainer.center.y = view.frame.height/2
randomImageContainer.image = randomImages[randomIndex]
randomImageContainer.contentMode = .ScaleAspectFill
view.addSubview(randomImageContainer)
closeRandomButton.frame = randomImageContainer.frame
closeRandomButton.addTarget(self, action: "closeRandomView:", forControlEvents: UIControlEvents.TouchUpInside)
view.addSubview(closeRandomButton)
view.bringSubviewToFront(closeRandomButton)
UIView.animateWithDuration(1, animations: {
self.randomImageContainer.alpha = 1
self.blurRandomView.alpha = 1
})
}
}
}
func closeRandomView(sender: UIButton!) {
println("Activated")
UIView.animateWithDuration(1, animations: {
self.randomImageContainer.alpha = 0
self.blurRandomView.alpha = 0
}, completion: { (finished:Bool) in
self.blurRandomView.removeFromSuperview()
self.randomImageContainer.removeFromSuperview()
self.closeRandomButton.removeFromSuperview()
})
}
The alpha value for the image and blur view are being set in viewDidLoad(), so they definitely pass the if statement first time around.
I can't see the problem, so I was wondering if anyone could notice any issues?
Thanks.

Figured it out. There was two issues. First of all, I was removing the views from the SuperView which meant they couldn't be checked in the if statement. The other problem was that there was a different view being brought to the front elsewhere in my code which meant the button was unaccessible.

Related

The completion block of UIView animation gets called before the animation is finished

I have a custom animator for view controller interactive transition. There is also a blur effect that is set to nil depending on the transition progress. The effect's animation code is the following:
#objc func blurEffectDissmisal() {
UIView.animate(withDuration: dismissAnimator.durationOfAnimation + 1, animations: {
self.blurEffectView?.effect = nil
}) { (done) in
if (done) {
self.blurEffectView?.removeFromSuperview()
}
}
}
I call it by a notification, which is called on the second view controller when the transition from it to the first one starts.
However, I have a problem here. The completion block is called before the animation ends. When I run the transition for the first time (without canceling it) it works fine, but during the subsequents runs it doesn't.
I had also tried to add the animation to my animator but it didn't work out, either.
Moreover, the completion block gets called before the actual animation ends when I cancel the transition (in this case, I understand why but can't figure out how to make it move backwards. Maybe I should create a reverse animation in a completion block?)
I have tried the suggestion from this answer as you can see, but doesn't help.
If you know how this problem could be solved, I would really appreciate your help.
Use a delay before calling animate function .
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
UIView.animate(withDuration: 2.0,delay: 0, animations: {
self.frame.origin.x = -2000
}) { (done) in
if(done){
self.removeFromSuperview()
}
}
}
I've created a playground where you can take a look at this change, just create new playground, click on the assistant editor (top left corner, two joined circles) and take a look at the animation. This should do.
import PlaygroundSupport
import UIKit
let rect = CGRect(x: 0, y: 0, width: 40, height: 40)
let view = UIView(frame: rect )
view.backgroundColor = UIColor.yellow
let label = UILabel(frame: rect)
label.text = "A"
label.textAlignment = .center
view.addSubview(label)
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = view.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(blurEffectView)
PlaygroundPage.current.liveView = view
UIView.animate(withDuration: 4, animations: {
blurEffectView.alpha = 0
}) { (done) in
if (done) {
blurEffectView.removeFromSuperview()
}
}
The problem you are facing is not that the UIView.animate doesn't do it's job, it's just because you are setting the view to nil is not animable. Like imagine deleting someething could be animated...
UIView.animate(withDuration: 1, animations: {
self.blurEffectView?.frame = CGRect(x: self.blurEffectView?.frame.origin.x, y: self.view.frame.size.height, width: self.blurEffectView?.frame.size.width, height: self.blurEffectView?.frame.size.height)
}, completion: {
(value: Bool) in
self.blurEffectView?.removeFromSuperview()
})
Using the same animation (on UIView) I am able to achieve this :

Detecting the collision b/w two objects in Swift 3.0?

I've added 2 UIViews on my screen and I want to detect if they collide. If so, then I need to show an alert on the screen.
what about
if (CGRectIntersectsRect(secondView.frame, sender.frame)) {
// Do something
}
You can check wether 2 views are intersecting by checking if their frames are intersecting. Here is an example:
let view1 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
let view2 = UIView(frame: CGRect(x: 90, y: 90, width: 50, height: 50))
extension UIView {
func intersects(_ otherView: UIView) -> Bool {
if self === otherView { return false }
return self.frame.intersects(otherView.frame)
}
}
print(view1.intersects(view2)) // Prints true because the 2 views are intersecting
You can call intersects(_:) every time you update any of your views frames (ie. change their size and/or position). If the method returns true, show an alert using UIAlertController.
// The Pan Gesture
func createPanGestureRecognizer(targetView: UIImageView)
{
var panGesture = UIPanGestureRecognizer(target: self, action:("handlePanGesture:"))
targetView.addGestureRecognizer(panGesture)
}
// THE HANDLE
func handlePanGesture(panGesture: UIPanGestureRecognizer) {
// get translation
var translation = panGesture.translationInView(view)
panGesture.setTranslation(CGPointZero, inView: view)
println(translation)
//create a new Label and give it the parameters of the old one
var label = panGesture.view as UIImageView
label.center = CGPoint(x: label.center.x+translation.x, y: label.center.y+translation.y)
label.multipleTouchEnabled = true
label.userInteractionEnabled = true
if panGesture.state == UIGestureRecognizerState.Began {
//add something you want to happen when the Label Panning has started
}
if panGesture.state == UIGestureRecognizerState.Ended {
//add something you want to happen when the Label Panning has ended
}
if panGesture.state == UIGestureRecognizerState.Changed {
//add something you want to happen when the Label Panning has been change ( during the moving/panning )
}
else {
// or something when its not moving
}
}

UIView block based animation weird behavior

I have got this very simple animation (case):
class ViewController: UIViewController {
var v: UIView!
var b = false
override func viewDidLoad() {
super.viewDidLoad()
self.v = UIView(frame: CGRect(x: 120, y: 250, width: 30, height: 30))
self.v.backgroundColor = .red
self.view.addSubview(self.v)
}
#IBAction func didTapButton(_ sender: UIButton) {
UIView.animate(withDuration: 3,
delay: 0,
options: [.beginFromCurrentState, .curveLinear],
animations: {
if self.b {
self.v.frame = CGRect(x: 120, y: 250, width: 30, height: 30)
} else {
self.v.frame = CGRect(x: 240, y: 250, width: 30, height: 30)
}
self.b = !self.b
},
completion: nil)
}
}
On each tap of the button the red view moves to left or right depending on current direction. The animation should start from the current position and to be linear.
However if I tap the button when the previous animation is already in progress then the red view does not start immediately to move in the opposite direction. It just freezes in the current position while the previous animation finishes and then starts moving. If I change the animation curve option from linear to easeOut and it works properly.
I am using iOS 10, Xcode 8.2.1
Any ideas why this happens?
When the animation is on, the UI components will not react to new changes. It will buffer the input and react once the animation is complete.
This is not related to the iOS or Xcode versions, but a normal behaviour.
I've found the answer.
I tis because of the additive animations in iOS 8 and above. Here is a very useful link which explains what actually happens and why the animation freezes.
http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/

Create UIButtons dynamically with for loop in Swift

I am trying to add buttons dynamically to a scroll view after pressing another button.
I created a custom UIView which I want to add to the scroll view.
Below you can find the code how I am trying to do it:
var buttonY: CGFloat = 0 // our Starting Offset, could be 0
for _ in audioCommentsArray {
UIView.animateWithDuration(0.15, animations: {
let commentView = AudioCommentView(frame: CGRectZero)
commentView.frame = CGRect(x: 0, y: buttonY, width: 75, height: 75)
buttonY = buttonY + 75 // we are going to space these UIButtons 75px apart
commentView.alpha = 1.0
audioCommentsListScrollView.addSubview(commentView)
})
}
I want to add these commentView's using a simple animation. However only the last commentView is added correctly to the scrollView whereas the above views are added like this:
Only the background of the commentView is shown whereas the other elements are not visible.
Does anyone have an idea what I might be missing? Adding views using a for loop shouldn't be complicated as I have done this many times before but this time I seem to miss something?
Thank you in advance!
The UIView animations are overlapping and interfering with each other as you process them through the loop. Instead, you should chain them so that the next animation does not interfere with the other. Delete the loop. Then call the animations one after the other in the completion handler. You can call it recursively to ensure each button is animated.
var count = 0
func startButtonsAnimation() {
UIView.animateWithDuration(0.15, animations: {
let commentView = AudioCommentView(frame: CGRectZero)
commentView.frame = CGRect(x: 0, y: buttonY, width: 75, height: 75)
buttonY = buttonY + 75 // we are going to space these UIButtons 75px apart
commentView.alpha = 1.0
audioCommentsListScrollView.addSubview(commentView)
}, completion: { (value: Bool) in
if self.count < self.audioCommentsArray.count {
self.count += 1
self.startButtonsAnimation()
}
})

ViewController won't reload properly on first Simulator launch

I have a weird issue that I just found out when I re-installed and launched my project first time on Simulator
My main ViewController has a collection view that will show a list of recipes. After a recipe is added and navigated back to this screen, the list will reflect the update.
There is also a ScrollView that appears when recipe.count > 0 with subview containing buttons (to filter list by category). The scrollview reflect the update on the recipes but it won't work on the very first launch in Simulator.
I've tried reloading collectionview, fetching recipe data on viewDidAppear, viewWillAppear but no luck with the first-time launch.
How can I make sure the first app launch experience is the same with the subsequent launches? Is there any known issue with first-time launch on Simulator that I should know about?
Here is the code that creates buttons and add to ScrollView.
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
fetchRecipe()
collection.reloadData()
if recipes.count == 0 {
categoryScrollView.hidden = true
categoryScrollViewHeight.constant = 0
} else {
populateCategoryScrollView()
}
}
func populateCategoryScrollView() {
//Create category sort buttons based on fetched category.name and add to scrollview
if recipes.count > 0 {
var categories: [String] = []
for recipe in recipes {
if let value = recipe.category {
let category = value as! RecipeCategory
categories.append(category.name!)
}
}
var distinctCategories = Array(Set(categories))
var widthStack = 0
if distinctCategories.count != 0 {
for subView in categoryScrollView.subviews {
subView.removeFromSuperview()
}
for i in 0..<distinctCategories.count {
//Pilot button creation to get width
let frame1 = CGRect(x: 0, y: 6, width: 80, height: 40 )
let button = UIButton(frame: frame1)
button.setTitle("\(distinctCategories[i])", forState: .Normal)
button.sizeToFit()
let buttonWidth = Int(button.frame.size.width)
var frame2: CGRect
if i == 0 {
frame2 = CGRect(x: 10, y: 6, width: buttonWidth+20, height: 30 )
} else {
frame2 = CGRect(x: 10 + widthStack, y: 6, width: buttonWidth+20, height: 30 )
}
widthStack += buttonWidth+32
let button1 = UIButton(frame: frame2)
let attributedTitle = NSAttributedString(string: "\(distinctCategories[i])".uppercaseString, attributes: [NSKernAttributeName: 1.0, NSForegroundColorAttributeName: UIColor.blackColor()])
button1.setAttributedTitle(attributedTitle, forState: .Normal)
button1.titleLabel!.font = UIFont(name: "HelveticaNeue", size: 13.0)
button1.layer.borderWidth = 2.0
button1.layer.borderColor = UIColor.blackColor().CGColor
button1.backgroundColor = UIColor.whiteColor()
button1.addTarget(self, action: "filterByCategory:", forControlEvents: UIControlEvents.TouchUpInside)
self.categoryScrollView.addSubview(button1)
}
}
}
}
The first time you enter the application, the recipes.count is 0, so you are hiding the scroll view by the constraint categoryScrollViewHeight.constant = 0. So next time you create a new recipe, the scroll view is still with a zero height. That's why you didn't see the scroll on first time launch.

Resources