Change height and width of view according to scrollview scroll horizontally using auto layout - swift

Container View hierarchy:-
Container View
Scrollview
view left
view center
view right
FloatingBottomView
I need to change height and width constrain of FloatingBottomView according to scrollview scroll horizontally.
Initial Outlet:-
#IBOutlet weak var constantFloatingBottomViewWidth: NSLayoutConstraint! // 300
#IBOutlet weak var constantFloatingBottomViewHeight: NSLayoutConstraint! // 70
override func viewWillAppear(_ animated: Bool) {
self.scrollView.contentOffset.x = self.view.bounds.width
// view center in scrollview
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.x < self.view.bounds.width {
// when scrollview move view center to view left, constantFloatingBottomViewWidth and height goes to down at some point
}
else if scrollView.contentOffset.x > self.view.bounds.width {
// when scrollview move view left to view center, constantFloatingBottomViewWidth & height goes up to same point range while it down and back to original constantFloatingBottomViewWidth and height
}
}
i have tried using this way to get some scale in scrollViewDidScroll method.
scroll quick left to right or vice-versa did not get exactly out-put
let scale = abs(scrollView.contentOffset.x / self.view.bounds.width)
print("scale:=\(scale)")

I think you need to give button animation like this
For this you have nothing to do just add animation to button like follow
call setupButtonanimation in viewDidLoad
func setupButtonAnimation()
{
let animation = CABasicAnimation.init(keyPath: "transform.scale")
animation.fromValue = 1.0
animation.toValue = 0.45
animation.duration = 1.0
//Set the speed of the layer to 0 so it doesn't animate until we tell it to
self.btn1.layer.speed = 0.0;
self.btn1.layer.add(animation, forKey: "transform");
}
func scrollViewDidScroll(_ scrollView: UIScrollView)
{
let screenSize = self.view.bounds.size
if let btn = self.btn1
{
var factor:CGFloat = 1.0
factor = scrollView.contentOffset.x / screenSize.width
if factor > 1
{
factor = 2 - factor
}
print(factor)
//This will change the size
let timeOffset = CFTimeInterval(1-factor)
btn.layer.timeOffset = timeOffset
}
}

Related

Updating variable in view constraint (SnapKit)

View is initialized with following constraints
View.snp.makeConstraints { (para) in
View.topConstraint = para.top.equalTo(parentview.snp.top).constraint
View.LeadingConstraint = para.leading.equalTo(parentview.snp.leading).constraint
View.TrailingConstraint = para.trailing.equalTo(parentview.snp.trailing).constraint
View.BottomConstraint =para.bottom.equalTo(parentview.snp.bottom).offset(-getheight).constraint
}
where getheight = parentview.frame.size.height/2 ;
when parentview changes its dimensions.View doesnt update its height as constraints are not called again.
any way to update or recall its constraints other the remakingConstraint which is not feasible at large scale.
Have tried:
View.updateConstraints()
View.setNeedsUpdateConstraints()
View.setNeedsLayout()
I need reference to each constraints because
if View.bottomTouch {
View.bottomConstraint.update(offset: View. BottomConstraint.layoutConstraints[0].constant + CurrentPoint - PreviousPoint)
}
Is there a reason you don't want to use 50% of the parent view height?
View.snp.makeConstraints { (para) in
para.top.equalTo(parentview.snp.top)
para.leading.equalTo(parentview.snp.leading)
para.trailing.equalTo(parentview.snp.trailing)
// 50% of the parent view height
para.height.equalTo(parentview.snp.height).multipliedBy(0.5)
// instead of this
//para.bottom.equalTo(parentview.snp.bottom).offset(-getheight)
}
Edit - after comments...
Keeping a reference to a constraint for the purposes of dragging a view is a very different question from "Keep the child view at 50% of the height of the parent view."
Give this a try...
It will create a cyan "parentView" with a blue "childView" (subview). Dragging the blue view (Pan Gesture) will drag its bottom up / down. Tapping anywhere (Tap Gesture) will toggle the insets on the frame of the parentView between 20 and 60.
When the parentView frame changes - either from the tap or, for example, on device rotation - the "childView" bottom will be reset to 50% of the height of the "parentView":
class ViewController: UIViewController {
let parentView = UIView()
let childView = UIView()
// childView bottom constraint
var bc: Constraint!
override func viewDidLoad() {
super.viewDidLoad()
parentView.backgroundColor = .cyan
childView.backgroundColor = .blue
parentView.addSubview(childView)
view.addSubview(parentView)
parentView.snp.makeConstraints { para in
para.top.leading.trailing.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20.0)
}
// childView's bottom constraint offset will be set in viewDidLayoutSubviews()
childView.snp.makeConstraints { para in
para.top.leading.trailing.equalToSuperview()
bc = para.bottom.equalToSuperview().constraint
}
let p = UIPanGestureRecognizer(target: self, action: #selector(panHandler(_:)))
childView.addGestureRecognizer(p)
let t = UITapGestureRecognizer(target: self, action: #selector(tapHandler(_:)))
view.addGestureRecognizer(t)
}
#objc func tapHandler(_ g: UITapGestureRecognizer) -> Void {
// on tap, toggle parentView inset
// between 20 and 60
// this will trigger viewDidLayoutSubviews(), where the childView bottom
// constraint will be reset to 50% of the parentView height
var i: CGFloat = 60.0
if parentView.frame.origin.x > 20 {
i = 20.0
}
parentView.snp.updateConstraints { para in
para.top.leading.trailing.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(i)
}
}
#objc func panHandler(_ g: UIPanGestureRecognizer) -> Void {
let translation = g.translation(in: g.view)
// update bottom constraint constant
bc.layoutConstraints[0].constant += translation.y
// reset gesture translation
g.setTranslation(CGPoint.zero, in: self.view)
}
var parentViewHeight: CGFloat = 0.0
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// reset childView's bottom constraint
// to 50% of its superView's height
// ONLY if parentView frame height has changed
if parentView.frame.height != parentViewHeight {
parentViewHeight = parentView.frame.height
bc.layoutConstraints[0].constant = -parentViewHeight * 0.5
}
}
}
Firstly, check whether the getheight value did update when the parent view layout change. In order to reload the existing constraints, you may need to call layoutIfNeeded() of your parent view.

