Scroll view paging content size doesn't work with iphone 5s ios 12 - swift

I have an horizontal paging scroll view with 3 images.
This is the code in my viewDidLoad:
for index in 0..<images.count {
frame.origin.x = scrollView.frame.size.width * CGFloat(index)
frame.size = scrollView.frame.size
let UrlImage = NSURL(string: images[index] as! String)
let dataImage = NSData(contentsOf: UrlImage! as URL)
if dataImage != nil {
let imgView = UIImageView(frame: frame)
imgView.image = UIImage(data: dataImage! as Data)
imgView.contentMode = .scaleAspectFit
self.scrollView.backgroundColor = .lightGray
self.scrollView.addSubview(imgView)
}
}
scrollView.contentSize = CGSize(width: (scrollView.frame.size.width * CGFloat(images.count)), height: scrollView.frame.size.height)
scrollView.delegate = self
and this is the result:
If i try with last iphone devices it works properly. But if i try with iphone 5s with ios12, content size doesn't work and this is the result:
How can i fix this? I'v been trying for 3 days

I'm assuming you have code to handle the page control and scroll delegate(s)...
Here's a very simple example of using a UIStackView to hold the 3 image views:
class ViewController: UIViewController {
let scrollView = UIScrollView()
let stack = UIStackView()
let pageControl = UIPageControl()
override func viewDidLoad() {
super.viewDidLoad()
// scroll view properties
scrollView.backgroundColor = .gray
scrollView.isPagingEnabled = true
// stack view properties
stack.axis = .horizontal
stack.alignment = .fill
stack.distribution = .fillEqually
stack.spacing = 0
// page control properties
pageControl.backgroundColor = .white
pageControl.pageIndicatorTintColor = .darkGray
pageControl.currentPageIndicatorTintColor = .blue
// we're going to use constraints
scrollView.translatesAutoresizingMaskIntoConstraints = false
stack.translatesAutoresizingMaskIntoConstraints = false
pageControl.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(stack)
view.addSubview(scrollView)
view.addSubview(pageControl)
// respect safe area
let g = view.layoutMarginsGuide
NSLayoutConstraint.activate([
// scroll view constrained Top / Leading / Trailing
scrollView.topAnchor.constraint(equalTo: g.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
// scroll view height = 1/2 of view height
scrollView.heightAnchor.constraint(equalTo: g.heightAnchor, multiplier: 0.5),
// stack view constrained Top / Bottom / Leading / Trailing of scroll view
stack.topAnchor.constraint(equalTo: scrollView.topAnchor),
stack.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
stack.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
stack.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
// stack view height == scroll view height
stack.heightAnchor.constraint(equalTo: scrollView.heightAnchor),
// page control centered horizontally just below the scroll view
pageControl.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 4.0),
pageControl.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
])
// add three image views to the stack view
for _ in 1...3 {
let v = UIImageView()
v.backgroundColor = .lightGray
v.contentMode = .scaleAspectFit
stack.addArrangedSubview(v)
}
// stack distribution is set to .fillEqually, so we only need to set the
// width constraint on the first image view
// unwrap it
if let v = stack.arrangedSubviews.first {
// stack view has 8-pts on each side
v.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
}
pageControl.numberOfPages = 3
// set delegates...
// implement page control funcs...
// etc...
// start image retrieval process
downloadImages()
}
func downloadImages() -> Void {
// get 3 "placeholder" images
// 400x300 400x400 300x400
let urls: [String] = [
"https://via.placeholder.com/400x300.png/FF0000/FFF",
"https://via.placeholder.com/400x400.png/00AA00/FFF",
"https://via.placeholder.com/300x400.png/0000FF/FFF",
]
for (v, urlString) in zip(stack.arrangedSubviews, urls) {
if let imgView = v as? UIImageView, let url = URL(string: urlString) {
imgView.load(url: url)
}
}
}
}
// async download image url from: https://www.hackingwithswift.com/example-code/uikit/how-to-load-a-remote-image-url-into-uiimageview
extension UIImageView {
func load(url: URL) {
DispatchQueue.global().async { [weak self] in
if let data = try? Data(contentsOf: url) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self?.image = image
}
}
}
}
}
}

Related

stack programmatically, giving space for bottom

