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
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..
This question asked to be implemented in Swift 4, iOS 11
Is there any way to make every subview of ViewController's view to be pushed down when it is under UINavigationBar?
If navigation bar is NOT TRANSLUCENT the subview is under it. This is what I want.
Desired Result
But when navigation bar is TRANSLUCENT the subview is lying under it. I dont want it. I want the subview is pushed down just be like if navigation bar is not translucent.
Undesired Result
I create the view programmatically :
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.red
let navBar = (self.parent as? UINavigationController)?.navigationBar
navBar?.isTranslucent = true
}
func makeChildView() {
let myframe = CGRect(x: 0, y: 10, width: self.view.frame.width, height:
self.view.frame.height * 0.4)
let view = UIView(frame: myframe)
view.backgroundColor = UIColor.green
self.view.addSubview(view)
}
Using Autolayout
I am able to solve this problem using autolayout. But I just want to know how to achieve this result without autolayout if possible. Is there any other approach?
Swift 3.x
navBar?.isTranslucent = true
self.automaticallyAdjustsScrollViewInsets = false
Add this line & you are good to go.
Situation: I've got a UITableViewController loading some data asynchronously from a service. During this time I would like to place a full screen (except navigation bar) view over the table view showing my custom indicator and text.
Problem: The problem I'm facing is that when my custom view (it has a red background) is placed over the UITableView the lines of the table view are shown trough my custom view (see image below).
What I tried:
I tried to use insertBelow and above, didn't work. I also tried to do: tableview.Hidden = true, but this also hides the custom view for some reason as seen on image 2.
Image1: For some reason I can see the lines threw my view.
Image 2: Tableview + custom view gone when hidden = true used.
My code:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
UIView view = new UIView (new RectangleF (0, 0, this.TableView.Frame.Width, this.TableView.Frame.Height));
view.BackgroundColor = UIColor.Red;
this.TableView.AddSubview (view);
TableView.Source = new SessionTableViewSource ();
}
You can use self.navigationController.view as view for adding subview.
The issue is that the View of a UITableViewController is a UITableView, so you cannot add subviews to the controller on top of the table.
I'd recommend switching from a UITableViewController to a simple UIViewController that contains a UITableView. This way the controller main view is a plain UIView that contains a table, and you can add subviews to the main UIView and they will be placed on top of the table view.
You can try to add the view to the window instead of nesting it in the table view like this:
UIWindow* mainWindow = [[UIApplication sharedApplication] keyWindow];
[mainWindow addSubview: overlayview];
UIWindow* window = [[UIApplication sharedApplication].delegate.window;
[window addSubview: your-overlayview];
Swift / Storyboard Solution
Note: The code below assumes one has a custom view (ratingView in my case) that is to be presented over a UITableView.
I've read many answers to this and similar questions on SO. The other answers from these sources worked to varying degrees for me (e.g.,view loaded but not shown or not accessible,...). I am using Swift 2.0+ and I am sharing the complete solution for doing this using a UITableViewController.
Create an outlet to the Navigation Bar and the view, which you want to bring over the tableview.
//MARK:Outlets
#IBOutlet weak var navBar:UINavigationBar!
#IBOutlet var ratingView: MNGStarRating!
In my case I also wanted to animate the view over the tableview so I used a class variable to hold a reference to the inflection point and a point above the scene (off-screen).
var centerYInflection:NSLayoutConstraint!
var aPointAboveScene = -(max(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height) * 2.0)
Then in viewDidLoad I called a function (configureRatingViewAutoLayout) which configures and adds the constraints for the new view to be animated over the tableview.
func configureRatingViewAutoLayout() {
//REQUIRED
self.navBar.superview?.addSubview(self.ratingView)
var newConstraints:[NSLayoutConstraint] = []
newConstraints.append(self.ratingView.leadingAnchor.constraintEqualToAnchor(self.view.leadingAnchor,constant: 10))
newConstraints.append(self.ratingView.trailingAnchor.constraintEqualToAnchor(self.view.trailingAnchor,constant: 10))
newConstraints.append(self.ratingView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor))
//hides the rating view above the scene
self.centerYInflection = self.ratingView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor, constant: self.aPointAboveScene)
//the priority must be set below 1000 if you intend to change it after it has been added to a view
self.centerYInflection.priority = 750
newConstraints.append(self.centerYInflection)
//constraints must be added to the container view of the two items
self.ratingView.superview?.addConstraints(newConstraints)
}
Nota Bene - On a UITableViewController; the self.view is the
self.tableView. They point to the same thing so I guess one could also
use the self.tableView reference above.
Sometime later... In response to a UIControl event I call this method.
#IBAction func toggleRatingView (sender:AnyObject?){
//REQUIRED
self.ratingView.superview?.layoutIfNeeded()
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.37, initialSpringVelocity: 0.99, options: [.CurveEaseOut], animations: { () -> Void in
if CGRectContainsRect(self.view.frame, self.ratingView.frame) {
//in frame ~ animate away
//I play a sound to alert the user something is happening
self.centerYInflection.constant = self.aPointAboveScene
self.centerYInflection.priority = UILayoutPriority(950)
//I disable portions of the UI
self.disableUIElements(nil)
} else {
//out of frame ~ animate in
//I play a different sound here
self.centerYInflection.constant = 0
self.centerYInflection.priority = UILayoutPriority(950)
//I enable the UI fully
self.enableUIElements(nil)
}
//REQUIRED
self.ratingView.superview?.setNeedsLayout()
self.ratingView.superview?.layoutIfNeeded()
}) { (success) -> Void in
//do something else
}
}
These helper methods can be configured to control access to elements in your scene during the presentation of the view.
func disableUIElements(sender:AnyObject?) {
//UI
}
func enableUIElements(sender:AnyObject?) {
//UI
}
Caveats
My view is a custom view in the Storyboard (sitting outside of the
tableview but connected to the TableView Controller). The view has a
required user runtime attribute defined layer.zPosition with a Number value set to 2 (this ensures that it presents in front of the
UITableView).
One could also try playing around with bringSubviewToFront:
and sendSubviewToBack: methods if you don't want to set the zPosition
(I think zPosition is simpler to use)
Try this to hook a button at bottom of the UITableViewController
declare button as a variable:
var submitButton: UIButton!
and in viewDidLoad:
submitButton = UIButton(frame: CGRect(x: 5, y: UIScreen.main.bounds.size.height - 50, width: UIScreen.main.bounds.size.width - 10, height: 50))
submitButton.backgroundColor = UIColor.init(red: 180/255, green: 40/255, blue: 56/255, alpha: 1.0)
submitButton.setTitle("Submit", for: .normal)
submitButton.titleLabel?.font = UIFont(name: "Arial", size: 15)
submitButton.titleLabel?.textColor = .white
submitButton.addTarget(self, action: #selector(submit), for: .touchUpInside)
submitButton.layer.cornerRadius = 5
self.view.addSubview(submitButton)
and implement this method:
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
submitButton.frame = CGRect.init(x: submitButton.frame.origin.x, y: UIScreen.main.bounds.size.height + scrollView.contentOffset.y - 50, width: submitButton.frame.width, height: submitButton.frame.height)
}
This works for me:
if let myTopView = Bundle.main.loadNibNamed("MyTopView", owner: self, options: nil)?.first as? MyTopView {
if let view = UIApplication.shared.keyWindow{
view.addSubview(myView);
myTopView.translatesAutoresizingMaskIntoConstraints = false
myTopView.topAnchor.constraint(equalTo: view.topAnchor ).isActive = true
myTopView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
myTopView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
myTopView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
}
Im adding a MKMapView to my tableview background in ViewdidLoad simply like this and its working.
override func viewDidLoad() {
super.viewDidLoad()
var myMapView = MKMapView(frame: CGRectMake(0, 0, 100, 500))
myMapView.mapType = MKMapType.Standard
self.tableView.backgroundView? = myMapView
}
This displays the map view in the background of the tableview but I wish for the MapView to only be displayed on the bottom of the tableview background and with the height of 250
Im guessing you have to add it as a subview and add constraints, could somebody show me some code as to how they would go about this?
Thanks!
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)