I am trying to create a cover flow image slide something like this :
I have to say I don't want to use others framework like iCarousel I need to write my own code. here is my codes but it only shows me one image per page , I was wondering how can I change my code to add previous and next image like the image ?
override func viewDidLoad() {
super.viewDidLoad()
pageControl.numberOfPages = imageArray.count
scrollView.frame = self.view.frame
scrollView.contentSize = CGSize(width: scrollView.frame.size.width * CGFloat(pageControl.numberOfPages) , height: 0)
scrollView.delegate = self
for i in 0..<imageArray.count {
ashvanImage = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width - 50 , height: self.view.frame.height - 200))
ashvanImage.center = scrollView.center
ashvanImage.contentMode = .scaleAspectFit
ashvanImage.image = UIImage(named: "\(i).jpg")
scrollView.addSubview(ashvanImage)
createPageWith(images: ashvanImage, page: i)
}
}
func createPageWith(images:UIImageView, page:Int) {
let newView = UIView(frame: CGRect(x: scrollView.frame.size.width * CGFloat(page), y: 0, width: scrollView.frame.size.width, height: scrollView.frame.size.height))
newView.addSubview(images)
scrollView.addSubview(newView)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let page = scrollView.contentOffset.x / scrollView.frame.size.width
pageControl.currentPage = Int(page)
}
As others have said, if you don't want to use iCarousel, which has CoverFlow built in, you should use a UICollectionView. It is built for what you want.
Certainly you could do this starting from a UIScrollView, but it would probably take an experienced developer several weeks of full-time work to get a clean design working and debugged, and the result would look and feel a whole lot like a collection view when you were done, only not as flexible or as maintainable. Plus you'd need pretty advanced knowledge of Core Animation, which is pretty specialized and not very well documented.
EDIT:
A Google search on "UICollectionView CoverFlow Swift" found this framework on Github:
https://github.com/sumitlni/LNICoverFlowLayout
If you're determined to do this yourself, you could at least look over that framework as a road map for what you'd need to do.
You are set scrollview's width equal to the whole view, make it less like 85% or 90% or according to your requirement.
Which will solve half of your problem.
By looking at the design i think you want the view to animate.
So first, turn off scrolling for the scrollview.
Then add 2 gesture's on the scrollview, one for right swipe and another for left swipe.
Then scroll the view 1 page at a time according to the gesture and animate the view's size by changing its x position by 1 page and decreasing its size and increasing the incoming one's.
You can use the default method UIScrollView.animate too.
Related
I have created an UIActivityIndicatorView in my UITableViewController in Swift like so:
indicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) as UIActivityIndicatorView
indicator.center = self.view.center
indicator.hidesWhenStopped = true
indicator.style = UIActivityIndicatorView.Style.white
indicator.backgroundColor = UIColor(white: 0.0, alpha: 0.6)
indicator.layer.cornerRadius = 15
self.view.addSubview(indicator)
self.indicator.startAnimating()
And this works like a charm, but when its running and I scroll on my UITableView Controller the UIActivityIndicatorView does not scroll with it. How do I get the UIActivityIndicatorView to scroll with the UITableViewController.
You can add it to the window
let wind = (UIApplication.shared.delegate as! AppDelegate).window!
wind.addSubview(indicator)
To remove
indicator.removeFromSuperview()
You can also make it inside the vc with
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
indicator.frame.origin = CGPoint(x: indicator.frame.origin.x, y: UIScreen.main.bounds.height / 2 - 50 + scrollView.contentOffset.y)
}
Your question and the title seem to ask different things. In the question you ask how to make it move with the scrolling, in the title you ask how not to make it scroll.
There are a few ways to do this (scrolling).
One way is to implement func scrollViewDidScroll(_ scrollView: UIScrollView) { } and move the activity indicator when the scroll view (a UITableView is a scrollView after all) scrolls. An other way (which I personally like more) is to put the activity indicator in a table view cell. Cells always scroll with the table view.
You need to do is set indicator.center to the center of its superview's bounds. Those values are in the same coordinate system (here, cell.like's coordinate system).
indicator.setCenter(CGPointMake(CGRectGetMidX(cell.like.bounds),
CGRectGetMidY(cell.like.bounds)));
I think that the indicator behind UITableView. Do you try to bring indicator to the front?
self.view.bringSubview(toFront: indicator)
Actually it scrolls without any problem, so I think there is no error in your code..
I have two (possibly more) views in a UIScrollView and want to use paging with it. The problem arises when I try to use the default Paging option for UIScrollView, since the views have different widths it can not page properly.
So I have implemented a custom paging code which works. However, when the scrolls are slow, it does not function as expected. (It goes back to the original position without animation.)
Here is how I currently do the custom paging through the UIScrollViewDelegate
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if direction == 1{
targetContentOffset.pointee.x = 0
}else{
targetContentOffset.pointee.x = 100
}
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
if scrollView.panGestureRecognizer.translation(in: scrollView.superview).x > 0 {
direction = 1
}
else {
direction = 0
}
}
What I want:
What I have:
try to below example for Custom UIScrollView Class
import UIKit
public class BaseScrollViewController: UIViewController, UIScrollViewDelegate {
public var leftVc: UIViewController!
public var middleVc: UIViewController!
public var rightVc: UIViewController!
public var initialContentOffset = CGPoint() // scrollView initial offset
public var maximumWidthFirstView : CGFloat = 0
public var scrollView: UIScrollView!
public class func containerViewWith(_ leftVC: UIViewController,
middleVC: UIViewController,
rightVC: UIViewController) -> BaseScrollViewViewController {
let container = BaseScrollViewViewController()
container.leftVc = leftVC
container.middleVc = middleVC
container.rightVc = rightVC
return container
}
override public func viewDidLoad() {
super.viewDidLoad()
setupHorizontalScrollView()
}
func setupHorizontalScrollView() {
scrollView = UIScrollView()
scrollView.isPagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.bounces = false
let view = (
x: self.view.bounds.origin.x,
y: self.view.bounds.origin.y,
width: self.view.bounds.width,
height: self.view.bounds.height
)
scrollView.frame = CGRect(x: view.x,
y: view.y,
width: view.width,
height: view.height
)
self.view.addSubview(scrollView)
let scrollWidth = 3 * view.width
let scrollHeight = view.height
scrollView.contentSize = CGSize(width: scrollWidth, height: scrollHeight)
leftVc.view.frame = CGRect(x: 0,
y: 0,
width: view.width,
height: view.height
)
middleVc.view.frame = CGRect(x: view.width,
y: 0,
width: view.width,
height: view.height
)
rightVc.view.frame = CGRect(x: 2 * view.width,
y: 0,
width: view.width,
height: view.height
)
addChildViewController(leftVc)
addChildViewController(middleVc)
addChildViewController(rightVc)
scrollView.addSubview(leftVc.view)
scrollView.addSubview(middleVc.view)
scrollView.addSubview(rightVc.view)
leftVc.didMove(toParentViewController: self)
middleVc.didMove(toParentViewController: self)
rightVc.didMove(toParentViewController: self)
scrollView.contentOffset.x = middleVc.view.frame.origin.x
scrollView.delegate = self
}
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.initialContentOffset = scrollView.contentOffset
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if maximumWidthFirstView != 0
{
if scrollView.contentOffset.x < maximumWidthFirstView
{
scrollView.isScrollEnabled = false
let newOffset = CGPoint(x: maximumWidthFirstView, y: self.initialContentOffset.y)
self.scrollView!.setContentOffset(newOffset, animated: false)
scrollView.isScrollEnabled = true
}
}
}
}
Use of BaseScrollViewController
let left = FirstController.init()
let middle = MiddleController()
let right = RightController.init()
let container = BaseScrollViewController.containerViewWith(left,middleVC: middle,rightVC: right)
container.maximumWidthFirstView = 150
Output:
GitHub gist Example code: https://gist.github.com/mspvirajpatel/58dac2fae0d3b4077a0cb6122def6570
I have previously written a short memo about this problem, and I'll copy/paste it since it is no longer accessible from anywhere. This may not be a specific answer and the codes are pretty old, but I hope this would help you in some degree.
If you have used a paging feature included in UIScrollView, you might also have tempted to customize the width of each page instead of a default, boring, frame width paging. It would be great if you can make the scroll stop at shorter or longer intervals than just multiples of its frame width. Surprisingly, there's no built-in way to configure the width of pages even in our latest iOS7 SDK. There are some ways to achieve custom paging, but none of them I would say are complete. As for now, you'll have to choose either of the following solutions.
1. Change the frame size of your UIScrollView
Alexander Repty has introduced a nice and easy solution to this problem and also included a sample code through his blog: http://blog.proculo.de/archives/180-Paging-enabled-UIScrollView-With-Previews.html
Basically, the instruction can be watered down to the following steps:
Create UIView subclass and override hitTest: withEvent:.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if ([self pointInside:point withEvent:event]) {
if ([self.subviews count] == 0) return nil;
else return [self.subviews lastObject];
}
return nil;
}
Include UIScrollView as a subview of the above UIView subclass.
Adjust the frame size of your UIScrollView.
Set clipsToBound property of your scroll view to NO.
Set pagingEnabled property of your scroll view to YES.
As you can see, I've just assumed that there is only one subview (the scrollView!) to your UIView subclass. Since you are passing all the touch events occurred in the UIView subclass to your UIScrollView, you'll be able to scroll the content by panning on the UIView subclass, but the paging width will be decided by the width of UIScrollView's frame.
The best part of this approach is that you'll get the genuine feeling and responsiveness, as it is somewhat hard to mimic the paging by using UIScrollView delegate methods.
The only problem I found using this solution is that the width of all pages will have to be identical. You can't set different widths to different pages. If you tries to change your scrollView's frame size dynamically, you'll find there're a number of new emerging problems to deal with. Before trying to fix these glitches, you may want to check out other two solutions using UIScrollView delegates.
2. scrollViewWillEndDragging: withVelocity: targetContentOffset
scrollViewWillEndDragging: withVelocity: targetContentOffset is one of the latest UIScrollView delegate methods(iOS 5.0 or up) that gives you more information than the other old ones.
Since you get the velocity of the scrollView right after you lift the finger up from the screen, we can figure out the direction of the scrolled contents. The last argument, targetContentOffset, not only gives you the expected offset when the scrolling stops eventually, you can also assign CGPoint value in order to let the scrollView scrolls to the desired point.
targetContentOffset = CGPointMake(500, 0);
or
targetContentOffset->x = 500;
However, this will not work as you would think it should because you cannot set the speed of scrolling animation. It feels more like the scrollView happens to stop at the right point rather than it snaps to the spot. I also have to warn you that manually scrolling the contents with setContentOffset: animated: or just by using UIView animation inside the method will not work as expected.
If the velocity is 0, however, you may(and you have to) use manual scrolling to make it snap to the nearest paging point.
It could be the simplest and the most clean approach among all, but the major downside is that it does not provide the same experience that you always had with the real paging feature. To be more honest, it's not even similar to what we call paging. For the better result, we need to combine more delegate methods.
3. Use multiple UIScrollView delegate methods
From my shallow experience, an attempt to scroll your scrollView manually inside any UIScrollView delegate methods will only work when your scrollView has started to decelerate, or when it's not scrolling at all. Therefore, the best place I've found to perform the manual scrolling is scrollViewWillBeginDecelerating:.
Before looking inside the sample code, remember scrollViewEndDragging: withVelocity: targetContentOffset: method will always called prior to scrollViewWillBeginDecelerating:.
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
_scrollVelocity = velocity.x;
if (_scrollVelocity == 0) {
// Find the nearest paging point and scroll.
}
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
if (_scrollVelocity < 0) {
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
scrollView.contentOffset = // Previous page offset
} completion:^(BOOL finished){}];
} else if (_scrollVelocity > 0) {
// Animate to the next page offset
}
}
_scrollVelocity is meant to be a global variable or a property, and I've assumed that you have your own ways to decide paging offsets for each page. Note that you'll have to handle the case of zero velocity inside the upper method because the latter method will not be called.
UIView animation with the duration 0.3 and the EaseOut curve option gave me the best result, but of course you should try other combinations to find what's the best for you.
This not the exact solution you might be looking for.
1) Check the offset of the scrollView when it reaches 0, You could show the VIEW you have above , You could animate while checking the scrollview movement so that it looks nice .But not completely
2) Now the VIEW is partially above your camera(you can decrease it alpha so that scrollview is still visible).
3) user can tap the view and you can show it completely.
You may want to consider calculating the most visible cell in your collection view after dragging ends and then programmatically scroll to – and center – that cell.
So something like:
First, implement the scrollViewDidEndDragging(_:willDecelerate:) method of your collection view's delegate. Then, in that method's body, determine which cell in collectionView.visibleCells is most visible by comparing each of their centers against your collection view's center. Once you find your collection view's most visible cell, scroll to it by calling scrollToItem(at:at:animated:).
I seem to be having some issues getting the UIPageControl to work.
I have a ViewController that holds a ScrollView. This ScrollView loads nib files that can be swiped. See image:
Here is the code that loads these:
self.addChildViewController(vc0)
self.displayReportView.addSubview(vc0.view)
vc0.didMoveToParentViewController(self)
var frame1 = vc1.view.frame
frame1.origin.x = self.view.frame.size.width
vc1.view.frame = frame1
self.addChildViewController(vc1)
self.displayReportView.addSubview(vc1.view)
vc1.didMoveToParentViewController(self)
// And so on
...
This works fine as in they scroll correctly etc..
Now, on the ViewController (one holding the scrollview) I added the delegate:
UIScrollViewDelegate
created some variables:
var frame: CGRect = CGRectMake(0, 0, 0, 0)
var colors:[UIColor] = [UIColor.redColor(), UIColor.blueColor(), UIColor.greenColor(), UIColor.yellowColor()]
var pageControl : UIPageControl = UIPageControl(frame: CGRectMake(50, 300, 200, 20))
I added some functions that are needed:
func configurePageControl() {
// The total number of pages that are available is based on how many available colors we have.
self.pageControl.numberOfPages = 4
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.redColor()
self.pageControl.pageIndicatorTintColor = UIColor.blackColor()
self.pageControl.currentPageIndicatorTintColor = UIColor.greenColor()
self.view.addSubview(pageControl)
}
// MARK : TO CHANGE WHILE CLICKING ON PAGE CONTROL
func changePage(sender: AnyObject) -> () {
let x = CGFloat(pageControl.currentPage) * displayReportView.frame.size.width
displayReportView.setContentOffset(CGPointMake(x, 0), animated: true)
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
Now, When I run the app the scrollview dots show, but when I swipe they do not update.
Question
How do I update the dots to reflect what view is showing?
let me know if you need anything else from my code to see functionality.
You can certainly do what you're describing, if you have a paging scroll view; I have an example of it that uses this code:
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let x = scrollView.contentOffset.x
let w = scrollView.bounds.size.width
pageControl.currentPage = Int(x/w)
}
Except for your round, that looks a lot your code, which makes me think that your code should work. That makes me think that something else is just misconfigured. Is this a paging scroll view? Did you remember to make this object your scroll view's delegate? Use logging or a breakpoint to be certain that your scrollViewDidEndDecelerating is even being called in the first place.
However, I would just like to point out that the configuration you are describing is effectively what UIPageViewController gives you for free — a scroll view with view controller views, plus a page control — so you might want to use that instead.
I would replace the scroll view with a UICollectionView. This way you get paging for free, and it will be better, because paging will work out of the box, without you having to calculate the frame offsets.
Be sure to set collectionView.pagingEnabled = true
To get the current page number, do collectionView.indexPathsForVisibleItems().first?.item
To change the page:
collectionView.scrollToItemAtIndexPath(newIndexPath, atScrollPosition: CenteredHorizontally, animated: true)
So I am using a UITableView to display information about different films.
At the top of the VC, I have a UIImage which sits inside of a UIView. And then my table sits underneath. The table currently sits right up against the bottom of the image (which is what I want), see below:
The Issue
I followed a tutorial to add a simple effect, so when the user pulls down on the tableView, the image enlarges. You can see what I mean by seeing the tutorial here: See here
This all worked wonderful and gave my the effect I wanted, however, it's now added an empty space below the image, see the image below:
Everything still works fine, and the effect works as expected, but this space is now there - which I really don't want.
The settings in the storyboard for this VC are set as followed:
The code I added to make the effect is as follows:
private let KTableHeaderHeight: CGFloat = 160.0 // which is the height of my UIImage
var headerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
headerView = tableView.tableHeaderView
tableView.tableHeaderView = nil
tableView.addSubview(headerView)
tableView.contentInset = UIEdgeInsets(top: KTableHeaderHeight, left: 0, bottom: 0, right: 0)
tableView.contentOffset = CGPoint(x: 0, y: -KTableHeaderHeight)
updateHeaderView()
}
And then:
func updateHeaderView() {
var headerRect = CGRect(x: 0, y: -KTableHeaderHeight, width: tableView.bounds.width, height: KTableHeaderHeight)
if tableView.contentOffset.y < -KTableHeaderHeight {
headerRect.origin.y = tableView.contentOffset.y
headerRect.size.height = -tableView.contentOffset.y
}
headerView.frame = headerRect
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
updateHeaderView()
}
If I comment out all the code I've added, it then looks fine again, so I'm guessing it is this code that's causing the space.
I'm really keen to understand why this gap has formed, and how I can remove it, still using the code added to make the image enlarging effect.
Update
My UIImageView layout Attributes:
Your issue is likely the private let KTableHeaderHeight: CGFloat = 160.0 line, which doesn't equal the height of the imageView in the header.
You need to find out the fixed height of the imageView after scaling, which you can get by multiplying the original image height by view width/original image width, and set the imageView height and private var KTableHeaderHeight: CGFloat to that value.
If your using a grouped UITableView then it makes a space for the group, not sure how to get rid of it. You might want to consider making it a plan UITableView and then create the sections headers that you want via the delegate
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
// create UIView here
}
However, after playing with the sample project, it seems like you can adjust your kTableHeightHeader variable in the top to adjust to hide the top section header.
I modified the sample project so you can see it here
Swift code:
tableView.tableFooterView = UIView()
Objective C code:
tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
I have UICollectionView that contains cells with large images. Scrolling was smooth until I added (small) UIVisualEffectView to each cell. Now, scrolling performance is awful.
Here is all the code that does something with that UIVisualEffectView the code:
class ThemeCardCell: UICollectionViewCell {
private let priceTagEffectView = UIVisualEffectView()
override func layoutSubviews() {
super.layoutSubviews()
if priceTagEffectView.superview == nil {
priceTagEffectView.effect = UIBlurEffect(style: UIBlurEffectStyle.Light)
priceTagEffectView.frame = CGRect(x: bounds.width - priceTagMargin.width - 80, y: priceTagMargin.height, width: 80, height: 40)
priceTagEffectView.opaque = true
addSubview(priceTagEffectView)
}
}
}
What can I do to improve scrolling performance?
Don't use UIVisualEffectView in this way, that's what you can do. Apple has given lots of info about animation / scrolling performance and that sort of thing is at the top of the list of what not to do. And visual effect views are the worst; they don't merely blur an image - they are performed at a much higher point in the render chain, and you are forcing them to re-render on every frame (hence the problem).