i am trying to place the image below the text i add
class SolicitudViewController: BaseViewController {
lazy var imagePrincipal : UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "diseƱo")
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
//imageView.heightAnchor.constraint(equalToConstant: 50).isActive = true
//imageView.bottomAnchor.constraint(equalToConstant: 100).isActive = true
return imageView
}()
lazy var stackView : UIStackView = {
let stack = UIStackView()
stack.axis = .vertical
stack.distribution = .fill
stack.translatesAutoresizingMaskIntoConstraints = false
stack.addArrangedSubview(imagePrincipal)
//stack.addArrangedSubview(lblsubTitulo)
//stack.addArrangedSubview(lineView)
stack.addArrangedSubview(imageEvaluando2)
stack.addArrangedSubview(imageEvaluando3)
return stack
}()
lazy var scrollView : UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(stackView)
return scrollView
}()
override func viewDidLoad() {
super.viewDidLoad()
setSubtitle(subtitle: "test View")
}
I have tried to use this: stack.setCustomSpacing(30, after: imagePrincipal) but it positions the image on top, I want the image to be below the text

GPUImagePoissonBlendFilter produces weird result

I am trying to use GPUImage (Objective-C) pod in order to blend two images with GPUImagePoissonBlendFilter in Swift project.
The result is kinda weird and I don't understand how to fix it.
Sample project:
Back image
Front image
Result image
What is going on in code:
Load Back image into backImage
Load Front image into frontImage
Blend images with GPUImagePoissonBlendFilter
Result put to imageView
class ViewController: UIViewController {
private let imageView: UIImageView = {
let image = UIImage(named: "back")
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.borderWidth = 1
imageView.layer.borderColor = UIColor.green.cgColor
return imageView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.black
view.addSubview(imageView)
NSLayoutConstraint.activate([
imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10),
imageView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor)
])
guard let backImage = imageView.image else { return }
guard let frontImage = UIImage(named: "front") else { return }
let backImageSource = GPUImagePicture(image: backImage)
let frontImageSource = GPUImagePicture(image: frontImage)
let poisionFilter = GPUImagePoissonBlendFilter()
poisionFilter.mix = 0.9
poisionFilter.numIterations = 100
backImageSource?.addTarget(poisionFilter, atTextureLocation: 0)
frontImageSource?.addTarget(poisionFilter)
backImageSource?.processImage()
frontImageSource?.processImage()
poisionFilter.useNextFrameForImageCapture()
let filterResult = poisionFilter.imageFromCurrentFramebuffer()
imageView.image = filterResult
}}
Can anyone point what I am doing wrong here ?
Thanks in advance !

NSLayoutConstraints not cooperating with UIImageView

