How can I remove the imageView from effectView? Currently I am animating it in when the user loses an Internet connection. But I can't figure out how to remove the imageView once the connection is established. The effectView gets animated out but the image is still on the view. I tried using removeFromSuperview(), but most likely I didn't use it the right way.
func internetConnection() {
let image = UIImage(named: "nointernet.png")
let imageView = UIImageView(image: image!)
if reach.connection == .none {
imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
imageView.center = self.effectView.center
self.effectView.contentView.addSubview(imageView)
effectView.isUserInteractionEnabled = true
imageView.alpha = 0
UIView.animate(withDuration: 0.4) {
self.effectView.effect = self.effect
imageView.alpha = 1
imageView.transform = CGAffineTransform.identity
}
}else{
effectView.isUserInteractionEnabled = false
UIView.animate(withDuration: 0.3, animations: {
imageView.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
imageView.alpha = 0
self.effectView.effect = nil
}) { (Bool) in
imageView.removeFromSuperview()
}
}
}
The problem is that when the connection comes back, you create a new image view and then attempt to remove the new image view. But you need to remove the existing image view. Update your code to get access to the existing image view in the effectView and remove it.
func internetConnection() {
if reach.connection == .none {
let image = UIImage(named: "nointernet.png")!
let imageView = UIImageView(image: image)
imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
imageView.center = self.effectView.center
imageView.alpha = 0
self.effectView.contentView.addSubview(imageView)
effectView.isUserInteractionEnabled = true
UIView.animate(withDuration: 0.4) {
self.effectView.effect = self.effect
imageView.alpha = 1
imageView.transform = CGAffineTransform.identity
}
} else {
if let imageView = self.effectView.contentView.subviews.last as? UIImageView {
effectView.isUserInteractionEnabled = false
UIView.animate(withDuration: 0.3, animations: {
imageView.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
imageView.alpha = 0
self.effectView.effect = nil
}) { (finished) in
imageView.removeFromSuperview()
}
}
}
}
One only potential issue. Make sure that internetConnection isn't called twice in a row when the connection is lost. Otherwise you will end up adding multiple image views to the effectView.
Related
I want to make it appear as if my image is slowly getting filtered from top to bottom. I am adding two image views. My processed image is in the background and non-processed in the front. I am making the height of non-processed image 0. Here is my code.
imageView.frame = CGRect(x: 0, y: 100, width: self.view.bounds.size.width, height: 400)
imageView.image = processedImage
let nonProcessedImageView = UIImageView()
nonProcessedImageView.frame = CGRect(x: 0, y: 100, width: self.view.bounds.size.width, height: 400)
nonProcessedImageView.image = nonProcessedImage
view.addSubview(nonProcessedImageView)
UIView.transition(with: nonProcessedImageView,
duration: 5.0,
animations: {
nonProcessedImageView.frame = CGRect(x: 0, y: 500, width: self.view.bounds.size.width, height: 0)
},
completion: {_ in
})
The non processed image does not even appear on top of the processed.
The issue seems to be that changing the y coordinate of the frame in the animation block leads to issues when using the UIView.Animate function.
See this answer
To quote the most essential part:
You should change the center property (to move the view) and bounds
property (to change its size) instead. Those properties behave as
expected.
Since you want to just reduce the height, you don't need to do anything to the y coordinate
let nonProcessedImageView = UIImageView()
var newImageFrame = imageView.frame
nonProcessedImageView.frame = newImageFrame
nonProcessedImageView.clipsToBounds = true
nonProcessedImageView.contentMode = .scaleAspectFit
nonProcessedImageView.image = imageView.image
view.addSubview(nonProcessedImageView)
// I set this to 1 instead of 0 as 0 does not show the image
// for some reason
newImageFrame.size.height = 1
// Update your processed image
imageView.image = processedImage
UIView.animate(withDuration: 5.0) {
nonProcessedImageView.frame = newImageFrame
}
completion: { (isComplete) in
if isComplete {
nonProcessedImageView.removeFromSuperview()
}
}
This gives some results but as you can see, the animation is not so good because as you reduce the height, the image view's contentMode kicks in and gives the seen effect.
For best results, add the new image view to a UIView and reduce the height of the UIView instead of the UIImageView
var newImageFrame = imageView.frame
let containerView = UIView(frame: newImageFrame)
containerView.clipsToBounds = true
let nonProcessedImageView = UIImageView()
nonProcessedImageView.frame = containerView.bounds
nonProcessedImageView.clipsToBounds = true
nonProcessedImageView.contentMode = .scaleAspectFit // same as original
nonProcessedImageView.image = imageView.image
containerView.addSubview(nonProcessedImageView)
view.addSubview(containerView)
newImageFrame.size.height = 1
imageView.image = processedImage
UIView.animate(withDuration: 3.0) {
containerView.frame = newImageFrame
}
completion: { (isComplete) in
if isComplete {
containerView.removeFromSuperview()
}
}
This should give you what you want:
This works for top to bottom transition.
imageView.frame = CGRect(x: 0, y: 100, width: self.view.bounds.width, height: 400)
imageView.image = nonProcessedImage
view.addSubview(imageView)
let frontView = UIView(frame: CGRect(x: 0, y: 100, width: self.view.frame.width, height: 0))
frontView.clipsToBounds = true
view.addSubview(frontView)
let nonProcessedImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 400))
nonProcessedImageView.image = processedImage
nonProcessedImageView.clipsToBounds = true
frontView.addSubview(nonProcessedImageView)
UIView.transition(with: frontView, duration: 5, options: [.allowAnimatedContent], animations: {
frontView.frame = CGRect(x: 0, y: 100, width: self.view.frame.width, height: 400)
}, completion: nil)
I have a Pan and Tap gestures, They don't seem to get recognized when I try to activate them on the simulator.
I don't get any error, tried to debugging, it seems that it does assign the gestures, but won't call the methods I try to use when they happened, like it just don't recognize them at all.
This is what I have:
dismissIndicator: (Attaching the Pan recognizer to this view):
let dismissIndicator: UIView = {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.appWhite
view.layer.cornerRadius = 15
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
let dismissImg = UIImageView(image: UIImage(named: "closeArrow"))
dismissImg.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(dismissImg)
dismissImg.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
dismissImg.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
return view
}()
This is how I set the gestures:
let dimView = UIView()
let detailView = DetailsView()
var currentPost: Post? = nil
init(post: Post) {
super.init()
dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
let panGesture = PanDirectionGestureRecognizer(direction: .vertical(.down), target: self, action: #selector(panGestureRecognizerHandler(_:)))
detailView.addGestureRecognizer(panGesture)
setPostDetails(post: post)
}
This is handleDismiss (Which does not get called when tapping on the DimView:
#objc func handleDismiss(){
UIView.animate(withDuration: 0.5) {
self.dimView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.detailView.frame = CGRect(x: 0, y: window.frame.height, width: self.detailView.frame.width, height: self.detailView.frame.height)
}
}
}
This is my panGestureRecognizerHandler:
#objc func panGestureRecognizerHandler(_ sender: UIPanGestureRecognizer) {
let touchPoint = sender.location(in: detailView.dismissIndicator.window)
var initialTouchPoint = CGPoint.zero
switch sender.state {
case .began:
initialTouchPoint = touchPoint
case .changed:
if touchPoint.y > initialTouchPoint.y {
detailView.frame.origin.y = initialTouchPoint.y + touchPoint.y
}
case .ended, .cancelled:
if touchPoint.y - initialTouchPoint.y > 300 {
handleDismiss()
} else {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
if let window = UIApplication.shared.keyWindow {
let height = window.frame.height - 80
let y = window.frame.height - height
self.dimView.alpha = 1
self.detailView.frame = CGRect(x: 0, y: y, width: self.detailView.frame.width, height: self.detailView.frame.height)
}
})
}
case .failed, .possible:
break
default:
break
}
}
This is where I add dimView and detailView to the screen:
func showDetailView(){
if let window = UIApplication.shared.keyWindow {
dimView.backgroundColor = UIColor(white: 0, alpha: 0.5)
window.addSubview(dimView)
window.addSubview(detailView)
let height = window.frame.height - 80
let y = window.frame.height - height // This is so we can later slide detailView all the way down.
detailView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: height)
dimView.frame = window.frame
dimView.alpha = 0
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.dimView.alpha = 1
self.detailView.frame = CGRect(x: 0, y: y, width: self.detailView.frame.width, height: self.detailView.frame.height)
})
}
}
I am facing issue in frame while doing rotation animation in custom UIBarButtonItem.
My code is as follow:
class ViewController: UIViewController {
var bellIcon: UIBarButtonItem = UIBarButtonItem()
override func viewDidLoad() {
super.viewDidLoad()
setupBellButton()
}
func setupBellButton(){
let icon = UIImage(named: "Bell.png")
let iconSize = CGRect(origin: CGPoint.zero, size: icon!.size)
let iconButton = UIButton(frame: iconSize)
iconButton.setBackgroundImage(icon, for: .normal)
let badgeView = UIView(frame: CGRect(origin: CGPoint(x: iconButton.frame.maxX-10, y: iconButton.frame.minX), size: CGSize(width: 10, height: 10)))
badgeView.layer.cornerRadius = 5
badgeView.layer.borderColor = UIColor.red.cgColor
badgeView.backgroundColor = UIColor.red
badgeView.layer.masksToBounds = true
badgeView.clipsToBounds = true
let btnContainer = UIView(frame: CGRect(origin: CGPoint.zero, size: icon!.size))
btnContainer.addSubview(iconButton)
btnContainer.addSubview(badgeView)
badgeView.transform = CGAffineTransform(scaleX: 0, y: 0)
UIView.animate(withDuration: 1) {
badgeView.transform = CGAffineTransform(scaleX: 1, y: 1)
}
bellIcon.customView = btnContainer
iconButton.addTarget(self, action:#selector(bellIconTapped), for: .touchUpInside)
navigationItem.rightBarButtonItem = bellIcon
bellIcon.customView?.backgroundColor = UIColor.purple
}
#IBAction func bellIconTapped(_ sender: Any) {
UIView.animate(withDuration: 0.3, animations: {
self.bellIcon.customView!.transform = CGAffineTransform(rotationAngle: -0.4)
}) { (true) in
UIView.animate(withDuration: 0.3, animations: {
self.bellIcon.customView!.transform = CGAffineTransform(rotationAngle: 0.4)
}, completion: { (true) in
UIView.animate(withDuration: 0.4, animations: {
self.bellIcon.customView!.transform = CGAffineTransform.identity
})
})
}
}
}
Thanks in advance!!!
I have resolved the above issue for both iOS 10 and 11 by simply adding the constant to the 'bellIcon.customView'
I have added following code at the end of the 'setupBellButton()'
bellIcon.customView?.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint = bellIcon.customView?.widthAnchor.constraint(equalToConstant: 25.0)
let heightConstraint = bellIcon.customView?.heightAnchor.constraint(equalToConstant: 25.0)
bellIcon.customView?.addConstraints([widthConstraint!, heightConstraint!])
Happy Coding!!!
I have punch of images(5-6 images) that should animatedly scrolling on back ground with different speed. I put them just on UIView not in UIScrollView.
Right now I have this method for it:
I adding images on the screen that user see and copy of images behind the screen. But because of that images and copyImages(behind the screen) must pass the same distance them has different speed(Not that I put).
So it's not working properly for me.
Could anyone help with that?
P.S. Some images has different size so i need change yPos for them. Just for elaboration.
func setUpBackGroundLayersWithArray(){
var xPos: CGFloat = 0
for (index,image) in self.backGroundsImages.reverse().enumerate(){
var yPos:CGFloat = -30
switch index {
case 1: yPos = -10
xPos = 320
case 2: yPos = -10
default: yPos = -30
}
let imageView = UIImageView(image: image)
imageView.frame = CGRectMake(xPos, yPos, image.size.width, image.size.height)
imageView.contentMode = .ScaleAspectFit
self.addSubview(imageView)
let copyimageView = UIImageView(image: image)
copyimageView.frame = CGRectMake(320, yPos, image.size.width, image.size.height)
copyimageView.contentMode = .ScaleAspectFit
self.addSubview(copyimageView)
self.layoutIfNeeded()
let animator = UIViewPropertyAnimator(duration:8 - Double(index + 1), curve: .Linear, animations: {
UIView.animateKeyframesWithDuration(0, delay: 0, options: [.Repeat], animations: {
imageView.frame = CGRectMake(0 - copyimageView.frame.size.width, imageView.frame.origin.y, imageView.frame.size.width, imageView.frame.size.height)
}, completion: nil)
})
let secondAnimator = UIViewPropertyAnimator(duration:10 - Double(index + 1), curve: .Linear, animations: {
UIView.animateKeyframesWithDuration(0, delay: 0, options: [.Repeat], animations: {
copyimageView.frame = CGRectMake(0-copyimageView.frame.size.width, copyimageView.frame.origin.y, copyimageView.frame.size.width, copyimageView.frame.size.height)
}, completion: nil)
})
self.animators.append(animator)
self.animators.append(secondAnimator)
}
}
You can use UICollectionView for simple showing and animating on images. For animation you need use scrollToItemAtIndexPath:atScrollPosition:animated: method after needed time interval
Everything works perfectly except that i cant zoom in on the imageview.
var zoomingImageView = UIImageView()
var startingFrame : CGRect?
var blackBackground : UIScrollView?
var startingImageView: UIImageView?
this is where i set up the scrollview and the imageview inside of it
func performZoomingForStartImageView(imageView: UIImageView){
if let keyWindow = UIApplication.shared.keyWindow {
blackBackground = UIScrollView(frame: CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.size.width, height: self.view.frame.size.height))
blackBackground?.delegate = self
blackBackground?.maximumZoomScale = 4.0
blackBackground?.minimumZoomScale = 1.0
blackBackground?.isScrollEnabled = true
blackBackground?.backgroundColor = .black
blackBackground?.alpha = 0.0
blackBackground?.isUserInteractionEnabled = true
blackBackground?.contentSize = CGSize(width: 0, height: self.view.frame.size.height)
keyWindow.addSubview(blackBackground!)
self.startingImageView = imageView
self.startingImageView?.isHidden = true
startingFrame = imageView.superview?.convert(imageView.frame, to: nil)
zoomingImageView = UIImageView(frame: startingFrame!)
zoomingImageView.image = startingImageView?.image
zoomingImageView.contentMode = .scaleAspectFit
zoomingImageView.isUserInteractionEnabled = true
zoomingImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleZoomOut)))
zoomingImageView.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.saveImage)))
blackBackground?.addSubview(zoomingImageView)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut,.allowUserInteraction], animations: {
self.blackBackground?.alpha = 1.0
self.inputContainerView.alpha = 0
//let height = startingFrame!.height / startingFrame!.height * keyWindow.frame.widthimagedShown
self.zoomingImageView.frame = CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.size.width, height: self.view.frame.size.height)
self.zoomingImageView.center = keyWindow.center
}, completion: nil)
}
}
this is supposed to allow the zooming but the print never gets called and i have set up the delegate
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
print("hi")
return zoomingImageView
}
For Swift 3 the method signature has changed.
Swift 3:
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}