Margin between images in UIScrollView - swift

The effect I'm after: Having spacing between images that is only visible while scrolling (like the photos app).
A lot of old obj-c answers suggest extending the scroll view's bounds offscreen to make it page farther, and making this offscreen space the gap between images.
The documentation for pagingEnabled states:
If the value of this property is YES, the scroll view stops on
multiples of the scroll view’s bounds when the user scrolls.
So in trying to change the multiples value, I extended the scrollView's width, and left paging enabled. Yet no answers I implement page past the gap - they always leave it in view:
So if the scroll width is longer, why isn't it paging the proper distance?
let gapMargin = CGFloat(20)
scrollView.frame = CGRect(x: 0, y: 0, width: view.frame.width + gapMargin, height: view.frame.height)
let exdScrollWidth = scrollView.frame.width
//1
let imageView1 = UIImageView()
imageView1.backgroundColor = UIColor.green
imageView1.frame = CGRect(x: 0, y: 0, width: exdScrollWidth - gapMargin, height: scrollView.bounds.size.height)
//2
let imageView2 = UIImageView()
imageView2.backgroundColor = UIColor.yellow
imageView2.frame = CGRect(x: exdScrollWidth, y: 0, width: exdScrollWidth - gapMargin, height: scrollView.bounds.size.height)
//3
let imageView3 = UIImageView()
imageView3.backgroundColor = UIColor.red
imageView3.frame = CGRect(x: exdScrollWidth * 2, y: 0, width: exdScrollWidth - gapMargin, height: scrollView.bounds.size.height)
scrollView.contentSize.width = exdScrollWidth * 3
scrollView.addSubview(imageView1)
scrollView.addSubview(imageView2)
scrollView.addSubview(imageView3)

As the docs tell you, one "page" width is the bounds width of the scroll view.
So let's say the images are 100 points wide. And let's say the space between the images is to be 10 points. Then:
The scroll view's width must be 110 points.
The spaces must be distributed 5 points on each side of each image, like this (supposing we have 3 images):
5pts - 100pts (im) - 10pts - 100pts (im) - 10pts - 100pts(im) - 5pts
This will cause each page to consist in a 100pt image with 5 pts of space on each side, a total of 110 pts, the width of the scroll view:
5pts - 100pts (im) - 10pts - 100pts (im) - 10pts - 100pts(im) - 5pts
| | | |

It turns out I had an equal widths constraint set up that I'd forgotten about. This meant the multiple by which the scrollview paged was fixed at the width of the superview.

Related

How to set a button's width based on an iPad Modal View Controller's frame width? - Swift 5

so I'm trying to add a button that is the view's width - 50 and have it be centered in the lower portion of the view controller.
This is how I am doing that:
purchaseButton.layer.cornerRadius = 14
purchaseButton.frame = CGRect(x: 0, y: 0, width: self.view.frame.width - 50, height: 50)
purchaseButton.autoresizingMask = [.flexibleLeftMargin, .flexibleBottomMargin]
purchaseButton.contentEdgeInsets = UIEdgeInsets(top: 14, left: 0, bottom: 14, right: 0)
let xPosition: CGFloat = self.view.frame.width / 2.0
let yPosition: CGFloat = self.view.frame.height / 1.25
purchaseButton.center = CGPoint(x: xPosition, y: yPosition)
This works perfectly on all iPhone devices and sizes. However, for an iPad the view is a modal and I think the frame.width is the entire iPad screen size instead of just the smaller view. This causes the button to be too low and too wide on iPad devices. Is there any way I can get the width and height of the modal popover view controller instead of the entire device's frame and use that to set the button size?
I found out that I need to call the code in viewWillAppear rather than viewDidLoad in order to get the correct size of the view controller. Doing so fixed the issue!

swift cannot show the view at bottom of imageview

i'm trying to show a layout at from the bottom of an imageview to a predefined height. Unfortunately its shown at the top, not at the bottom. This is my code i have tried:
var frm: CGRect = firstImageOverlay.frame
frm.origin.x = frm.origin.x
frm.origin.y = frm.origin.y
frm.size.width = frm.size.width
frm.size.height = frm.size.height
firstImageOverlay.frame = frm
UIView.animate(withDuration: 0.5, animations: {
self.progressA.frame = CGRect(x: frm.origin.x, y: frm.origin.y, width: frm.size.width, height: self.firstImageOverlay.frame.height*CGFloat(0.4))
})
and here is the result of this code:
the light gray part shows the layout at the left imageview. this should be shown from the bottom till the defined height
iOS uses a reflected cartesian coordinate system with 0,0 in the top right and positive y pointing down and positive x pointing right, so when you draw at x: frm.origin.x, y: frm.origin.y you are saying draw from the top left down to the width and height.
You need to adjust your y coordinate down:
self.progressA.frame = CGRect(x: frm.origin.x, y: frm.origin.y + self.firstImageOverlay.frame.height*CGFloat(0.6), width: frm.size.width, height: self.firstImageOverlay.frame.height*CGFloat(0.4))