In my UIView subclass, I have one image view and three labels:
let imageView = UIImageView()
let firstLabel = UILabel()
let secondLabel = UILabel()
let thirdLabel = UILabel()
The image and texts are set by the view controller that uses the view.
I begin to set them up with:
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
addSubview(imageView)
firstLabel.translatesAutoresizingMaskIntoConstraints = false
firstLabel.textAlignment = .center
addSubview(firstLabel)
secondLabel.translatesAutoresizingMaskIntoConstraints = false
secondLabel.textAlignment = .center
addSubview(secondLabel)
thirdLabel.translatesAutoresizingMaskIntoConstraints = false
thirdLabel.textAlignment = .center
addSubview(thirdLabel)
I am trying to constrain these in such a way such that it looks like the following (rough drawing):
Specifically:
thirdLabel is in the center at the bottom
secondLabel is in the center directly above thirdLabel
firstLabel is in the center directly above secondLabel
The size of imageView will vary depending on the size of the view, however it must meet these criteria:
It is in the center directly above firstLabel
It reaches the top
It is a square
So if the height of the view was larger, only the image view would enlarge, the labels would NOT increase height and evenly space out. They would remain at the bottom. So visually, this would be good:
and this would be bad:
An example of what I've tried (one of MANY):
NSLayoutConstraint.activate([
thirdLabel.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor),
thirdLabel.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor),
thirdLabel.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor),
thirdLabel.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor),
secondLabel.centerXAnchor.constraint(equalTo: thirdLabel.centerXAnchor),
secondLabel.bottomAnchor.constraint(equalTo: thirdLabel.topAnchor),
secondLabel.leadingAnchor.constraint(equalTo: thirdLabel.leadingAnchor),
secondLabel.trailingAnchor.constraint(equalTo: thirdLabel.trailingAnchor),
firstLabel.centerXAnchor.constraint(equalTo: secondLabel.centerXAnchor),
firstLabel.bottomAnchor.constraint(equalTo: secondLabel.topAnchor),
firstLabel.leadingAnchor.constraint(equalTo: secondLabel.leadingAnchor),
firstLabel.trailingAnchor.constraint(equalTo: secondLabel.trailingAnchor),
imageView.centerXAnchor.constraint(equalTo: firstLabel.centerXAnchor),
imageView.bottomAnchor.constraint(equalTo: firstLabel.topAnchor),
imageView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor),
])
I've mixed and matched so many constraints but I cannot achieve the layout in the first image. Not only can I get it working with various heights, I can't even get it to work with ANY height. Sometimes the image view takes up the whole thing and I can't even see the labels (are they underneath the view? behind the image view?). Sometimes the height of the labels are increased. These things occur even though I have constraints that seemingly don't allow this to happen? No breaking of constraint messages appear in the console either.
I believe it may have something to do with sizing, because if I don't set an image (and set a background color for imageView so I can see where it is), it works perfectly. It's only when I actually assign an image to imageView.image do things act up. I've tried resizing the image beforehand, along with setting many variables and constraints not shown in the particular example given above.
Frustrating!
You need to set both Content Compression Resistance and Content Hugging priorities on your labels.
Here is an example custom view class (using mostly your code):
class AJPView: UIView {
let imageView = UIImageView()
let firstLabel = UILabel()
let secondLabel = UILabel()
let thirdLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
addSubview(imageView)
firstLabel.translatesAutoresizingMaskIntoConstraints = false
firstLabel.textAlignment = .center
addSubview(firstLabel)
secondLabel.translatesAutoresizingMaskIntoConstraints = false
secondLabel.textAlignment = .center
addSubview(secondLabel)
thirdLabel.translatesAutoresizingMaskIntoConstraints = false
thirdLabel.textAlignment = .center
addSubview(thirdLabel)
NSLayoutConstraint.activate([
thirdLabel.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor),
thirdLabel.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor),
thirdLabel.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor),
secondLabel.bottomAnchor.constraint(equalTo: thirdLabel.topAnchor),
secondLabel.leadingAnchor.constraint(equalTo: thirdLabel.leadingAnchor),
secondLabel.trailingAnchor.constraint(equalTo: thirdLabel.trailingAnchor),
firstLabel.bottomAnchor.constraint(equalTo: secondLabel.topAnchor),
firstLabel.leadingAnchor.constraint(equalTo: secondLabel.leadingAnchor),
firstLabel.trailingAnchor.constraint(equalTo: secondLabel.trailingAnchor),
imageView.centerXAnchor.constraint(equalTo: firstLabel.centerXAnchor),
imageView.bottomAnchor.constraint(equalTo: firstLabel.topAnchor),
imageView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor),
// you've given the labels leading and trailing constraints,
// so you don't need these
//thirdLabel.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor),
//secondLabel.centerXAnchor.constraint(equalTo: thirdLabel.centerXAnchor),
//firstLabel.centerXAnchor.constraint(equalTo: secondLabel.centerXAnchor),
])
// prevent labels from being compressed or stretched vertically
[firstLabel, secondLabel, thirdLabel].forEach {
$0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.setContentHuggingPriority(.required, for: .vertical)
}
// let's give the subviews background colors
// so we can easily see the frames
let clrs: [UIColor] = [
.systemYellow,
.green,
.cyan,
.yellow
]
for (v, c) in zip([imageView, firstLabel, secondLabel, thirdLabel], clrs) {
v.backgroundColor = c
}
}
}
and a demo view controller:
class ViewController: UIViewController {
var heightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
let testView = AJPView()
testView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(testView)
let g = view.safeAreaLayoutGuide
heightConstraint = testView.heightAnchor.constraint(equalToConstant: 120.0)
NSLayoutConstraint.activate([
testView.widthAnchor.constraint(equalToConstant: 300.0),
testView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
testView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
// activate height anchor
heightConstraint,
])
testView.firstLabel.text = "First"
testView.secondLabel.text = "Second"
testView.thirdLabel.text = "Third"
if let img = UIImage(named: "myImage") {
testView.imageView.image = img
} else {
if let img = UIImage(systemName: "person.circle.fill") {
testView.imageView.image = img
}
}
// so we can see the frame of the view
testView.layer.borderWidth = 1
testView.layer.borderColor = UIColor.red.cgColor
// add grow / shrink buttons
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.spacing = 20
stack.distribution = .fillEqually
["Taller", "Shorter"].forEach {
let b = UIButton(type: .system)
b.backgroundColor = .yellow
b.setTitle($0, for: [])
b.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)
stack.addArrangedSubview(b)
}
view.addSubview(stack)
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
stack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
stack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
])
}
#objc func btnTapped(_ sender: UIButton) -> Void {
var h = heightConstraint.constant
if sender.currentTitle == "Taller" {
h += 10
} else {
h -= 10
}
heightConstraint.constant = h
}
}
The output looks like this (the custom view is outlined in red):
you can tap the "Taller" / "Shorter" buttons to make the custom view grow or shrink (by 10-pts each tap) to see the changes:
Note that the view will eventually get too tall for the 1:1 ratio image view to fit horizontally:

