I am trying to make an app that utilizes some search feature and I am trying to make it so that after the search button is pressed, a view (which contains the search results) moves up from the bottom of the superview and replaces the search view. On the storyboard, the resultsView (of type UIView) is constrained so that its top is equal to the superview's bottom. After the search button is pressed, I would like to animate the view to move up and replace the view already at the bottom of the superview. The problem is, in the viewcontroller's class, when I call the resultsView, the animateWithDuration(NSTimeInterval) that is supposed to be associated with the UIView class is not appearing for me. May this be because the view is already constrained in place? Here is the code, simplified for this post:
import UIKit
import MapKit
class MapViewController: UIViewController, CLLocationManagerDelegate,
MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var slider: UISlider!
#IBOutlet weak var distanceLabel: UILabel!
#IBOutlet weak var searchButton: UIButton!
#IBOutlet weak var searchView: UIView!
#IBOutlet weak var resultView: UIView!
#IBOutlet weak var resultNameLabel: UILabel!
#IBOutlet weak var resultDistanceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
self.resultView.isHidden = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func sliderAdjusted(_ sender: Any) {
let int = Int(slider.value)
switch int {
case 1:
distanceLabel.text = "1 mile"
default:
distanceLabel.text = "\(int) miles"
}
}
#IBAction func searchButtonPressed(_ sender: Any) {
/*This is where, after calling a search function which is omitted
from this example, I would like to make the resultsView not
hidden and then animate it to sort of eclipse the search view*/
self.resultView.isHidden = false
self.resultView.animate(withDuration: NSTimeInterval)
/*The above line of code is not actually appearing for me.
After typing "self.resultView." the animate function is not being
given to me as an option for my UIView*/
}
}
I will also attach some images of the view controller so you can sort of get the idea. The results view is not visible in this image because its top is constrained to the superview's bottom, thus it is just out of the visible representation of its superview.
The first image is the view controller with the searchView highlighted. This is the view that I would like to be eclipsed by my resultView after the searchButton is pressed.
The second image is the same view controller with the resultView highlighted. As you can see, its top is constrained to be equal to the superview's bottom. This is the view that I would like to animate upwards into the superview and eclipse the searchView after the search button is pressed.
The methods for all the animate family are all class methods. Which means you call them on the class object not an instance.
You are trying to call
class func animate(withDuration: TimeInterval, animations: () -> Void)
so your code needs to look like
UIView.animate(withDuration: 0.5) {
//the things you want to animate
//will animate with 0.5 seconds duration
}
In the particular case it looks like you are trying to animate the height of resultView so you need an IBOutlet to that constraint. You could call it resultViewHeight.
UIView.animate(withDuration: 0.5) {
self.resultViewHeight.constant = theDesiredHeight
self.layoutIfNeeded()
}
Calling layoutIfNeeded() within the closure is the secret sauce to animating auto layout. Without that the animation will just jump to the and point.
Related
This is the image on the scroll view where I'm able create those rectangles from superview
This is the image I want how to look my output when I touch to began creating multiple rectangles
I'm trying to create multiple annotations on images using views where it's been achieved. And for the next step trying to zoom in through image view to annotate where it's possible with pinch gesture but the constraint is not able scroll left or right while zoomed in. I have to zoom out and then again have to zoom in to the concentrated area or co-ordinate to annotate again. I gone through few examples and found to be possible of moving or scrolling left or right while zoomed in using scroll view, where I have achieved it. But not able to annotate the image by creating views. When I tried to create it's not happening still accidently I srolled it from superview where it has created the view. Thank you in adavnce.
Class ViewController : UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate{
// Outlets
#IBOutlet weak var displayImageView : UIImageView!
#IBOutlet weak var imageScrolling : UIScrollView!
#IBOutlet weak var containerView : UIView!
// View Did Load
override func viewDidLoad() {
super.viewDidLoad()
displayImageView.isMultipleTouchEnabled [](https://i.stack.imgur.com/ba2n7.png)= false
displayImageView.isUserInteractionEnabled = true
imageScrolling.minimumZoomScale = 1.0
imageScrolling.maximumZoomScale = 4.0
imageScrolling.delegate = self
imageScrolling.contentSize = displayImageView.bounds.size
imageScrolling.isUserInteractionEnabled = true
containerView.isUserInteractionEnabled = true
imageScrolling.isExclusiveTouch = true
imageScrolling.panGestureRecognizer.isEnabled = true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
// View For Zooming - Scroll View Image Zooming and Scrolling
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return displayImageView
}
}
I have also added the screenshot of how it's happening while touch began from superview and how i wanted it to be.
I have also added views hierarchy
I am trying to learn the basics, but still very new to Xcode and Swift. I want to use a slider to transition between two images. When the slider is all the way to the left, image 1 should show in the image view. As the slider moves to the right, image 1 should fade and an image 2 should come into view. At the halfway point, both images should be visible (alpha = 0.5 for both image), superimposed one on top of the other. As the slider goes all the way to the right, image 1 should vanish and image 2 should be the only one visible.
Using UIImageView and a slider, I am able to adjust the alpha and "fade" my image based on the position of the slider.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var UAimage: UIImageView!
#IBOutlet weak var Slider: UISlider!
#IBAction func sliderValueChange(_ sender: UISlider) {
var currentValue = Float(sender.value)
UAimage.alpha = CGFloat(sender.value)
}
}
At this point, I am looking for help getting the second image added and using alpha to programmatically control its opacity.
Just add a 2nd imageview over the first and add this line
imageview2.alpha = CGFloat(1 - sender.value)
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var UAimage: UIImageView!
/*
Your 2nd image.
On storyboard, go place a UIImageView directly on top of your initial imageView and set the image to whatever you'd like just as you did the original.
*/
#IBOutlet weak var UAimage2: UIImageView! //Your 2nd image
#IBOutlet weak var Slider: UISlider!
#IBAction func sliderValueChange(_ sender: UISlider) {
var currentValue = Float(sender.value)
UAimage.alpha = CGFloat(sender.value)
UAimag2.alpha = 1 - CGFloat(sender.value)
}
}
I'm kinda new to Swift coding and I wanted to train myself by finding some layouts and try to implement them using AutoLayout.
Here is a screen shot of a screen I try to implement
https://i.imgur.com/9yzvnzp.png
The behaviour is wanted here is that on scrolling the picture should fade away and the title should be set like so
https://i.imgur.com/bLhyTGs.png
Could anybody help me on how am I supposed to do that ?
Should I use a ScrollView ? UITableView ? UICollectionView ?
I'm actually working on an application that does something similar.
So, the first thing you want to do is create a UIView for the background, i.e. the blue part that shows Squirtle. This view can take up the entire view controller. Connect it to your UIViewController via IBOutlet and call it backgroundView.
Next, we want to lay a UIScrollView over backgroundView. This scroll view should be constrained to the top, bottom, leading, and trailing edges of the superview so that it covers the entire view controller's frame. Connect this UIScrollView to your UIViewController via IBOutlet and name it scrollView. Also, make sure that scrollView's backgroundColor is set to clear. This way, we'll be able to see backgroundView underneath our scrollView.
Within your UIViewController, you'll want to set scrollView's delegate to self inside of viewDidLoad. Your UIViewController's code should now look something like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
#IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
We'll set delegate methods for scrollView later. These will help us know when the scroll view has begun scrolling to the top of our SquirtleViewController.
Now, our scrollView has a clear background, and doesn't actually display anything to our user. This is how we want it for now. We want the top of our scrollView to be clear so that we can see backgroundView behind it, and the bottom of our scrollView to have another view, i.e. the "content view" that shows STATS, EVOLUTIONS, MOVES, HP, etc.
So, let's add another UIView as a subview of our scrollView. Connect it to SquirtleViewController via IBOutlet and name it contentView.
Now, we need to create a constraint between contentView's top edge and scrollView's top edge. This constraint's constant needs to be equal to the height of backgroundView's content. This way, our contentView won't cover up what we want to see from backgroundView. We should also save this height as backgroundViewContentHeight so that we can reference it later.
We also need contentView's leading, trailing, and bottom constraints to be equal to those of its superview, i.e. scrollView. These do not need to be connected via IBOutlets.
Also, give contentView a height and width constraint and connect these as contentViewHeight and contentViewWidth respectively to SquirtleViewController via IBOutlet. This will help us set the contentSize of our scrollView later.
Your code should now look something like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//New lines of code
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
Now, when SquirtleViewController lays out its subviews, we are going to want to set the constant properties of contentViewHeight and contentViewWidth. This will set a contentSize for our scrollView. We want our contentViewWidth to simply be the size of our SquirtleViewController's view's width. Our contentViewHeight's constant will be a little different though, as it depends on the height of our contentView's subviews. For this example, we'll say the contentViewHeight's constant should be 1200. The code should now look like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
//New lines of code
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentViewWidth.constant = view.frame.width
contentViewHeight.constant = 1200
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
From here, you can add subviews to backgroundView and contentView as you see fit in order to show the pokemon (backgroundView) as well as the pokemon's stats (contentView). But, you still need to know how to change the content of your backgroundView depending upon how much we have scrolled the scrollView. This is where the UIScrollViewDelegate helps us out.
There is a method in UIScrollViewDelegate that is called every time our scrollView's contentOffset.y changes. This basically means every time we change the amount that our scrollView has scrolled, this method will be called.
Inside of this method, we can cross reference the amount we've scrolled with the height of the background view. As our scrollView's contentOffset.y approaches the height of our backgroundView's content, we can fade out the image of Squirtle and fade in a UILabel that simply says "Squirtle" (like in your example).
So, in your case, I would suggest adding a UIImage of Squirtle as a subview of contentView and connect it via IBOutlet as pokemonImageView. pokemonImageView should have its frame partially outside of the contentView (like in your example). If you do this, ensure that contentView's clipsToBounds property is set to false.
I would also add a UILabel as a subview of backgroundView and connect it to SquirtleViewController via IBOutlet as pokemonNameLabel. When our SquirtleViewController's view loads, we should set pokemonNameLabel.alpha equal to zero so that it is initially hidden.
Your code should now look like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
//New line of code
#IBOutlet var pokemonNameLabel: UILabel!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//New line of code
#IBOutlet var pokemonImageView: UIImageView!
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
//New line of code
pokemonNameLabel.alpha = 0
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentViewWidth.constant = view.frame.width
contentViewHeight.constant = 1200
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
Now we simply need to add the scrollViewDidScroll method inside of our class extension. This is the method that will tell us our scrollView's contentOffset.y property so that we know how much we've scrolled. As this number increases, our pokemonImageView's alpha should decrease to zero and our pokemonNameLabel's alpha should increase towards one.
Inside of this method, I would divide our scrollView.contentOffset.y by the height of our backgroundView minus the height of the pokemonNameLabel. We can then use this decimal to set our respective alphas of pokemonImageView and pokemonNameLabel.
Your code should now look something like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
//New line of code
#IBOutlet var pokemonNameLabel: UILabel!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//New line of code
#IBOutlet var pokemonImageView: UIImageView!
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
//New line of code
pokemonNameLabel.alpha = 0
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentViewWidth.constant = view.frame.width
contentViewHeight.constant = 1200
}
}
extension SquirtleViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
//Subtracting contentView.frame.height from scrollView.frame.height
let alphaDecimal = scrollView.contentOffset.y / (backgroundViewContentHeight - pokemonNameLabel.frame.height)
pokemonNameLabel.alpha = alphaDecimal
pokemonImageView.alpha = 1 - alphaDecimal
}
}
Now, as we scroll up and what can be seen of the backgroundView shrinks, our pokemonImageView will fade out and our pokemonNameLabel will fade in. There will be a point in-between where both will have an alpha of 0.5. You can mess with this scrollViewDidScroll method as you see fit to make this work best for you.
This question already has answers here:
Load view from XIB as a subview of a scrollview
(2 answers)
Closed 3 years ago.
Problem
I am trying to add a custom view from a xib into a UIScrollView.
I added a UIScrollView to an empty ViewController in my storyboard. I also added 4 constraints for leading, trailing, top, bottom to Safe Area and set them all Equal to 0.
Now in the code I want to add a custom view "EquipmentInfoView" to the existed UIScrollView
class EquipmentInfoVC: UIViewController {
#IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
equipmentInfoView = EquipmentInfoView.instantiate()
scrollView.addSubview(equipmentInfoView)
}
}
And it does not work as the ScrollView does not scroll. How should I do it instead?
Here is the custom view "EquipmentInfoView"
class EquipmentInfoView: UIView {
#IBOutlet var nameTextView: UITextView!
#IBOutlet var descriptionTextView: UITextView!
#IBOutlet var equipmentImageView: UIImageView!
static func instantiate() -> EquipmentInfoView {
let view: EquipmentInfoView = initFromNib()
return view
}
}
Extra Notes
I know this is not the normal way to create a custom view from xib. However, this way I dont have to set the File's Owner to "EquipmentInfoView", instead I set the View's Custom Class to "EquipmentInfoView".
(Setting File's Owner to "EquipmentInfoView" gives me EXC_BAD_ACCESS when I do unit testing)
For clarity, this is how I would add this view to a normal (non-scroll) view:
equipmentInfoView = EquipmentInfoView.instantiate()
mainView.addSubview(equipmentInfoView)
equipmentInfoView.frame = mainView.bounds
equipmentInfoView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
You are probably missing the contentSize of your scrollView
class EquipmentInfoVC: UIViewController {
#IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
equipmentInfoView = EquipmentInfoView.instantiate()
scrollView.contentSize = equipmentInfoView.frame.size
scrollView.addSubview(equipmentInfoView)
}
}
demo image
When I update a number to label will make the view get back to original position.
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBOutlet var aview: UIView!
var number = 0
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func plus(sender: UIButton) {
number++
label.text = "number:\(number)"
}
#IBAction func move(sender: AnyObject) {
aview.frame.origin.y -= 20
}
}
I couldn't find the answer on web, please help me to fix this problem.Thank you very much!
Because your xib or Storyboard you set use Autolayout:
So if you don't set constrain system will auto generate it. When you change frame by set frame it effect but when you access to it. It will auto back to old position.
if you don't want it happen. You set in viewDidLoad:
self.view.translatesAutoresizingMaskIntoConstraints = false
Issue is probably related to a constraint reloading when the label reloads.
Instead of setting aview.frame.origin.y -= 20 you should make an outlet to the constraint holding the y position of your aView and then update the constant of that constraint outlet instead.