UIPageControl set numberOfPages with UICollectionView page? - ios

I'm trying to add an UIPageControl and UICollectionView. My collection is enable paging, and now I want my page control numberOfPages is the collection view number of page.
I try to set it like this:
self.pageControl.numberOfPages = self.gridMenuCollection.frame.size.width / self.gridMenuCollection.contentSize.width;
but it's not work. If using my data array for numberOfPage it return alot of dots and I don't want to do it.
Can anyone help?

You are doing the devision the other way around.
Should be:
self.pageControl.numberOfPages = self.gridMenuCollection.contentSize.width / self.gridMenuCollection.frame.size.width;
And also use ceiling to show the correct amount of dots. like this
e.g.
content width is 1200
frame width is 500
content / frame = 2.4
ceil(content / frame) = 3
And you want to show 3 dots, because the elements will fit only to 3.

Related

Swift 4 reverse change height of a UIView

I would like to build a progressbar. I took a rectangular view (progressBarBckgr) and put another one in front of it (progressBar).
The one in the front should increase its height by tabbing on the correct answer within a quiz app.
func updateUI () {
let numberOfAllQuestions = allQuestion.questionList.count
print(numberOfAllQuestions) //prints 3
let progressBarBckgrHeight = progressBarBckgr.frame.size.height
let progressBarBckgrHeightInt = Int(progressBarBckgrHeight)
let progressBarBckgrHeightPiece = progressBarBckgrHeightInt / numberOfAllQuestions
progressBarOutlet.frame.size.height = (progressBarBckgr.frame.height) + CGFloat (progressBarBckgrHeightPiece)
}
So with every correct clicked answer the progressbar should increase just so far, that in the end of quiz the whole backgroundprogressbar is covered.
Example:
10 Questions
-> clicked answer fills increases the progressbar's height for 1/10
In addition to that i would like to increase the bar from the bottom to the top. As i enter the property height to increase it, it only gets bigger in direction bottom. Is there a nice turnaround trick?
Thanks!!!
You also need to move the view up by the amount of height you have increased (progressBarBckgrHeightPiece)
Also, you should set the frame directly.
progressBarOutlet.frame = CGRect(x: progressBarOutlet.frame.origin.x,
y: progressBarOutlet.frame.origin.y - progressBarBckgrHeightPiece, // Notice here
width: progressBarOutlet.frame.width,
height: progressBarOutlet.frame.height + CGFloat(progressBarBckgrHeightPiece))

How to center the subviews of UIScrollView

