i have built an app that allows the user to take their picture with a selection of glasses overlaid. it is working exactly how i want it on both an ipad and ipad mini but on the iphone the position in relation the the scroll view is wrong (see Images).
func setupPages(){
pageImages = [
UIImage(named: "glasses_one")!,
UIImage(named: "glasses_two")!,
UIImage(named: "glasses_three")!,
UIImage(named: "glasses_four")!,
UIImage(named: "glasses_five")!,
UIImage(named: "glasses_six")!
]
let pageCount = pageImages.count
for _ in 0..<pageCount {
pageViews.append(nil)
}
print(pageViews.count)
let pagesScrollViewSize = scrollView.frame.size
scrollView.contentSize = CGSize(width: scrollWidth * CGFloat(pageImages.count),
height: pagesScrollViewSize.height)
scrollView.backgroundColor = UIColor.blueColor()
loadVisiblePages()
}
func loadVisiblePages() {
let pageWidth = scrollView.frame.size.width
page = Int(floor((scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth * 2.0)))
print(page)
pageControl.currentPage = page
for index in 0...5 {
//print(" -- Index = \(index)")
loadPage(index)
}
}
func loadPage(page: Int) {
if page < 0 || page >= pageImages.count {
// If it's outside the range of what you have to display, then do nothing
return
}
// 1
if let pageView = pageViews[page] {
print(pageView.description)
// Do nothing. The view is already loaded.
} else {
// 2
var frame = scrollView.bounds
frame.origin.x = scrollWidth * CGFloat(page)
frame.origin.y = 0.0
frame.size.width = scrollWidth
// 3
let newPageView = UIImageView(image: pageImages[page])
newPageView.contentMode = .ScaleAspectFit
newPageView.frame = frame
newPageView.clipsToBounds = false
scrollView.addSubview(newPageView)
// 4
pageViews[page] = newPageView
}
}
Ipad
Iphone
I think your issue is you need to add a constraint to center you image vertically. Or you could also solve this by making the frame for your "glasses frame" A set width and height. Smaller screens will have less space around it but will still be in the middle.
Related
I have place UIImageView under UIScrollView on screen with constant width same as screen, and it works fine in iphone 5s but on iphone 7 or 8 the size of image is less than the width of screen size as shared on screenshots.
here is my code:
let x = CGFloat(selectedEvent.album?.count ?? 0)
pageControl.numberOfPages = (selectedEvent.album?.count ?? 0)
for index in 0..<(selectedEvent.album?.count ?? 0) {
frame.origin.x = scroll.frame.size.width * CGFloat(index)
frame.size = scroll.frame.size
print(self.frame.size.width)
print(scroll.frame.size.width)
print(frame.size.width)
let image = UIImageView(frame: frame)
image.contentMode = .scaleToFill
print(image.frame.size.width)
if (selectedEvent.album?.count ?? 0) > 0 {
selectedEvent.getAlbumImage(key: selectedEvent.album![index], completion: { (gotImage:UIImage) in
image.image = gotImage
})
} else {
image.image = UIImage(named: "defaultImage.jpg")
}
scroll .addSubview(image)
}
self.scroll.contentSize = CGSize(width: scroll.frame.size.width * x, height: scroll.frame.size.height)
pageControl.addTarget(self, action: #selector(self.changePage(sender:)), for: UIControlEvents.valueChanged)
here is the Print Log
320.0
320.0
320.0
320.0
320.0
320.0
320.0
320.0
In shorts, views might not have updated frames on viewDidLoad or ViewWillAppear methods instead views are initialised with storyboard sizes. So you are not getting updated rect for iPhone 7 or other devices.
Another reason is scrollview adjusts it's subview automatically. So disable it on viewDidLoad().
override func viewDidLoad() {
super.viewDidLoad()
self.scroll.contentInsetAdjustmentBehavior = .never;
// write your code
}
You should add subviews on viewDidLoad() method as you have done already and update frame & scrollview content related changes on viewDidLayoutSubviews().
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews();
for view:UIView in self.scroll.subviews {
frame.origin.x = self.scroll.frame.size.width * CGFloat(self.scroll.subviews.index(of: view)!);
frame.size = scroll.frame.size
view.frame = frame;
}
self.scroll.contentSize = CGSize(width: scroll.frame.size.width * selectedEvent.album?.count , height: scroll.frame.size.height)
}
Hope will work.
You want the frame width to be the screen width. Use as :
let size = UIScreen.main.bounds.size
self.scroll.contentSize = CGSize(width: size.width, height: scroll.frame.size.height)
I develop an App in Xcoode and I add an ScrollView. Now I added some little Images in a Row.
let myImages = ["BallBeispielBlau.png","BallBeispielRot.png","BallBeispielGelb.png","BallBeispielBlau.png","BallBeispielRot.png","BallBeispielGelb.png","BallBeispielBlau.png","BallBeispielRot.png","BallBeispielGelb.png"]
let imageWidth:CGFloat = 66
let imageHeight:CGFloat = 66
var xPosition:CGFloat = 0
var scrollViewSize:CGFloat=0
for image in myImages {
let myImage:UIImage = UIImage(named: image)!
let myImageView:UIImageView = UIImageView()
myImageView.image = myImage
let LongTapGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "InfosZuEinemEvent:")
LongTapGestureRecognizer.minimumPressDuration = 1.0;
myImageView.addGestureRecognizer(LongTapGestureRecognizer)
myImageView.userInteractionEnabled = true
myImageView.frame.size.width = imageWidth
myImageView.frame.size.height = imageHeight
myImageView.frame.origin.x = xPosition
myImageView.frame.origin.y = 0
ScrollViewFreunde.addSubview(myImageView)
ScrollViewFreunde.showsHorizontalScrollIndicator = false
xPosition += imageWidth
scrollViewSize += imageWidth
}
ScrollViewFreunde.contentSize = CGSize(width: scrollViewSize, height: imageHeight)
That works fine but I want it that the six image so the image which come after the five image where show under the first image so in the second row. I try it with an if statement but it doesn´t work. I hope you could help me.
Thanks for help.
You should reeeeally use a Collection View for this. It is really easy and way better to display items in a matricial form.
However, to answer your question...
You can create a "yPosition" variable that will be incremented by imageHeight every time one image overflows the scrollView visible frame.
When this happens, xPositions goes to 0.
var yPosition = 0
for image in myImages {
let myImage:UIImage = UIImage(named: image)!
let myImageView:UIImageView = UIImageView()
myImageView.image = myImage
let LongTapGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "InfosZuEinemEvent:")
LongTapGestureRecognizer.minimumPressDuration = 1.0;
myImageView.addGestureRecognizer(LongTapGestureRecognizer)
myImageView.userInteractionEnabled = true
myImageView.frame.size.width = imageWidth
myImageView.frame.size.height = imageHeight
myImageView.frame.origin.x = xPosition
myImageView.frame.origin.y = yPosition
ScrollViewFreunde.addSubview(myImageView)
ScrollViewFreunde.showsHorizontalScrollIndicator = false
xPosition += imageWidth
if xPosition + imageWidth >= view.frame.width {
xPosition = 0
yPosition += imageHeight
}
scrollViewSize += imageWidth
}
You should prefer collectionview instead of scrollview. Its just a suggestion.
I've wrote a stack view for this reason, you can download a sample project at:
https://github.com/mcmatan/StackViewWithAutoLayout
Its pretty straight forward, you just call "addSubviewWithLayout" and add your view.
The stack view will manage the scroll content size.
You can call "removeViewWithLayout" to remove a view from the stack.
Cheers
I'm trying to build a very simple UIScrollView with several images where paging is enabled.
However, I can't seen to get the images to correctly be resized to the bounds of my UIScrollView. The images are always bigger than the bounds, thus messing with my paging.
I have a UIScollView and a UIPageControl in Interface Builder and link it in my ViewController swift file.
Here is my viewDidLoad method (pageImages is defined as var pageImages: [UIImage] = [] and pageViews as var pageViews: [UIImageView?] = []:
override func viewDidLoad() {
scrollView.delegate = self
scrollView.pagingEnabled = true
scrollView.scrollEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false
scrollView.bounces = true
scrollView.scrollsToTop = false
pageImages = [UIImage(named: "image1")!,
UIImage(named: "image2")!,
UIImage(named: "image3")!,
UIImage(named: "image4")!,
UIImage(named: "image5")!]
let pageCount = pageImages.count
pageControl.currentPage = 0
pageControl.numberOfPages = pageCount
for (var i=0; i<pageCount; i++) {
var xOrigin: CGFloat = CGFloat(i) * scrollView.bounds.size.width
let imageView: UIImageView = UIImageView(frame: CGRectMake(xOrigin, 0, scrollView.bounds.size.width, scrollView.bounds.size.height))
imageView.image = pageImages[i]
imageView.contentMode = UIViewContentMode.ScaleAspectFill
imageView.clipsToBounds = true
scrollView.addSubview(imageView)
}
let pagesScrollViewSize = scrollView.frame.size
scrollView.contentSize = CGSize(width: pagesScrollViewSize.width * CGFloat(pageImages.count),
height: pagesScrollViewSize.height)
}
My scrollViewDidScroll method is as follows:
func scrollViewDidScroll(scrollView: UIScrollView) {
let pageWidth = self.scrollView.frame.size.width
let page = Int(floor((self.scrollView.contentOffset.x - pageWidth/2)/pageWidth) + 1)
self.pageControl.currentPage = page
}
Can anyone spot my mistake? Do I have to set the contentMode maybe in viewWillAppear or viewDidAppear?
Try setting up your imageViews sizes under:
(void)viewDidLayoutSubviews
At this point, autolayout had finished resizing your views/subviews according to the layout system you defined on interface builder so if your calculations are correct (according your requirements) this should work out fine.
I would like my scrollview's frame to be the same size as the superview's frame. And set the contentsize so that it contains all the imageViews.
The problem is with AutoLayout, the width and height is 600 x 480, I use an iphone5 and the size class is w:Compact / h:Any.(I expected something like : 320 x 523 ).
My constraints are : Equal Width with superview, Equal Height with superview (multiplier 0.8), horizontal center X, and vertical center Y.
let pagesScrollViewSize = scrollView.frame.size
println("pagesScrollViewSize : \(pagesScrollViewSize)") //600x480
scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(pageCount), pagesScrollViewSize.height)
Would someone know how to solve this? The imageview appears bigger than the frame of the screen, so the .SizeAspectFit does not follow the right dimensions. Instead of scrolling to another imageView, it scrolls to see the rest of the image, which I don't want.
EDIT:
This is the whole code in ViewController.swift :
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var scrollView: UIScrollView!
var pageViews: [UIImageView?] = []
var pageImages : [UIImage?] = []
override func viewDidLoad() {
super.viewDidLoad()
pageImages = [UIImage(named:"photo1.png"),
UIImage(named:"photo2.png"),
UIImage(named:"photo3.png"),
UIImage(named:"photo4.png")]
let pageCount = pageImages.count
pageControl.currentPage = 0
pageControl.numberOfPages = pageCount
for _ in 0..<pageCount {
pageViews.append(nil)
}
let pagesScrollViewSize = scrollView.frame.size
println("pagesScrollViewSize : \(pagesScrollViewSize)")
scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(pageCount), pagesScrollViewSize.height)
loadVisiblePages()
}
//when scrolling
func scrollViewDidScroll(scrollView: UIScrollView!){
loadVisiblePages()
}
func loadVisiblePages(){
let pageWidth = scrollView.frame.size.width
let page = Int(floor( (scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth*2.0) ))
println("page: \(page)")
pageControl.currentPage = page
let firstPage = page-1
let lastPage = page+1
//remove all the pages before firstPage
for var index = 0; index < firstPage; ++index{
purgePage(index)
}
//load pages in our range
for var index = firstPage; index <= lastPage; ++index {
loadPage(index)
}
//remove after lastPage
for var index = lastPage+1; index < pageImages.count; ++index {
purgePage(index)
}
}
func loadPage(index:Int){
if index < 0 || index >= pageImages.count {
return
}
if let pageView = pageViews[index] {
//already loaded
}
else {
var frame = scrollView.bounds
frame.origin.x = frame.size.width * CGFloat(index)
frame.origin.y = 0.0
println("\(frame)")
var newImageView = UIImageView(image:pageImages[index])
newImageView.contentMode = .ScaleAspectFit
newImageView.frame = frame
scrollView.addSubview(newImageView)
pageViews[index] = newImageView
}
}
func purgePage(index:Int){
if index < 0 || index >= pageImages.count {
return
}
if let pageView = pageViews[index] {
pageView.removeFromSuperview()
pageViews[index] = nil
}
}
}
Thanks
Size classes (or any auto layout changes, for that matter) are not applied until after viewDidAppear or viewDidLayoutSubviews. You will need to move this code into one of those. You are seeing the frame of the view as it is loaded out of the Interface Builder document (xib or storyboard). You should never be checking frames until after those lifecycle events.
Unable to set frame correctly before viewDidAppear
i'm trying to create a scrollView with a UIPageControl. This works fine, but the problem is the images wont center in each page of the scrollView. it is aligned to the right. How can i center the images always?
image illustration:
http://i.stack.imgur.com/In6t7.png
code:
viewDidLoad
self.pageControl = UIPageControl()
self.pageControl?.frame = CGRectZero
self.pageControl?.currentPage = 0
self.pageControl?.numberOfPages = self.imageArray!.count
self.pageControl?.tintColor = UIColor.whiteColor()
self.pageControl?.userInteractionEnabled = false
self.navigationItem.titleView = self.pageControl?
for var i = 0; i<self.imageArray?.count; i++ {
self.pageViews.append(nil)
}
let pagesScrollViewSize = self.scrollView.frame.size
self.scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(self.imageArray!.count), pagesScrollViewSize.height)
self.loadVisiblePages()
rest of the methods
func loadPage(page: Int) {
if page < 0 || page >= self.imageArray!.count {
// If it's outside the range of what you have to display, then do nothing
return
}
// 1
if let pageView = pageViews[page] {
// Do nothing. The view is already loaded.
} else {
// 2
let newPageView = UIImageView(image: self.imageArray![page] as UIImage)
var frame = scrollView.bounds
frame.origin.x = (frame.size.width * CGFloat(page))
frame.origin.y = 0.0
// 3
newPageView.contentMode = .ScaleAspectFit
newPageView.frame = frame
scrollView.addSubview(newPageView)
newPageView.backgroundColor = UIColor.blackColor()
// 4
pageViews[page] = newPageView
}
}
func purgePage(page: Int) {
if page < 0 || page >= self.imageArray!.count {
// If it's outside the range of what you have to display, then do nothing
return
}
// Remove a page from the scroll view and reset the container array
if let pageView = pageViews[page] {
pageView.removeFromSuperview()
pageViews[page] = nil
}
}
func loadVisiblePages() {
// First, determine which page is currently visible
let pageWidth = scrollView.frame.size.width
let page = Int(floor((scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth * 2.0)))
// Update the page control
pageControl?.currentPage = page
// Work out which pages you want to load
let firstPage = page - 1
let lastPage = page + 1
// Purge anything before the first page
for var index = 0; index < firstPage; ++index {
purgePage(index)
}
// Load pages in our range
for var index = firstPage; index <= lastPage; ++index {
loadPage(index)
}
// Purge anything after the last page
for var index = lastPage+1; index < self.imageArray!.count; ++index {
purgePage(index)
}
}
func scrollViewDidScroll(scrollView: UIScrollView!) {
// Load the pages that are now on screen
loadVisiblePages()
}
I found here you use let to declare a constant UIImageView, I think you should use var.
let newPageView = UIImageView(image: self.imageArray![page] as UIImage)
Actually, I can find any problem besides this, I think the reason you can't set your image to the center because that all the operation after this declaration didn't work, so you can't set the right frame, the right contentMode to the UIImageView.
Perhaps it would be easier if you use the center.x instead of origin.x when positioning your scrollView's image views.
The formula I would use in this case:
centerX = pageNumber * 320 + 160
So something like:
myImageView.center = CGPointMake(pageNumber * 320 + 160, 0);
Running through my formula, I get these result:
page 0: 0 * 320 + 160 = 160
page 1: 1 * 320 + 160 = 480
page 2: 2 * 320 + 160 = 800
page 3: 3 * 320 + 160 = 1120
You can see each page has an offset of 320 (assuming iPhone screen dimension here). You would obviously use this instead:
self.view.bounds.size.width
The 320 here is for illustration purpose.
Your Method
OK, so lets run through your code:
var frame = scrollView.bounds
frame.origin.x = (frame.size.width * CGFloat(page))
frame.origin.y = 0.0
First time, origin.x would be 0 if page starts from 0, scrollView's width remains 320.
Second time, origin.x would be 320 * 1 (page == 1 here), scrollView's width will become 640 since you have 2 imageViews that are 320 wide I assume.
Third time, origin.x would be 640 * 2 = 1280, scrollView's width is now 1600 (origin.x = 1280 + 320 imageView width)
You see how the numbers keeps getting a lot bigger instead of a constant 320?