UIScrollView with Embedded UIImageView; how to get the image to fill the screen

UIKit/Programmatic UI
I have an UIScrollView with an UIImageView inside. The image is set by user selection and can have all kinds of sizes. What I want is that the image initially fills the screen (view) and then can be zoomed and scrolled all the way to the edges of the image.
If I add the ImageView directly to the view (no scrollView), I get it to fill the screen with the following code:
mapImageView.image = ProjectImages.projectDefaultImage
mapImageView.translatesAutoresizingMaskIntoConstraints = false
mapImageView.contentMode = .scaleAspectFill
view.addSubview(mapImageView)
Now the same with the scrollView and the embedded imageView:
view.insertSubview(mapImageScrollView, at: 0)
mapImageScrollView.delegate = self
mapImageScrollView.translatesAutoresizingMaskIntoConstraints = false
mapImageScrollView.contentMode = .scaleAspectFill
mapImageScrollView.maximumZoomScale = 4.0
mapImageScrollView.pinToEdges(of: view, safeArea: true)
mapImageView.image = ProjectImages.projectDefaultImage
mapImageView.translatesAutoresizingMaskIntoConstraints = false
mapImageView.contentMode = .scaleAspectFill
mapImageScrollView.addSubview(mapImageView)
And now, if the image's height is smaller than the view's height, the image does not fill the screen and I'm left with a blank view area below the image. I can zoom and scroll ok, and then the image does fill the view.
Adding contsraints will fill the view as I want, but interferes with the zooming and scrolling and prevents me getting to the edges of the image when zoomed in.
How to set this up correctly ?
You might find this useful...
It allows you to zoom an image in a scrollView, starting with it centered and maintaining aspect ratio.
Here's a complete implementation. It has two important variables at the top:
// can be .scaleAspectFill or .scaleAspectFit
var fitMode: UIView.ContentMode = .scaleAspectFill
// if fitMode is .scaleAspectFit, allowFullImage is ignored
// if fitMode is .scaleAspectFill, image will start zoomed to .scaleAspectFill
// if allowFullImage is false, image will zoom back to .scaleAspectFill if "pinched in"
// if allowFullImage is true, image can be "pinched in" to see the full image
var allowFullImage: Bool = true
Everything is done via code - no #IBOutlet or other connections - so just create add a new view controller and assign its custom class to ZoomAspectViewController (and edit the name of the image you want to use):
class ZoomAspectViewController: UIViewController, UIScrollViewDelegate {
var scrollView: UIScrollView!
var imageView: UIImageView!
var imageViewBottomConstraint: NSLayoutConstraint!
var imageViewLeadingConstraint: NSLayoutConstraint!
var imageViewTopConstraint: NSLayoutConstraint!
var imageViewTrailingConstraint: NSLayoutConstraint!
// can be .scaleAspectFill or .scaleAspectFit
var fitMode: UIView.ContentMode = .scaleAspectFit
// if fitMode is .scaleAspectFit, allowFullImage is ignored
// if fitMode is .scaleAspectFill, image will start zoomed to .scaleAspectFill
// if allowFullImage is false, image will zoom back to .scaleAspectFill if "pinched in"
// if allowFullImage is true, image can be "pinched in" to see the full image
var allowFullImage: Bool = true
override func viewDidLoad() {
super.viewDidLoad()
guard let img = UIImage(named: "myImage") else {
fatalError("Could not load the image!!!")
}
scrollView = UIScrollView()
imageView = UIImageView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleToFill
scrollView.addSubview(imageView)
view.addSubview(scrollView)
// respect safe area
let g = view.safeAreaLayoutGuide
imageViewTopConstraint = imageView.topAnchor.constraint(equalTo: scrollView.topAnchor)
imageViewBottomConstraint = imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor)
imageViewTrailingConstraint = imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor)
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: g.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
imageViewTopConstraint,
imageViewBottomConstraint,
imageViewLeadingConstraint,
imageViewTrailingConstraint,
])
scrollView.delegate = self
scrollView.minimumZoomScale = 0.1
scrollView.maximumZoomScale = 5.0
imageView.image = img
imageView.frame.size = img.size
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { _ in
self.updateMinZoomScaleForSize(size, shouldSize: (self.scrollView.zoomScale == self.scrollView.minimumZoomScale))
self.updateConstraintsForSize(size)
}, completion: {
_ in
})
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateMinZoomScaleForSize(scrollView.bounds.size)
updateConstraintsForSize(scrollView.bounds.size)
if fitMode == .scaleAspectFill {
centerImageView()
}
}
func updateMinZoomScaleForSize(_ size: CGSize, shouldSize: Bool = true) {
guard let img = imageView.image else {
return
}
var bShouldSize = shouldSize
let widthScale = size.width / img.size.width
let heightScale = size.height / img.size.height
var minScale = min(widthScale, heightScale)
let startScale = max(widthScale, heightScale)
if fitMode == .scaleAspectFill && !allowFullImage {
minScale = startScale
}
if scrollView.zoomScale < minScale {
bShouldSize = true
}
scrollView.minimumZoomScale = minScale
if bShouldSize {
scrollView.zoomScale = fitMode == .scaleAspectFill ? startScale : minScale
}
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
updateConstraintsForSize(scrollView.bounds.size)
}
func centerImageView() -> Void {
let yOffset = (scrollView.frame.size.height - imageView.frame.size.height) / 2
let xOffset = (scrollView.frame.size.width - imageView.frame.size.width) / 2
scrollView.contentOffset = CGPoint(x: -xOffset, y: -yOffset)
}
func updateConstraintsForSize(_ size: CGSize) {
let yOffset = max(0, (size.height - imageView.frame.height) / 2)
imageViewTopConstraint.constant = yOffset
imageViewBottomConstraint.constant = yOffset
let xOffset = max(0, (size.width - imageView.frame.width) / 2)
imageViewLeadingConstraint.constant = xOffset
imageViewTrailingConstraint.constant = xOffset
view.layoutIfNeeded()
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
}
Edit
As an example, I used this image (2560 x 1440):
and I get this result on launch:
and maximum zoom in (5.0) scrolled to top-center:
Edit 2
Same image, at launch, with:
var fitMode: UIView.ContentMode = .scaleAspectFill
instead of .scaleAspectFit:
I've found this solution that works for me, when setting and changing the image, I calculate the minimum needed zoom scale and set it on the scrollView:
var selectedMapImage: MapImage? {
didSet {
mapImageView.image = mapImagesController.getImageForMapImage(selectedMapImage!)
mapImageScrollView.minimumZoomScale = view.bounds.height / mapImageView.image!.size.height
mapImageScrollView.setZoomScale(mapImageScrollView.minimumZoomScale, animated: true)
mapImageScrollView.scrollRectToVisible(view.bounds, animated: true)
}
}