I'm a beginner in creating a custom view. I'm trying to create a custom UIView with a scrollview and buttons that will look like this:
I'm adding a view(view with label of page number) inside of scrollView depending on the the number of pages. Is that how it should be?
Currently it looks like this:
My question is how can I center the subviews of scrollview? and next is what's wrong with this code? Why is that I can only see 1 label inside the view? and the other doesn't show up. How can I scroll to the selected page if the page number is not visible already in the scrollview?
Here's my code:
func addPageNumberViewWithCount(count: Int) {
var pageNumberViewX: CGFloat! = 0
let pageNumberViewDistance: CGFloat! = 10
for i in 1...count {
let pageNumberView = UIView(frame: CGRectMake(pageNumberViewX, 0, 30, 30))
pageNumberView.backgroundColor = UIColor.redColor()
pageNumberView.layer.cornerRadius = pageNumberView.frame.height / 2
pageNumberView.layer.masksToBounds = true
pageNumberView.clipsToBounds = true
// add number label
let label = UILabel(frame: CGRectMake(pageNumberViewX, 0, 30, 30))
label.center = pageNumberView.center
label.text = "\(i)"
label.textAlignment = .Center
pageNumberView.addSubview(label)
// update x for next view
pageNumberViewX = pageNumberView.frame.origin.x + pageNumberView.frame.width + pageNumberViewDistance
// add view inside scrollview
scrollView.addSubview(pageNumberView)
if i == count {
scrollView.contentSize = CGSizeMake(pageNumberViewX + pageNumberView.frame.width, 30)
}
}
}
Part of my answer will go to providing a solution to your question,and another part of my answer will go toward strongly suggesting that this not be the method you use to complete your desired tasks.
At this point, AutoLayout and Interface Builder have come a long way. Where they used to be difficult to use because of their inconsistency and unpredictability, they are now highly predictable and consistent as long as you understand the tools and how to use them.
Apple's suggested method for completing this task (which I mostly stand behind) is creating a .xib file (nib) to lay out the base components of the design, and to load the nib into the view or view controller whenever that design should be used. My question for you: have you tried this, or have you determined for some reason that this would be an unsatisfactory solution to your problem? AutoLayout exists to solve these problems not just in allowing you to achieve your desired solution in this one situation but to achieve it in other situations as well, with varying screen sizes and device types.
Now, if you were to simply ignore all of that and continue on your path, there would be a few good ways to handle your problem. One suggested solution I have:
1) Wrap your pageNumberView in another view. Constrain that view to the size of the scrollView. Doing this gives the scrollView content with which to base its scrollable content size, and gives the inner pageNumberView something to compare itself to.
2) Center the pageNumberView horizontally in its container (the new view that we just created).
Doing this, the page numbers should now center themselves in the container until they reach a size where they exceed the width of the scrollView. At that point, they will then continue to expand, making the area horizontally scrollable.
I can provide code examples of how you would do this, but frankly I would much prefer if you scrapped the idea of doing things this way and instead opted for the AutoLayout method at least, and perhaps even the Interface Builder method. I started out with iOS the same way you did, trying to do everything in code. It really isn't the best way to do things, at least with regard to iOS.
Edit: I've provided an example of how this would look in Interface Builder using UINib. I've populated the view with an example of 5 pages to show what it is like. I will see if I can make a GIF or something similar to show what each of the subviews look like.
For the OP, my suggestion would be this: Use this for reference, and go learn the constraints system. It is extremely unlikely that you will find success with iOS if you do not learn and utilize the constraints system. Coding in X values to a UIView's frame is only going to create a product with poor, inconsistent performance across devices, and will take much, much longer than it would to take the time to learn constraints.
Perhaps you should have a UICollectionView with a cell for each of these buttons. That's a better way of doing this, and you can lay it out again when the screen rotates and it changes width.
Those cells will layout offset to the left. You can solve that this way:
let pageNumberViewTotalWidth = 30 * count + (pageNumberViewDistance * count - 1)
self.collectionView.contentInset.left = (self.collectionView.frame.size.width - pageNumberViewTotalWidth) / 2
The labels aren't showing up because you're setting their frame's x to be the same as the page number view's x. It's frame should be relative to it's superview, in this case pageNumberView.
First Question of yours "how can I center the subviews of scrollview?"
Solution: lets suppose you have in total 50 pages and you want to show 5 pages at a time in the scrollview.
Then make 10 subviews of equal widths where each subview width will be equal to visible portion of the collection view that is
self.view.size.width - 2*(width of toggle button)
Then in each container view add 5 of your pageNumberView placed at equal distance
lets pageNumberViewWidth = container.width/5 - 2*margin
now pageNumberView frame will be (margin,0,pageNumberViewWidth,height)
In this way in each container view your pageNumberViews will be placed equally and it will look as if you have centred them.
Second Question "Why is that I can only see 1 label inside the view?"
Answer : Its because you are setting label frame incorrectly
let label = UILabel(frame: CGRectMake(pageNumberViewX, 0, 30, 30))
Here label is the subview of pageNumberView So you have to set its frame according to its parent's view which is pageNumberView, so change it to
let label = UILabel(frame: CGRectMake(0, 0, 30, 30))
First time it was right because pageNumberViewX is 0 for first iteration after that it become some positive value which makes its frame shifted to right but its parent's width is small so its not visible to you.
Third Question : "How can I scroll to the selected page if the page number is not visible already in the scrollview?"
For this you need to find the frame of your selected page:
you can do that by using the offset that you used to create pageNumberView.
(width of each pageNumberView)*pageNumber = starting point of the required pageNumberView.
let frame : CGRect = CGRectMake(calculated offset above, 0,30, 30)
//where you want to scroll
self.scrollView.scrollRectToVisible(frame, animated:true)
I hope this will help you in solving your problem
Edit for first problem
func addPageNumberViewWithCount(count: Int) {
var containerViewX: CGFloat! = 0
let pageNumberViewDistance: CGFloat! = 10
let pageNumberViewPerSubview = 5
var numberOfSubview = count/pageNumberViewPerSubview
if(count % pageNumberViewPerSubview > 0){
numberOfSubview = numberOfSubview + 1
}
var pagesLeft = count
for i in 1...numberOfSubview {
var pageNumberViewX: CGFloat! = 0
let containerView : UIView = UIView(frame:CGRectMake(containerViewX,0,scrollView.frame.size.width,scrollView.frame.size.height))
if(pagesLeft < pageNumberViewPerSubview){
for k in 1...pagesLeft{
}
}
else{
for j in 1...pageNumberViewPerSubview{
let pageNumberView = UIView(frame: CGRectMake(pageNumberViewX, 0, 30, 30))
pageNumberView.backgroundColor = UIColor.redColor()
pageNumberView.layer.cornerRadius = pageNumberView.frame.height / 2
pageNumberView.layer.masksToBounds = true
pageNumberView.clipsToBounds = true
// add number label
let label = UILabel(frame: CGRectMake(0, 0, 30, 30))
label.text = "\(i)"
label.textAlignment = .Center
pageNumberView.addSubview(label)
// update x for next view
pageNumberViewX = pageNumberView.frame.origin.x + pageNumberView.frame.width + pageNumberViewDistance
containerView.addSubview(pageNumberView)
}
containerViewX = containerViewX + scrollView.frame.size.width
// add view inside scrollview
scrollView.addSubview(containerView)
pagesLeft = pagesLeft - pageNumberViewPerSubview
}
if i == count {
scrollView.contentSize = CGSizeMake(numberOfSubview*scrollView.frame.size.width, 30)
}
}
}