Swift - TableView background image: how to center the image

I want to add a logo as a background image to my tableView. Image size is 50px, 50px.
I tried the code below, but this puts the image lower right corner.
let imageView = UIImageView(image: UIImage(named: "logo"))
imageView.contentMode = .scaleAspectFit
imageView.layer.frame = CGRect(x: self.view.frame.midX, y: self.view.frame.midY, width: 50, height: 50)
let tableViewBackgroundView = UIView()
tableViewBackgroundView.addSubview(imageView)
self.tableView.backgroundView = tableViewBackgroundView
There are a few points about swift that are pretty key:
1) The x and y parameters in CGRect.init(x:y:width:height:) don't refer to the center of the imageView. Instead, they are points in a coordinate system where (0, 0) is the Upper Left Corner of the view that the CGRect is being presented over, and
2) UIImageView actually inherits from UIView, so because of this you can just say
tableView.backgroundView = imageView
You shouldn't have to fiddle around with any CGRects, I believe this code should work just fine, although you may have to fiddle with different contentMode properties to get it to display how you like.
let imageView = UIImageView(image: UIImage(named: "logo"))
imageView.layer.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
imageView.layer.frame.midX = tableView.layer.frame.midX
imageView.layer.frame.midY = tableView.layer.frame.midY
tableView.backgroundView = imageView

Central align subView

I'm trying to centre a UIView in another view (so like a pop up view), but whatever I do, I just cannot align it centrally!
func loadPopUpView() {
let customView = CGRect(x: view.center.x, y: view.center.y, width: 100, height: 100)
popUpView = UIView(frame: customView)
popUpView.backgroundColor = UIColor.black
view.addSubview(popUpView)
popUpView.isHidden = false
}
I've changed the background colour to black just so I know when it appears.
I cannot do this with storyboard because it's going on a tableView, so I'm trying to do it programmatically. Result Image
You also need to minus half value of your custom view height and width from x and y like below:
let customView = CGRect(x: view.center.x - 50, y: view.center.y - 50, width: 100, height: 100)
You are telling it to put top left corner in the center, hence you need to let it get half size in position.
let customView = CGRect(x: view.center.x-50, y: view.center.y-50, width: 100, height: 100)
Another solution is the use anchorpoints.
customView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
customView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
customView.heightAnchor.constraint(equalToConstant: 100).isActive = true
customView.widthAnchor.constraint(equalToConstant: 100).isActive = true
On CGRect(x:, y:, width:, height:), the point (x,y) is the origin. In iOS, that's the top left point.
On the CGRect doc:
In the default Core Graphics coordinate space, the origin is located
in the lower-left corner of the rectangle and the rectangle extends
towards the upper-right corner. If the context has a
flipped-coordinate space—often the case on iOS—the origin is in the
upper-left corner and the rectangle extends towards the lower-right
corner.
So to fix this:
let width = 100.0
let height = 100.0
let customViewFrame = CGRect(x: view.center.x - width/2.0, y: view.center.y - height/2.0, width: width, height: height)
Another solution would be to apply the center once the frame (especially the width/size) have been set.
let customViewFrame = CGRect(x: 0, y: 0, width: 100, height: 100)
customViewFrame = view.center

I want my view split 46:54 into 2 views, proportioned to phone screen. Views have subviews. How?

I want my main view to be separated into two views, where left takes 46% and right takes 54% of screen, minus a pixel for a divider view.
Within these 2 views I want to put some subviews on the storyboard. Problem is I can't set width constraints of the 2 views since their widths are not calculated until run time, and I believe this constraint ambiguity is why their subviews wont respect constraints I give them (ie: center horizontal).
How can I achieve the results I want? This is how I am currently setting geometry of the 2 views:
override func viewWillAppear(_ animated: Bool) {
setGeometry()
}
func setGeometry(){
let width = view.bounds.width
let dividerPoint = width * 0.46
let height = view.bounds.height
leftView.frame = CGRect(x: 0, y: 0, width: dividerPoint, height: height)
divider.frame = CGRect(x: dividerPoint, y: 0, width: 1, height: height)
rightView.frame = CGRect(x: dividerPoint + 1, y: 0, width: width - 1 - dividerPoint, height: height)
}
You can do it in Interface Builder by constraints. Add equal width constraints from each view to main view with multiplier 0.46 and 0.54 (last also with -1 value).