Is there a way to auto layout an image in a scroll view combined with page control?

This is my first entry. I am new in app designing and from Germany, but I still hope you can understand my problem. I used Xcode 11 and Swift 5.
I am using a page control and scroll view to switch between images in one screen. It looks good on the iPhone 11 but on the iPhone 8 the width and height of the images is too great, which is why part of the first image can still be seen when the page control is on the second segment. The same happens for the with the second image and the third segment.This hopefully shows the problem.
Is there a way to fit the images to the screen size?
This is my code:
```
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageControl: UIPageControl!
var Pages: [String] = ["Page1","Page2","Page3"]
var frame = CGRect.zero
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
pageControl.numberOfPages = Pages.count //
setupScreens()
scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false
scrollView.isPagingEnabled = true
scrollView.delegate = self
}
func setupScreens() {
for index in 0..<Pages.count {
// 1.
frame.origin.x = scrollView.frame.size.width * CGFloat(index)
frame.size = scrollView.frame.size
// 2.
let imageView = UIImageView(frame: frame)
imageView.image = UIImage(named: Pages[index])
self.scrollView.addSubview(imageView) }
// 3.
scrollView.contentSize = CGSize(width: (scrollView.frame.size.width * CGFloat(Pages.count)), height: scrollView.frame.size.height)
scrollView.delegate = self
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
pageControl.currentPage = Int(pageNumber)
}
}
```
Your code is totally working fine except calling setupScreens() method in viewDidLoad().
call that method in viewDidLayoutSubviews so that it will get proper frame of scrollView.
override func viewDidLayoutSubviews() {
setupScreens()
}
Download demo from here

Image Slider using UIScrollView and Auto Layout

I am trying to implement a image slider using scrollView and pageControl, with the images being appended to the scrollView programmatically using the .addSubView method. The code is as follows:
#IBOutlet weak var sliderScrollView: UIScrollView!
#IBOutlet weak var sliderPageControl: UIPageControl!
var images: [String] = ["0", "1", "2"]
func updateSlider() {
sliderPageControl.numberOfPages = images.count
for index in 0..<images.count {
frame.origin.x = sliderScrollView.frame.size.width * CGFloat(index)
print(sliderScrollView.frame.size.width)
frame.size = sliderScrollView.frame.size
let image = UIImageView(frame: frame)
image.contentMode = .scaleAspectFill
image.image = UIImage(named: cafeObject.images[index])
sliderScrollView.addSubview(image)
}
sliderScrollView.contentSize = CGSize(width: sliderScrollView.frame.size.width * CGFloat(cafeObject.images.count), height: sliderScrollView.frame.size.height)
sliderScrollView.delegate = self
sliderPageControl.currentPage = 0
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
switch scrollView {
case sliderScrollView:
sliderPageControl.currentPage = Int(pageNumber)
default:
break
}
}
As I designed the storyboard on iPhone 8 layout, the above code works nicely for iPhone 8. However, once I run the code in iPhone 8 Plus, the photo does not adapt the new size of the scrollView. I have added constraints to the scrollView such that the top, leading, trailing and bottom are equal to the super view's top, leading, trailing and bottom. When debugging, I realized that the UIImageView's frame is still using the old width as on iPhone 8.
Any workaround for this either programatically or using interface builder? Thanks!
Okay I managed to get this fixed by placing the updateSlider() function under viewDidLayoutSubviews() instead!