After changing my UItableView's Height, my scroll does not work again. Cant reach data at the bottom

So I am currently very new to ios programming and I am having a bit of an issue with my table view. First I call my web API to get the data I required to populate my table View. Using the data I am able to calculate the number of rows and sections that would exist on my table view. After that I calculate the correct height of my table view. I change the height of my table view and reload the table. It looks like this
func tableViewHight(numberOfRows : Int)
{
let sectionHeight = CGFloat(30) * CGFloat(numberOfSections - 1)
//This is the height of all the sections in my tableview put together(except the first section since its height will always be 0)
let tableviewMaximumHeight = UIScreen.mainScreen().bounds.size.height - self.myTableView.frame.origin.y
//Maximum height would be the distance from the y position of my table view, all the way to the bottom of the device.
if(tableviewMaximumHeight <= (cellRowHeight * CGFloat(numberOfRows) + sectionHeight))
{
self.myTableView.frame.size.height = tableviewMaximumHeight
}
else
{
self.myTableView.frame.size.height = cellRowHeight * CGFloat(numberOfRows) + sectionHeight
}
self.myTableView.reloadData()
}
My table view is able to change height and reload data perfectly. The only Problem is that I am not able to reach the bottom of my table view. I do not know what else to do. I have already check that my table view has
myTableView.scrollEnabled = true
myTableView.scrollTop = false
If you guys have any advice, I would appreciate it :).
The problem when you manually set the height of a UITableView is that you'll then also have to manually set its content height.
Use Autolayout instead to set the height.
Have an IBOutlet for an NSLayoutConstraint variable (say, tableHeightConstraint) that sets the height of your table, then, in your code:
tableHeightConstraint.constant = tableviewMaximumHeight
or
tableHeightConstraint.constant = cellRowHeight * CGFloat(numberOfRows) + sectionHeight
I found the main root of my problem. The issued lied in my maximumTableviewHeight variable. The problem was that I would the value for this variable in the view did load and apparently the y position of the my table view would always give zero in the view did load. Once the maximum table view height was resolve. The application worked like a a charm.

iCarousel - How to shift the focus to the left side