Removing a UIView from UIStackView Changes its Size

I am trying to build a drag drop action and if I drag a view from stack view and drop it to somewhere and remove the view with "removeArrangedSubview" it changes the dragged items size and makes it bigger. I am using this piece of code to resize dropped item.
sender.view!.frame = CGRectMake(sender.view!.frame.origin.x, sender.view!.frame.origin.y, sender.view!.frame.width * 0.5, sender.view!.frame.height * 0.5)
Here is the image for the comparison.
You are setting the frame of the view you are moving but when you place it in a stack view the stack view will reset it based on the stack view's constraints and the intrinsic content size of the view that you are adding to it. The reason it is not doing that when you don't call removeArrangedSubview is because it has not triggered an auto layout pass. In general, when you're using auto layout you shouldn't set views frames but rather update constraints intrinsicContentSize and request auto layout updates.
You should play around with the distribution property of your stack view, the constraints you have placed on it, as well as the intrinsicContentSize of the views you are adding to it until you get your desired result.
For example:
class ViewController: UIViewController
{
let stackView = UIStackView()
let otherStackView = UIStackView()
override func viewDidLoad()
{
self.view.backgroundColor = UIColor.whiteColor()
stackView.alignment = .Center
stackView.axis = .Horizontal
stackView.spacing = 10.0
stackView.distribution = .FillEqually
stackView.translatesAutoresizingMaskIntoConstraints = false
otherStackView.alignment = .Center
otherStackView.axis = .Horizontal
otherStackView.spacing = 10.0
otherStackView.distribution = .FillEqually
otherStackView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackView)
self.view.addSubview(otherStackView)
stackView.bottomAnchor.constraintEqualToAnchor(self.view.bottomAnchor).active = true
stackView.leadingAnchor.constraintEqualToAnchor(self.view.leadingAnchor).active = true
stackView.trailingAnchor.constraintEqualToAnchor(self.view.trailingAnchor).active = true
stackView.heightAnchor.constraintEqualToConstant(150).active = true
otherStackView.topAnchor.constraintEqualToAnchor(self.view.topAnchor).active = true
otherStackView.widthAnchor.constraintGreaterThanOrEqualToConstant(50).active = true
otherStackView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor).active = true
otherStackView.heightAnchor.constraintEqualToConstant(150).active = true
self.addSubviews()
}
func addSubviews()
{
for _ in 0 ..< 5
{
let view = View(frame: CGRect(x: 0.0, y: 0.0, width: 100, height: 100))
view.userInteractionEnabled = true
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.panHandler(_:)))
view.addGestureRecognizer(panGesture)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.redColor()
self.stackView.addArrangedSubview(view)
}
}
func panHandler(sender: UIPanGestureRecognizer)
{
guard let view = sender.view else{ return }
switch sender.state {
case .Began:
self.stackView.removeArrangedSubview(view)
self.view.addSubview(view)
let location = sender.locationInView(self.view)
view.center = location
case .Changed:
view.center.x += sender.translationInView(self.view).x
view.center.y += sender.translationInView(self.view).y
case .Ended:
if CGRectContainsPoint(self.otherStackView.frame, view.center)
{
self.otherStackView.addArrangedSubview(view)
self.otherStackView.layoutIfNeeded()
}
else
{
self.stackView.addArrangedSubview(view)
self.otherStackView.layoutIfNeeded()
}
default:
self.stackView.addArrangedSubview(view)
self.otherStackView.layoutIfNeeded()
}
sender.setTranslation(CGPointZero, inView: self.view)
}
}
class View: UIView
{
override func intrinsicContentSize() -> CGSize
{
return CGSize(width: 100, height: 100)
}
}
The above view controller and UIView subclass does something similar to what you're looking for, but it's not possible to tell from your question. Notice that pinning a stack view to the edges of its super view (like stackView) causes it to stretch subviews to fill all the available space while allowing the stack view to dynamically size based on the intrinsic size of its subviews (like otherStackView) does not.
Update
If you don't want to add the view to another stack view but you want them to retain their frame, you should remove any constraints the view's have on them and then set their translatesAutoresizingMaskIntoConstraints property to true. For example:
func panHandler(sender: UIPanGestureRecognizer)
{
guard let view = sender.view else{ return }
switch sender.state {
case .Began:
self.stackView.removeArrangedSubview(view)
self.view.addSubview(view)
let location = sender.locationInView(self.view)
view.center = location
case .Changed:
view.center.x += sender.translationInView(self.view).x
view.center.y += sender.translationInView(self.view).y
default:
view.removeConstraints(view.constraints)
view.translatesAutoresizingMaskIntoConstraints = true
// You can now set the view's frame or position however you want
view.center.x += sender.translationInView(self.view).x
view.center.y += sender.translationInView(self.view).y
}
sender.setTranslation(CGPointZero, inView: self.view)
}