Rotate UIImageView inside UIScrollView in Swift

I'm working in a basic photo editor which is supposed to zoom, rotate and flip a photo. I'm using an image view (aspect fill) inside a scroll view which allows me to zoom easily. But when I try to rotate or flip the result is not what I would expect. The image view keeps the original frame and seems like rotating the image. The scroll view zoom scale changes. Any suggestions on how to do this?
It also would be great to have suggestions about setting the image view anchor point to match the scroll view anchor point before transforming cause I don't want to display a different portion of the image after transforming, just the same portion of the image, but rotated.
View stack before transform:
View stack after applying rotation:
My code so far:
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
setZoomScale()
scrollView.zoomScale = scrollView.minimumZoomScale
}
#IBAction func rotateAnticlockwise(_ sender: UIButton) {
rotationAngle -= 0.5
transformImage()
}
func transformImage(){
var transform = CGAffineTransform.identity
transform = transform.rotated(by: .pi * rotationAngle)
imageView.transform = transform
}
func setZoomScale(){
let imageSize = imageView.image!.size
let smallestDimension = min(imageSize.width, imageSize.height)
scrollView.minimumZoomScale = scrollView.bounds.width / smallestDimension
scrollView.maximumZoomScale = smallestDimension / scrollView.bounds.width
}
I think you are looking for, e.g. :
imageView.transform = CGAffineTransform(rotationAngle: 0.5)

Swift - Visual constraints not working

I have a scrollView which I instantiate programaticaly. What I want to do is to add constraints so it will look good in both horizontal and vertical orientation. The problem that constraints not working
How it looks vertically - good
How it looks horizontally - not good
Code is following
class FAPhoto: UIViewController, UIScrollViewDelegate {
var imageURLsArray = [String]()
var imageViews:[UIImageView] = []
var arrayOfPhotos = [Photo]()
var scrollView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
scrollView = UIScrollView(frame: self.view.frame)
view.addSubview(scrollView)
scrollView.contentSize = CGSizeMake(view.frame.size.width * CGFloat(arrayOfPhotos.count), scrollView.frame.size.height)
scrollView.pagingEnabled = true
for (var i = 0; i < arrayOfPhotos.count; i++) {
var imageView = UIImageView()
imageView.frame = CGRectMake(CGFloat(i) * view.frame.size.width, scrollView.frame.origin.y, scrollView.frame.size.width, scrollView.frame.size.height)
imageView.contentMode = .ScaleAspectFit
let image = imageView.hnk_setImageFromURL(NSURL(string: arrayOfPhotos[i].url!)!)
scrollView.addSubview(imageView)
}
}
override func viewWillAppear(animated: Bool) {
}
override func viewWillLayoutSubviews() {
let bindings = Dictionary(dictionaryLiteral: ("scrollView", self.scrollView))
let horizontalConstraints =
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-0-[scrollView]-0-|",
options: [],
metrics: nil,
views: bindings)
self.view.addConstraints(horizontalConstraints)
let verticalConstraints =
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|-0-[scrollView]-0-|",
options: [],
metrics: nil,
views: bindings)
self.view.addConstraints(verticalConstraints)
}
}
The problem is that you set contentSize of scrollView and imageView's frames in viewDidLoad. This is OK for portrait mode, but when view rotates to landscape, it's frame also changes, so you have to update these values accordingly. I think you have 3 opportunities here.
Track rotation events and update contentSize of scrollView and imageView's frames, when view rotates.
Use Autolayout for scrollView. You will not have to calculate contentSize or set imageView's frames, just set constraints once in viewDidLoad. Here you can find some examples https://stackoverflow.com/a/20232911/4757335.
Use UICollectionView instead of UIScrollView. It handles rotation much easier.