Stretching UIView in Swift - ios

So I have two view and a button one of the view is hidden "View1" and the other is not "View2" , and once I click on the button "View1" will appear , but I want "View2" to go down , I will add image show what I want to do

Brute way, if your views has fix sizes:
1, create 3 variables for the position of the views
var view1Center: CGPoint = CGPoint(x: 100, y: 100)
var view2CenterBefore: CGPoint = CGPoint(x: 100, y: 100)
var view2CenterAfter: CGPoint = CGPoint(x: 100, y: 400)
2, In ViewDidLoad or ViewDidAppear set view's alpha to 0 and the positions
view1.alpha = 0
view2.alpha = 0
view1.center = view1Center
view2.center = view2CenterBefore
3, When you press the button, you should animate the moves and show view1.
#IBAction func showView1(_ sender: Any) {
UIView.animate(withDuration: 0.5, animations: {
view2.center = view2CenterAfter
})
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
UIView.animate(withDuration: 0.5, animatios: {
view1.alpha = 1
}
}
}

We can use UIStackView class for this with ease. We can add both the views in a UIStackView instance with vertical axis. view1 height should have Highest constraint priority (999) instead of required (1000). This way when view1 is hidden, it's height constraint won't have conflict.
You can take reference of following code snippet:
let stack = UIStackView()
// aligns subviews in top to bottom manner
stack.axis = .vertical
stack.addArrangedSubview(view1)
stack.addArrangedSubview(view2)
When view1 is kept hidden, view2 will automatically move up. StackView should not be given height constraint so that it will have inferred height of summation of its subviews.

Related

Resize MapView on tap

I have mapView to my ViewController in storyboard. It has 4 constraints ( Height equals: 300 , align trailing to: Safe Area, align Leading to: Safe Area and Align Top to: Safe Area. I created tapGestureRecognizer to my mapview. I want to resize mapview to full screen when user tap on a map.
private func setupMapView() {
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(mapTouchAction(gestureRecognizer:)))
mapView.addGestureRecognizer(gestureRecognizer)
}
#objc func mapTouchAction(gestureRecognizer: UITapGestureRecognizer) {
}
I tried to change height constraint to screen Height but it is not what i want ( view is positioning in the middle of the screen and then its resizing , this is not what i am looking for) I used this code inside my mapTouchAction(). I want my view to resize with animation to the bottom view ( screen height). How can i achieve it? should i use CGAffineTransform or maybe theres another way? Any ideas?
for constraint in self.mapView.constraints {
if constraint.identifier == "mapViewHeightConstraintID" {
constraint.constant = UIScreen.main.bounds.height
}
}
UIView.animate(withDuration: 2, animations: {
self.mapView.layoutIfNeeded()
})
I found an easy solution for my problem. I only change my mapView frame. So inside tapGesture function mapTouchAction(_: UITapGestureRecognizer) i have
#objc func mapTouchAction(gestureRecognizer: UITapGestureRecognizer) {
UIView.animate(withDuration: 2, animations: {
self.mapView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
self.mapView.layoutIfNeeded()
})
}
This code change my mapView height to UIScreen Height without with a nice resizing animation for 2 seconds.

Want to present modal view but how to make it take up screen partially?