I need to implement iCarousel in a way so that it looks like this:
I have done all the modifications at my end to make it look like this but now the problem is that iCarousel works with the center image in the focus. Can I make it so that if there are only two images, they don't appear in the center but rather on the left? Think about it like the "left indented" content.
How do I do this?
iCarousal provide a property to sift focus left or right by set the width offset.
For right side use this code for swift 2.3 -
let widthOffset: Float = -120.0
self.customCarousel.viewpointOffset = CGSize(width: CGFloat(widthOffset), height: CGFloat(0))
This should be the property in iCarousel you are looking for viewpointOffset
Example...
// Modifiy widthOffset so it works well in your case
float widthOffset = 100.0;
[_yourCarousel setViewpointOffset:CGSizeMake(widthOffset, 0)];
The viewpointOffset property doesn't really work that way. It will let you align to one edge, but then when you scroll to the end you'll have the same problem.
iCarousel wasn't designed to do this. I suggest you switch to SwipeView instead (http://github.com/nicklockwood/SwipeView) which has the same delegate/datasource interface, but has an alignment property that you can use to set edge alignment, like this:
swipeView.alignment = SwipeViewAlignmentEdge;
Visit https://github.com/nicklockwood/iCarousel/issues/142
it may help.
nicklockwood (author) answer:
You could use the delegate and create a custom transform that offsets all of your views by a negative amount. Something like this:
- (CATransform3D)carousel:(iCarousel *)carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform
{
float OFFSET_WIDTH = ...; // set this to about half the width of your view
return CATransform3DMakeTranslation(offset * itemWidth - OFFSET_WIDTH, 0, 0);
}
The only problem would be that when you reach the rightmost end of the
carousel there would be a gap, but you could use placeholder views to
fill the gap up.
I tripped over the same problem. After simple math I've found a more accurate answer. I've put this code in my viewDidAppear() function, because when I put it in viewDidLoad() the view.frame.width and view.frame.height took the original width of my storyboard, which didn't fit to bigger devices.
My use case was the following. My iCarousel items are simple squares. That means, I have for example a height of 3 and a width of 3. But the width of my iCarousel view is 5. In sum I have 5-3=2 space, 1 left and 1 right (because we know it's default is centered). So last step is to divide with 2 to get the width on one site = 1. With this knowledge we just have to set the viewPointOffset to 1 (in this case).
Final formular: let widthOffset = (view.frame.width - view.frame.height) / 2
Example in my code:
let widthOffset: Float = Float((mDetailCarousel.frame.width - mDetailCarousel.frame.height) / 2 )
mDetailCarousel.viewpointOffset = CGSize(width: CGFloat(widthOffset), height: CGFloat(0))

UIPageController currentPage stubbornly sets itself to nib value

I have a UIPageController that I placed in my xib file, so I could get the placement proper, but the value of "currentPage" set in the xib is the value that currentPage assumes when i try to set it to something else, like below, the line of code that sets my UIScrollView to the proper initial image when starting in the middle (i.e. not page 0):
aScrollView.contentOffset = CGPointMake((currentIndex + 0.0) * screenWidth, 0.0);
which calls
-(void) scrollViewDidScroll: (UIScrollView*) aScrollView
{
// Update the page control to match the current scroll:
CGPoint offset = aScrollView.contentOffset;
float width = self.view.frame.size.width;
int pageNumber = (int)(offset.x / width);
uiPageControl.currentPage = pageNumber;
NSLog(#"%g // %g = %d", offset.x, width, (int)(offset.x/width));
}
The pageNumber integer is correct, so I am properly calculating the desired page number from the offset. However, when I try to manually (programatically) set currentPage, it doesn't set to pageNumber, it sets to the value in the xib.
Easy solution is to not use the xib and do it all programatically, but I want to know how I can continue using the UIPageController in the xib, because it was easy to place without knowledge of coordinates, etc. Normally, Attributes set in the xib are overwritable programatically, so this bug really surprised me. Any suggestions on how I can properly set currentPage?
Thanks for your help!
I found a hack that temporarily solves my problem: in the xib, I set the "Number of Pages" value to something I know will be higher than the number of pages I will ever have.
If this is too small, it turns out that setting currentPage beyond that value didn't do anything because it was clamping or setting it to some number below the number of pages I had in the xib.
If I set this xib attribute higher, then setting currentPage works.
Sorry for the quick trigger.

Resources