I am trying to horizontally center 30 images in a scrollview. What is the best approach to do so? Thanks.
EDIT: Programmatically I was able to create the images, thanks to David Cao, but I am still having the issue of it not centering. Thanks.
If they're all the same image, why not do this programmatically? Just make a for loop and iterate through the bounds.
NSInteger numWidth = 3;
NSInteger numHeight = 10;
CGFloat border = 8;
CGFloat width = (self.scrollView.frame.size.width - (numWidth + 1) * border)/3;
for (NSInteger i = 0; i < numWidth; ++i) {
for (NSInteger j = 0; j < numHeight; ++j) {
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"imageNameHere"]];
imageView.frame = CGRectMake(border + width*i, border + width*j, width, width);
[self.scrollView addSubview:imageView];
}
}
[self.scrollView setContentSize:CGSizeMake(self.scrollView.frame.size.width, (border + width)*numHeight + border];
I was able to solve this problem. I modified David Cao's code to account for various screen widths using
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width`
I adjusted the scrollView frame size width to equal the screenWidth. Removing the border variable fit my need for fitting the screen perfectly.
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width
//let screenHeight = screenSize.height
let numWidth: CGFloat = 3
let numHeight: CGFloat = 10
self.scrollView.frame.size.width = screenWidth
let width: CGFloat = (self.scrollView.frame.size.width - (numWidth + 1))/3
for var i:CGFloat = 0; i < 3; ++i{
for var j:CGFloat = 0; j < 10; ++j {
let image: UIImage = UIImage(named: "image1.png")!
imageView = UIImageView(image: image)
imageView!.frame = CGRectMake(width*i, width*j, width, width)
self.scrollView.addSubview(imageView!)
}
}
scrollView.contentSize.height = (width)*numHeight
Thank you all for the help!
Related
enter image description hereWhen i add three imageViews to scrollView, and i setting scrollView contentSize is three times scrollView's width, even if it was appeared in scrolView,but imageView's width not equal to scrollview's. I ensured that already setting they width equeal to each otehr. If who know that how to resolve this issue,please help me, thinks.
- (void)addScrollViewImage {
for (NSInteger index = 0; index < IMAGE_NUM; index++) {
UIImageView *imageView = [[UIImageView alloc] init];
CGFloat imageViewX = index * (self.scrollView.frame.size.width);
imageView.frame = CGRectMake(imageViewX, 0, self.scrollView.frame.size.width , self.scrollView.frame.size.height);
imageView.image = [UIImage imageNamed:#"搜索图片"];
imageView.contentMode = UIViewContentModeScaleAspectFill;
[self.scrollView addSubview:imageView];
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * IMAGE_NUM, self.scrollView.frame.size.height);
}
you need to change imageview content mode
imageView.contentMode = UIViewContentModeScaleAspectFill;
With
imageView.contentMode = UIViewContentModeScaleToFill;
This is because when your viewController is loaded, view / scrollView frame is still unknown.
You need to override viewDidLayoutSubviews() and setup children frames and content size there.
Important note: iOS is adding EXTRA UIImageView's for scroll indicators. That's why you can't use scrollView.subviews.count inside viewDidLayoutSubviews(), but you can use IMAGE_NUM though.
Some Swift code:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let scrollSize = scrollView.bounds.size
var frame = scrollView.bounds
// Wrong: for i in 0 ..< scrollView.subviews.count
for i in 0 ..< IMAGE_NUM {
let view = scrollView.subviews[i]
view.frame = frame
frame.origin.x += scrollSize.width
}
scrollView.contentSize = CGSize(width: frame.origin.x, height: scrollSize.height)
}
I have a ScrollView combined with a PageControll and it contains 5 images which I want them to scroll. My problem is that the ScrollView width even if it is 320 in simulator it doesn't show covering the all width.
This is my code:
override func viewDidLoad() {
super.viewDidLoad()
images.append(UIImage(named: "one.jpg")!)
images.append(UIImage(named: "two.jpg")!)
images.append(UIImage(named: "three.jpg")!)
images.append(UIImage(named: "four.jpg")!)
images.append(UIImage(named: "five.jpg")!)
for var i = 0; i < images.count; i++ {
var frame: CGRect = CGRectMake(0, 0, 0, 0)
frame.origin.x = self.scrollView.frame.size.width * CGFloat(i)
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
var imageView: UIImageView = UIImageView(frame: frame)
imageView.image = images[i]
self.scrollView.addSubview(imageView)
}
self.scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * CGFloat(images.count), scrollView.frame.size.height)
}
Most likely, the scroll view's width is the correct size, but the content-mode of the UIImageView is set incorrectly such that as a result of the image being displayed having a smaller size than the scroll view, it will not fill the whole of the image view as you wanted.
imageView.contentMode = UIViewContentModeScaleAspectFill;
// Swift
imageView.contentMode = .ScaleAspectFit
Try adding constraints to your image in your interface builder.
Put Constraints:
Main.storyboard -> UIImageView (for each UIImageView) -> Editor -> Pin -> Select leading,top space, bottom and trailing space to superview.
Your scrollView doesn't have constraints
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?
It looks like aspect fit aligns the image to the bottom of the frame by default. Is there a way to override the alignment while keeping aspect fit intact?
** EDIT **
This question predates auto layout. In fact, auto layout was being revealed in WWDC 2012 the same week this question was asked
In short, you cannot do this with a UIImageView.
One solution is to subclass a UIView containing an UIImageView and change its frame according to image size. For example, you can find one version here.
Set the UIImageView's bottom layout constraint priority to lowest (i.e. 250) and it will handle it for you.
The way to do this is to modify the contentsRect of the UIImageView layer. The following code from my project (sub class of UIImageView) assumes scaleToFill and offsets the image such that it aligns top, bottom, left or right instead of the default center alignment. For aspectFit is would be a similar solution.
typedef NS_OPTIONS(NSUInteger, AHTImageAlignmentMode) {
AHTImageAlignmentModeCenter = 0,
AHTImageAlignmentModeLeft = 1 << 0,
AHTImageAlignmentModeRight = 1 << 1,
AHTImageAlignmentModeTop = 1 << 2,
AHTImageAlignmentModeBottom = 1 << 3,
AHTImageAlignmentModeDefault = AHTImageAlignmentModeCenter,
};
- (void)updateImageViewContentsRect {
CGRect imageViewContentsRect = CGRectMake(0, 0, 1, 1);
if (self.image.size.height > 0 && self.bounds.size.height > 0) {
CGRect imageViewBounds = self.bounds;
CGSize imageSize = self.image.size;
CGFloat imageViewFactor = imageViewBounds.size.width / imageViewBounds.size.height;
CGFloat imageFactor = imageSize.width / imageSize.height;
if (imageFactor > imageViewFactor) {
//Image is wider than the view, so height will match
CGFloat scaledImageWidth = imageViewBounds.size.height * imageFactor;
CGFloat xOffset = 0.0;
if (BM_CONTAINS_BIT(self.alignmentMode, AHTImageAlignmentModeLeft)) {
xOffset = -(scaledImageWidth - imageViewBounds.size.width) / 2;
} else if (BM_CONTAINS_BIT(self.alignmentMode, AHTImageAlignmentModeRight)) {
xOffset = (scaledImageWidth - imageViewBounds.size.width) / 2;
}
imageViewContentsRect.origin.x = (xOffset / scaledImageWidth);
} else if (imageFactor < imageViewFactor) {
CGFloat scaledImageHeight = imageViewBounds.size.width / imageFactor;
CGFloat yOffset = 0.0;
if (BM_CONTAINS_BIT(self.alignmentMode, AHTImageAlignmentModeTop)) {
yOffset = -(scaledImageHeight - imageViewBounds.size.height) / 2;
} else if (BM_CONTAINS_BIT(self.alignmentMode, AHTImageAlignmentModeBottom)) {
yOffset = (scaledImageHeight - imageViewBounds.size.height) / 2;
}
imageViewContentsRect.origin.y = (yOffset / scaledImageHeight);
}
}
self.layer.contentsRect = imageViewContentsRect;
}
Swift version
class AlignmentImageView: UIImageView {
enum HorizontalAlignment {
case left, center, right
}
enum VerticalAlignment {
case top, center, bottom
}
// MARK: Properties
var horizontalAlignment: HorizontalAlignment = .center
var verticalAlignment: VerticalAlignment = .center
// MARK: Overrides
override var image: UIImage? {
didSet {
updateContentsRect()
}
}
override func layoutSubviews() {
super.layoutSubviews()
updateContentsRect()
}
// MARK: Content layout
private func updateContentsRect() {
var contentsRect = CGRect(origin: .zero, size: CGSize(width: 1, height: 1))
guard let imageSize = image?.size else {
layer.contentsRect = contentsRect
return
}
let viewBounds = bounds
let imageViewFactor = viewBounds.size.width / viewBounds.size.height
let imageFactor = imageSize.width / imageSize.height
if imageFactor > imageViewFactor {
// Image is wider than the view, so height will match
let scaledImageWidth = viewBounds.size.height * imageFactor
var xOffset: CGFloat = 0.0
if case .left = horizontalAlignment {
xOffset = -(scaledImageWidth - viewBounds.size.width) / 2
}
else if case .right = horizontalAlignment {
xOffset = (scaledImageWidth - viewBounds.size.width) / 2
}
contentsRect.origin.x = xOffset / scaledImageWidth
}
else {
let scaledImageHeight = viewBounds.size.width / imageFactor
var yOffset: CGFloat = 0.0
if case .top = verticalAlignment {
yOffset = -(scaledImageHeight - viewBounds.size.height) / 2
}
else if case .bottom = verticalAlignment {
yOffset = (scaledImageHeight - viewBounds.size.height) / 2
}
contentsRect.origin.y = yOffset / scaledImageHeight
}
layer.contentsRect = contentsRect
}
}
this will make the image fill the width and occupy only the height it needs to fit the image (widthly talking)
swift 4.2:
let image = UIImage(named: "my_image")!
let ratio = image.size.width / image.size.height
cardImageView.widthAnchor
.constraint(equalTo: cardImageView.heightAnchor, multiplier: ratio).isActive = true
I had similar problem.
Simplest way was to create own subclass of UIImageView. I add for subclass 3 properties so now it can be use easly without knowing internal implementation:
#property (nonatomic) LDImageVerticalAlignment imageVerticalAlignment;
#property (nonatomic) LDImageHorizontalAlignment imageHorizontalAlignment;
#property (nonatomic) LDImageContentMode imageContentMode;
You can check it here:
https://github.com/LucasssD/LDAlignmentImageView
Add the Aspect Ratio constraint with your image proportions.
Do not pin UIImageView to bottom.
If you want to change the UIImage dynamically remember to update aspect ratio constraint.
I solved this natively in Interface Builder by setting a constraint on the height of the UIImageView, since the image would always be 'pushed' up when the image was larger than the screen size.
More specifically, I set the UIImageView to be the same height as the View it is in (via height constraint), then positioned the UIImageView with spacing constraints in IB. This results in the UIImageView having an 'Aspect Fit' which still respects the top spacing constraint I set in IB.
If you are able to subclass UIImageView, then you can just override the image var.
override var image: UIImage? {
didSet {
self.sizeToFit()
}
}
In Objective-C you can do the same thing by overriding the setter.
From the URL Image in Mail
I'm adding image to mail view. It will show full image. But I want to calculate, proportionally change the height and width of the image.
How can I get the height and width of UIImage?
let heightInPoints = image.size.height
let heightInPixels = heightInPoints * image.scale
let widthInPoints = image.size.width
let widthInPixels = widthInPoints * image.scale
Use the size property on the UIImage instance. See the documentation for more details.
UIImage *img = [UIImage imageNamed:#"logo.png"];
CGFloat width = img.size.width;
CGFloat height = img.size.height;
Some of the anwsers above, don't work well when rotating device.
Try:
CGRect imageContent = self.myUIImage.bounds;
CGFloat imageWidth = imageContent.size.width;
CGFloat imageHeight = imageContent.size.height;
There are a lot of helpful solutions out there, but there is no simplified way with extension. Here is the code to solve the issue with an extension:
extension UIImage {
var getWidth: CGFloat {
get {
let width = self.size.width
return width
}
}
var getHeight: CGFloat {
get {
let height = self.size.height
return height
}
}
}
UIImageView *imageView = [[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"MyImage.png"]]autorelease];
NSLog(#"Size of my Image => %f, %f ", [[imageView image] size].width, [[imageView image] size].height) ;
import func AVFoundation.AVMakeRect
let imageRect = AVMakeRect(aspectRatio: self.image!.size, insideRect: self.bounds)
x = imageRect.minX
y = imageRect.minY
let imageView: UIImageView = //this is your existing imageView
let imageViewHeight: CGFloat = imageView.frame.height
let imageViewWidth: CGFloat = imageView.frame.width