I have two viewcontrollers. (See the attachment) Viewcontroller on the left is called "ViewController" and Modal viewController on the right is known as "draggableViewController". When I run this app, blue view object on the right will take over the first viewController. Blue view object on the Right Modal viewController should slide over on top of the first viewController, but I am presenting that view with no animation, so it will appear as if blue view has been resting on top of first ViewController.
What I want to achieve is to show only 1/3 of the blue view and I want it to rest on bottom 1/3 of the screen. I have no idea on how to set the position of blue view
to show it only on bottom 1/3 of the screen.
Here is my source code for each of viewControllers.
1: ViewController:
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
let newView = self.storyboard!.instantiateViewController(withIdentifier: "draggableViewController") as! draggableViewController
newView.modalPresentationStyle = UIModalPresentationStyle.overFullScreen
self.present(newView, animated: false, completion: nil)
}
}
2: draggableViewController:
class draggableViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.frame = CGRect(x: 0, y: 300, width: self.view.frame.width, height: self.view.frame.height)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognizerAction(_:)))
self.view.addGestureRecognizer(panGestureRecognizer)
}
#objc func panGestureRecognizerAction(_ gestureRecognizer: UIPanGestureRecognizer){
// 1. use these values to restrict the left and right sides so the orangeImageView won't go beyond these points
let leftSideRestrction = self.view.frame.minX
let rightSideRestriction = self.view.frame.maxX
let topRestriction = 0
// 2. use these values to redraw the orangeImageView's correct size in either Step 6 or Step 8 below
let imageViewHeight = self.view.frame.size.height
let imageViewWidth = self.view.frame.size.width
if gestureRecognizer.state == .changed || gestureRecognizer.state == .began {
let translation: CGPoint = gestureRecognizer.translation(in: self.view)
gestureRecognizer.view!.center = CGPoint(x: gestureRecognizer.view!.center.x + translation.x, y: gestureRecognizer.view!.center.y + translation.y)
gestureRecognizer.setTranslation(CGPoint.zero, in: self.view)
/*
3.
-get the the upper left hand corner of the imageView's X and Y origin to get the current location of the imageView as it's dragged across the screen.
-you need the orangeImageView.frame.origin.x value to make sure it doesn't go beyond the left or right edges
-you need the orangeImageView.frame.origin.y value to redraw it in Steps 6 and 8 at whatever Y position it's in when it hits either the left or right sides
*/
var imageViewCurrentOrginXValue = self.view.frame.origin.x
var imageViewCurrentOrginYValue = self.view.frame.origin.y
print("origin x: \(imageViewCurrentOrginXValue)")
print("origin y: \(imageViewCurrentOrginYValue)")
// 4. get the right side of the orangeImageView. It's computed using the orangeImageView.frame.origin.x + orangeImageView.frame.size.width
let imageViewRightEdgePosition = imageViewCurrentOrginXValue + imageViewWidth
// 5. if the the orangeImageView.frame.origin.x touches the left edge of the screen or beyond it proceed to Step 6
if imageViewCurrentOrginXValue <= leftSideRestrction {
// 6. redraw the orangeImageView's frame with x: being the far left side of the screen and Y being where ever the current orangeImageView.frame.origin.y is currently positioned at
if imageViewCurrentOrginYValue <= 0 {
// 6. redraw the orangeImageView's frame with x: being the far left side of the screen and Y being where ever the current orangeImageView.frame.origin.y is currently positioned at
imageViewCurrentOrginYValue = 0
self.view.frame = CGRect(x: leftSideRestrction, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
if imageViewCurrentOrginYValue >= self.view.frame.height-200 {
// 6. redraw the orangeImageView's frame with x: being the far left side of the screen and Y being where ever the current orangeImageView.frame.origin.y is currently positioned at
imageViewCurrentOrginYValue = self.view.frame.height - 200
print(imageViewCurrentOrginYValue)
self.view.frame = CGRect(x: leftSideRestrction, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
self.view.frame = CGRect(x: leftSideRestrction, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
// 7. if the the orangeImageView.frame.origin.x touches the right edge of the screen or beyond it proceed to Step 8
if imageViewRightEdgePosition >= rightSideRestriction{
// 8. redraw the orangeImageView's frame with x: being the rightSide of the screen - the orangeImageView's width and y: being where ever the current orangeImageView.frame.origin.y is currently positioned at
if imageViewCurrentOrginYValue <= 0 {
// 6. redraw the orangeImageView's frame with x: being the far left side of the screen and Y being where ever the current orangeImageView.frame.origin.y is currently positioned at
imageViewCurrentOrginYValue = 0
self.view.frame = CGRect(x: leftSideRestrction, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
if imageViewCurrentOrginYValue >= self.view.frame.height - 200 {
// 6. redraw the orangeImageView's frame with x: being the far left side of the screen and Y being where ever the current orangeImageView.frame.origin.y is currently positioned at
imageViewCurrentOrginYValue = self.view.frame.height - 200
print(imageViewCurrentOrginYValue)
self.view.frame = CGRect(x: leftSideRestrction, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
self.view.frame = CGRect(x: rightSideRestriction - imageViewWidth, y: imageViewCurrentOrginYValue, width: imageViewWidth, height: imageViewHeight)
}
}
}
}
Where do I set the position of that blue view?
A simple solution is to set constraints in the blue view to be 1/3 the size of the main controller, and make the background transparent, or blurry so it shows as its sliding up only 1/3 the size.
Steps to this, set a View inside the blue and use that view, and everything behind it to make it transparent.
Where do I set the position of that blue view
In a UIPresentationController subclass, when you do this properly as a custom transition animation.

How to create multiple labels without them all disappearing at once?

I used the code below to create a label every time a button is pressed, then have the label move to a specific location, and then have it delete.
My problem is it can't create multiple labels because it's deleting the label way too soon, because it removes everything named label.
How can I fix this so it creates multiple labels that only get deleted when a label individually completes its animation?
A solution I've thought of, but can't figure out is where you have it create a label with a different name such as label1, label2, and so on, so that way it can delete a specific label when it completes it's animation, instead of deleting all of them.
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
func createLabel() {
// Find the button's width and height
let labelWidth = label.frame.width
// Find the width and height of the enclosing view
let viewWidth = self.view.frame.width
// Compute width and height of the area to contain the button's center
let xwidth = viewWidth - labelWidth
// Generate a random x and y offset
let xoffset = CGFloat(arc4random_uniform(UInt32(xwidth)))
// Offset the button's center by the random offsets.
label.center.x = xoffset + labelWidth / 2
label.center.y = 300
label.font = UIFont(name:"Riffic Free", size: 18.0)
label.textColor = UIColor.white
label.textAlignment = .center
label.text = "+1"
self.view.addSubview(label)
}
func clearLabel() {
UIView.animate(withDuration: 0.9, delay: 0.4, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: .curveLinear, animations: {
self.label.center = CGPoint(x: 265, y: 75 )
}, completion: { (finished: Bool) in
self.label.removeFromSuperview()
})
}
#IBAction func clicked(_ sender: Any) {
createLabel()
clearLabel()
}
The problem is with this code:
#IBAction func clicked(_ sender: Any) {
createLabel()
clearLabel()
}
There are two issues. The first is that you are doing the animation and removal before you even give the label a chance to become part of the interface. You need to introduce a delay (you can use my delay utility, https://stackoverflow.com/a/24318861/341994):
#IBAction func clicked(_ sender: Any) {
createLabel()
delay(0.1) {
clearLabel()
}
}
The second problem, as you have rightly said, is that you have one label instance variable, which you are using to share the label between createLabel and clearLabel. Thus another label cannot come along during the animation.
But you don't actually need any instance variable. So get rid of your label declaration completely! Instead, modify createLabel so that it actually creates the label (i.e. calls UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 100))) as a local variable, and then returns a reference to label it creates, like this:
func createLabel() -> UILabel {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
// ...
return label
}
... And then just have clearLabel take that same label as a parameter so that it moves that label and removes it at the end of the animation, like this:
func clearLabel(_ label : UILabel) {
// ...
}
Your clicked implementation will thus look like this, passing the label out of createLabel and into clearLabel:
#IBAction func clicked(_ sender: Any) {
let label = self.createLabel()
delay(0.1) {
self.clearLabel(label)
}
}
(The remaining details of modifying createLabel and clearLabel to make that work are left as an exercise for the reader.)
Now each tap on the button creates and animates and removes a new label, independently of whatever else may have happened before.

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()
}
})

Limited Area Pinchview in Swift

I have a label on a imageview and both of them are in one view. The label is pinch-able via below code. However, it is pinch-able also out of image view. I want the areas outside of the imageview not seen (like in whatsapp or snapchat. The texts outside of the screen is not seen, i want that for only imageview area). I'm not sure but i guess somehow it will be done with bring view to front function. Just i don't know how to do it
func handlePinch(recognizer: UIPinchGestureRecognizer) {
if let view = recognizer.view as? UILabel {
let pinchScale: CGFloat = recognizer.scale
view.transform = view.transform.scaledBy(x: pinchScale, y: pinchScale)
recognizer.scale = 1.0
}
}
I done it with creating views out of imageview (left, right upper and bottom side) and set all views to the front except the view which has imageview. I created the first three view in (left, right and the bottomside) in the storyboard and last one (the upper one) in the code because i needed to hide status bar first then create a view on top of it.
customView = UIView(frame: CGRect(x: 0, y: 0, width: 440, height: 20))
customView.backgroundColor = UIColor.white
self.view.addSubview(customView)
self.view.bringSubview(toFront: view_main)
self.view.bringSubview(toFront: view2)
self.view.bringSubview(toFront: view3)
self.view.bringSubview(toFront: customView)

Resources