UIActivityIndicatorView goes away when scrolling on UITableView - ios

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..

Related

Swift 4 UIActivityIndicatorView not centered on UITableView

I am trying to center my UIActivityIndicatorView in a UITableView, this is how I am creating my UIActivityIndicatorView:
indicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) as UIActivityIndicatorView
//Set the activity indictor center
indicator.center = self.view.center
//Hide the indicator when its stopped.
indicator.hidesWhenStopped = true
//Set the style of the activity indicator
indicator.style = UIActivityIndicatorView.Style.white
//Set the background colour of the activity indicator
indicator.backgroundColor = UIColor(white: 0.0, alpha: 0.6)
//Make the activity indicator have rounded corners
indicator.layer.cornerRadius = 15
//Add activity indicator to view
self.view.addSubview(indicator)
//Start activity indicator
self.indicator.startAnimating()
But when I scroll up on my UITableView I can't see my UIActivityIndicatorView, I have tried the following:
override func viewWillLayoutSubviews() {
self.indicator.center = self.view.center
}
But that did not work.
I have also tried:
override func viewWillLayoutSubviews() {
self.indicator.translatesAutoresizingMaskIntoConstraints = true
self.indicator.frame = self.view.bounds
self.indicator.center = self.view.center
}
Also did not work, what am I doing wrong?
This is happening when I scroll down, then select an item in my table view then the activity indicator will appear.
You can't center inside the UITableViewController, as it is a scroll view, and the scroll view is the View Controller's view. However, you can center it inside the window's view like so:
// get the visible window
let window = UIApplication.shared.keyWindow!
let viewActivity = UIActivityIndicatorView(style: .whiteLarge)
// set your activity indicator's center to the center of the screen
viewActivity.center = window.center
viewActivity.hidesWhenStopped = true
// add the indicator to the active window (not your uitableviewcontroller)
// and make sure it is at the front (so it is visible)
window.addSubview(viewActivity)
window.bringSubviewToFront(viewActivity)
viewActivity.startAnimating()
You could also accomplish this by grabbing your root Navigation, or tab view controller, and centering it inside there, as well.
I had a situation where everything is fine on the UIViewController, but there were problems with the UIActivityIndicatorView on the UITableViewController.
Please pay attention to the proposed solution. In my case, activityView is stretched to the whole form and this solution helped me out.
https://stackoverflow.com/a/41886375/7938405

Smooth custom paging for UIScrollView

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:).

Create cover flow using UIScrollView

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.

UIActivityIndicatorView Not Appearing After Scrolling UITableView

I have a table view controller and I display a UIActivityIndicatorView in the center of the screen in viewDidLoad()
let bounds = UIScreen.mainScreen().bounds;
let width = bounds.size.width
let height = bounds.size.height
loadingIndicator = UIActivityIndicatorView(frame: CGRectMake(width / 2, height / 2, 75, 75))
loadingIndicator.layer.cornerRadius = 10
loadingIndicator.backgroundColor = kfbBlue
loadingIndicator.center = CGPointMake(width / 2, height / 2 - 37)
loadingIndicator.activityIndicatorViewStyle = .WhiteLarge
loadingIndicator.hidesWhenStopped = true
self.tableView.addSubview(loadingIndicator)
loadingIndicator.startAnimating()
This works just fine. I have a reload method for reloading the table view data. reload() works, but if I press the button that calls this method after I have scrolled the table view, the activity indicator does not appear. It only appears if the table view is scrolled up to the top.
func reload() {
loadingIndicator.startAnimating()
self.beginParsing()
}
How can I get the activity indicator to appear in the center regardless of how far the table view has been scrolled?
Don't add the UIActivityIndicatorView to the UITableView. Add it to the center pf the parent view that holds the UITableView. You are adding it to the UITableView's content view, which will scroll. Add it outside that if you want it to persist even when the UITableView has been scrolled. Most likely something like this:
self.view.addSubview(loadingIndicator)
instead of your line
self.tableView.addSubview(loadingIndicator)
If you are using a UITableViewController, the topmost view will be the TableView, so you that won't work. You will instead have to adjust the center of the activity indicator to take into account the scroll location:
loadingIndicator.center = CGPointMake(width / 2, self.tableView.contentOffset.y + height / 2 - 37)

How to make swipe back in left side in single view

I made more than one view without taking new Controller class , I used UIgestureRecognizer but it not look better.
I want to swipe same like ios device, when we start to swipe from left side corner.
You can implement this with scrollview.
Steps:
1)First place a scrollview that fills the full screen.
2)Next create a container view which is as big as to hold your two views.
let containerSize = CGSize(width: 640.0, height: 640.0)
containerView = UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0),size:containerSize))
scrollView.addSubview(containerView)
3) Now you create your view 1 , view2 and add them to container view.
4) Set the scrollview content size to container size
scrollView.contentSize = containerSize;
5) Then set the scrollview delegate method
func viewForZoomingInScrollView(scrollView: UIScrollView!) -> UIView!{
return containerView
}

Resources