In my application I want to animate a view from bottom to up in override func viewDidLayoutSubviews(). So far I have done this way to transit the view from bottom but in different device it is showing different UI. UIView transition is working but view is not fully transitioning from bottom expect iPhone XS. So far I have done these two ways :
First Step:
let currentFrame = self.animationVIew.frame
self.animationVIew.frame = CGRect(x: 0, y: self.view.frame.height, width: currentFrame.size.width, height: currentFrame.size.height)
UIView.animate(withDuration: 1.0) {
self.animationVIew.frame = currentFrame
self.loadViewIfNeeded()
}
Second Step:
UIView.transition(with: animationVIew,
duration: 0.5,
options: [.transitionFlipFromBottom],
animations: {
},
completion: nil)
But for both way I am facing same problem. No luck!!
It was my mistake I did not think in proper way to solve it.Anyway if anyone face similar kind of problem then may be this answer will help them. I have just changed the current autolayout to fix this issue.This problem was happening because of constraint between animationView and topView. The distance constraint between these two view was fixed, That is why when animation was happening it was resizing the animationView to adjust the constraints.So my solution was making the animation view height constraint fixed and have changed the priority required to low of distance constraint between animationView and topView in storyboard
Related
I have a view and a subview inside it.
In the subview there are a few Gesture Recognizers that work.
They are all added normally when the subview is created, for example:
let assetViewTapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleAssetViewTap(_:)))
assetViewTapGesture.delegate = self
assetView.addGestureRecognizer(assetViewTapGesture)
in the superview I have a UIPinchGestureRecognizer, that calls a function in the subview, which, in its turn changes sizes of frames in the subview:
public func scale(newWidth:CGFloat){
let factor:CGFloat = newWidth / self.internalAssetView.frame.width
self.internalAssetView.frame = CGRect(x: self.internalAssetView.frame.origin.x, y: self.internalAssetView.frame.origin.y, width: newWidth, height: self.internalAssetView.frame.height)
self.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: newWidth + additionParameter, height: self.frame.height)
}
When I pinch, everything behaves well, and the views change their sizes the way they should.
The problem:
after the pinch ends, I tap in order to fire the TapGestureRecognizer inside the subview. It is being fired, but only after a few seconds. Actually - the more I pinch the views, the longer I wait, between tapping the screen and the actual firing of the recognizer. How come changing views's frames can affect the delay of a Gesture Recognizer's firing?
I do not know the reason for this however, I can offer a solution that might mitigate your issue.
There is no need to add an independent tap gesture, in-fact every single UIVIEW has a touches method. So you can delete the tap gesture and try typing this function
override func touchesBegan()
I've omitted the arguments, when you start typing touches... it will auto complete. Put the code you want to execute within it. Similarly there are touches started, touches ended methods also which you can use. Try the UIVIEW Touch methods. Hopefully they wont lag and your issue will be resolved.
in my swift app I’ve some issues about view constraints.
I present this view(.xib file) by using scale method to get scale from center animation.
view.addSubview(xibView)
UIView.animate(duration: 0.5) {
xibView.transform = scaleBy(x: 0.01, y: 0.01)
}, completion: { _ in
xibView.transform = .identity
})
But I lose all constraints in this way and this happen only with iPad.
What’s the problem?
When you add or remove a view to another view you loose any previous constraints you had.
You need to set the constraints after adding xibView to view.
Swift View Animation from Line
Swift 4.2, Xcode 10.0
I want to perform an animation with a view wherein it builds out or into a line.
Build out of line:
Build into line:
My only solution at this point is to put another view on the opposite side of the line to cover the view before it starts to animate. But this is a bad solution and it can sometimes block other views. Also, I would like to avoid stretching my content because I want to be able to perform this animation with images. What would be the best way to integrate this. Thank you in advance for the help!
So you want this:
A subview can extend outside the bounds of its superview. When this happens, the part of the subview that is outside the superview is visible if the superview's clipsToBounds property is false, and hidden if the superview's clipsToBounds is true.
I used this clipping to hide the part of the green view that is below the line. Perhaps these side views at the halfway point of the animation will help explain how I set it up:
The superview (labelled “hider”) has been shrunk here to half the height of the green view. With clipping enabled, half of the green view is hidden. With clipping disabled, the bottom of the green view is visible below the line.
Here is how I set up the demo:
I put the green view in a new superview. I call this new superview the “hider”.
I turned on the “Clips to Bounds” check box in the hider's Attributes inspector.
I set width and height constraints on the green view. You could use other constraints (like leading/trailing) to control its size.
I constrained the leading, trailing, and top edges of the green view to equal the corresponding edges of the hider.
I constrained the height of the hider to equal the height of the green view plus 12. The 12 will make the white margin between the green view and the line. I set this constraint's priority to 999. This means auto layout will try very hard to satisfy the constraint, but will break this constraint if necessary in order to satisfy all required constraints.
I created a constraint setting the hider's height to zero. I connected this constraint to an outlet named hiderHeightConstraint in my view controller. Note that this means there are two constraints on the hider's height. This one forces the hider's height to zero and is required. The other (created in the prior step) sets the hider's height to the green view's height plus padding, but is not required. When you are editing the layout in the storyboard, you probably want to turn off this constraint by turning off its “Installed” check box in the constraint's Attributes inspector.
I connected the “Toggle Secret Message” button to this action:
#IBAction private func toggleButtonWasTapped() {
UIView.animate(withDuration: 0.7) {
self.hiderHeightConstraint.isActive.toggle()
self.view.layoutIfNeeded()
}
}
Here's how it works. When the hiderHeightConstraint is active, the hider is forced to zero height. Since it clips its subviews, the green view is not visible. Since the constraint between the hider's height and the green view's height is not required, the green view can still be its natural size. The other constraints (leading/trailing/top) force the green view to stick out beyond the bottom edge of the hider.
When the hiderHeightConstraint is not active, the hider grows to the height of the green view plus the padding, allowing the green view to be fully visible.
Here is what my storyboard looks like:
This is one approach you can follow to achieve this:
Take 2 Container UIView and 2 line view (you may take UIView again) and 2 UIImageView (or any for green view). So your StoryBoard should look like this :
Make sure your container views are clips to bound (you can set it from storyboard only).
Now you can achieve the animation easily by CGAffineTransform.
I will call outlet names for animated view as imageViewOne and imageViewTwo.
In viewDidLoad() write this (if initially views are hidden):
imageViewOne.transform = CGAffineTransform(translationX: 0.0, y: containerViewOne.bounds.height)
imageViewTwo.transform = CGAffineTransform(translationX: 0.0, y: -containerViewOne.bounds.height)
Then to show it with animation:
//To build out of line
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.imageViewOne.transform = .identity
self.imageViewTwo.transform = .identity
}, completion: nil)
//To build into line
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.imageViewOne.transform = CGAffineTransform(translationX: 0.0, y: containerViewOne.bounds.height)
self.imageViewTwo.transform = CGAffineTransform(translationX: 0.0, y: -containerViewOne.bounds.height)
}, completion: nil)
Use a view but then put in that view the line to make it look authentic.
Then when you are done make the whole animation disappear or if you want to make other animations go over it , you can call functions to move views above others
https://stackoverflow.com/a/38780829/4674760
I have got an UIButton on a storyboard ViewController. When I load data into the form and the layout is significantly changing the button does not recognise the touch action.
I have figured out that when button is visible on the scrollview right after it if filled with data, the touch action works.
If the data too long and the button is not visible at first, just when it is scrolled into the display, the touch action does not work.
I was checking if something is above the button, but nothing. I have tried to change the zPosition of the button, not solved the problem.
What can be the issue?
I have made custom classes from the UIScrollView and the UIButton to check how the touches event triggered. It is showing the same behaviour, which is obvious. If the button is visible right at the beginning, the UIButton's touchesBegan event is triggered. If the button moves down and not visible at the beginning, it is never triggered, but the scrollview's touchesBegan is called instead.
Depending on the size of the data I load into the page sometimes the button is visible at the beginning, but the form can be still scrolled a bit. In this case the button still work, so it seems that this behaviour is not depending on if the scrollview is scrolled before or not, just on the initial visibility of the button.
Is there any layout or display refresh function which should be called to set back the behaviour to the button?
The code portion which ensures that the contentview is resized for the scroll if the filled data requires bigger space.
func fillFormWithData() {
dispDescription.text = jSonData[0]["advdescription"]
dispLongDescription.text = jSonData[0]["advlongdesc"]
priceandcurrency.text = jSonData[0]["advprice"]! + " " + jSonData[0]["advpricecur"]!
validitydate.text = jSonData[0]["advdate"]!
contentview.layoutIfNeeded()
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
contentview.frame.size.height = contentRect.size.height
scrollview.contentSize = contentview.bounds.size
}
Ok, so another update. I have coloured the contentview background to blue and the scrollview background to white. When I load the data and resize the layout constraints, the contentview is resizing as expected, however now the scrollview is going to the bottom. After I scroll the view it is resizing to the original size which fits the screen. Now the button is only recognised when I touch the are which is blue behind. With the white background it is not recognised anymore, so it seems that the scrollview is hiding the button.
Let me get this clear the button is added in storyboard and it is a spritekit project?? If you are using zPosition?? Why don’t u connect the UIButton via the assistant editor as an IBAction then the action is always tied to the button.
You can also do it differently
Create an SKLabelNode and put it on the screen where you want to have the button and then set a name to it as myButton
override func touchesBegan(_ touches: Set<UITouch>, with event:
UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let tappedNodes = nodes(at: location)
for node in tappedNodes {
if node.name == "myButton" {
// call your action here
}
}
}
}
EDIT 1:
You could also try auto resizing your scrollView.content this works also if you are adding any views via the app or programmatically
private func resizeScrollView(){
print("RESIZING THE SCROLLVIEW from \(scrollView.contentSize)")
for view in scrollView.subviews {
contentRect = contentRect.union(view.frame)
}
scrollView.contentSize = CGSize(width: contentRect.size.width, height: contentRect.size.height + 150)
print("THE CONTENT SIZE AFTER RESIZING IS: \(scrollView.contentSize)")
}
EDIT 2: I think I found the issue with your project. You need to move the MessageButton(UzenetButton) above DispDescription label in the object inspector in that way it will always be above your message textView.
At the moment the UzeneButton is at the very far back in your view hierarchy so if your textView is resizing whilst editing it covers the button that is why you cannot click on it.
See #Endre Olah,
To make situation more clear do one more thing, set clipToBound property of contentview to true.
you will notice that after loading of data your button not fully visible, it means it is shifting out of bound of its parentView (ContentView)
And that's why button is not taking your touch. However, if you carefully touch upper part of button it still do its job. Because upper part is still in bound of ContentView
Solution :
After loading of data you have to make sure that you increase height of ContentView such that button should never go out of bound of its parentView(ContentView).
FOR EXAMPLE
#IBOutlet var heightConstraintOfContentView : NSLayoutConstraint!
After loading of data
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
heightConstraintOfContentView.constant = contentRect.size.height
contentView.layoutIfNeeded()
I use following steps when I need to use scrollview with dynamic content:
1) Firstly add a scrollView with top, bottom, trailing and leading is 0 to super view.
2) Add a view to scrollView and view's trailing, leading bottom and top space to scrollView can be set to 0 (or you can add margin optionally).
3) Now, you should add UI elements like buttons, labels with appropriate top, bottom, trailing and leading margins to each other.
4) Lastly, add equal height and equal width constraint to view with Safe Area:
and change equal height priority of view to 250:
It should solve your problem with UIScrollView.
Finally, I have found the solution in another chain, once it became clear that the scrollview's contentview is resizing on scroll event to the original size. (Not clear why this is like this, but that is the fact.)
So I had to add a height constraint to the contentview in the storyboard and create an outlet to it and adjust this constraint when the content size is changing, like this:
#IBOutlet weak var ContentViewHeight: NSLayoutConstraint!
func fillFormWithData() {
dispDescription.text = jSonData[0]["advdescription"]
dispLongDescription.text = jSonData[0]["advlongdesc"]
priceandcurrency.text = jSonData[0]["advprice"]! + " " + jSonData[0]["advpricecur"]!
validitydate.text = jSonData[0]["advdate"]!
contentview.layoutIfNeeded()
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
contentview.bounds = contentRect
scrollview.contentSize = contentRect.size
----------- This is the key line to the success ----------
ContentViewHeight.constant = contentRect.size.height
----------------------------------------------------------
}
After this is added, it works perfectly.
I'm using material components for iOS for making a draggable bottom sheet, that starts half screen (preview mode), and you can drag to full screen. But I don't know how, and the documentation is not very complete. https://material.io/develop/ios/components/bottom-sheet/
I present my DetailViewController within the MDCBottomSheetController:
let graphDetailViewController = UIStoryboard.detail().instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
let bottomSheet: MDCBottomSheetController = MDCBottomSheetController(contentViewController: graphDetailViewController)
bottomSheet.preferredContentSize = CGSize(width: self.view.frame.size.width, height: self.view.frame.size.height / 2)
present(bottomSheet, animated: true, completion: nil)
With this code, my vc is appearing half screen, but it's not possible to drag it up to full screen.
Is anyone experiences with material components (bottom sheet) for iOS and could help me?
Thanks a lot!
Everything is right except you set the preferredContentSize to be 1/2 the screen height.
bottomSheet.preferredContentSize = CGSize(width: self.view.frame.size.width, height: self.view.frame.size.height / 2)
Is the line causing your error. That line is telling the bottom sheet that there is no more content to scroll. You can delete it. If you want to have your contentViewController be a scroll view you may want to use the trackingScrollView property.