I am having some trouble with my Floating Action Button in Swift. I am using Floaty found here: https://github.com/kciter/Floaty. So it works well on the first time I click the "Calls" screen, but when I switch to another tab, it goes behind the tab bar. The code I use to set up the FAB can be found below.
What I think is going on is that whenever the tabs are switched, the view alternates between recognizing the tabBar and Safe Inset Areas, but sometimes it doesn't and only the paddingX and paddingY values I supplied in the beginning are supported.
I have tried switching between a TableViewController and a ViewController with a tableView in it, but that doesn't really seem to be the problem. I have also tried resetting the padding on every viewWillAppear() just to make sure it is at the right height, but that did not work either. Lastly, I tried to add constraints to the FAB. That was a complex process, that I don't think I did correctly, so it didn't end up working either. The code I used can be seen below.
Does anyone have any suggestions as to why this might be happening, or what I can do to make it work? Right now, my other option is to either get rid of the FAB entirely, or switch to some other library like Google's Material Design Components and use that FAB.
import UIKit
import Floaty
import Material
class CallCreationViewController: UIViewController, UISearchResultsUpdating, UISearchBarDelegate {
#IBOutlet weak var tableView: UITableView!
let floaty : Floaty = Floaty()
var connections: [User] = []
var filteredConnections : [User] = []
var selectedIds: [String] = []
let inset : CGFloat = 34
let blue : Color = Color(red:0.09, green:0.04, blue:0.35, alpha:1.0)
override func viewDidLoad() {
super.viewDidLoad()
floaty.buttonColor = blue
floaty.plusColor = .white
let voiceItem : FloatyItem = FloatyItem()
voiceItem.title = "Start Voice Call"
voiceItem.icon = UIImage(imageLiteralResourceName: "baseline_phone_white_24pt")
voiceItem.iconTintColor = .white
voiceItem.buttonColor = blue
voiceItem.handler = { item in
self.performSegue(withIdentifier: "startVoiceCall", sender: self)
self.floaty.close()
}
let videoItem : FloatyItem = FloatyItem()
videoItem.title = "Start Video Call"
videoItem.icon = UIImage(imageLiteralResourceName: "baseline_video_call_white_24pt")
videoItem.iconTintColor = .white
videoItem.buttonColor = blue
videoItem.handler = { item in
self.performSegue(withIdentifier: "startVideoCall", sender: self)
self.floaty.close()
}
let broadcastItem : FloatyItem = FloatyItem()
broadcastItem.title = "Start Broadcast"
broadcastItem.icon = UIImage(imageLiteralResourceName: "baseline_settings_input_antenna_white_24pt")
broadcastItem.iconTintColor = .white
broadcastItem.buttonColor = blue
broadcastItem.handler = { item in
self.performSegue(withIdentifier: "startBroadcast", sender: self)
self.floaty.close()
}
floaty.addItem(item: voiceItem)
floaty.addItem(item: videoItem)
floaty.addItem(item: broadcastItem)
floaty.respondsToKeyboard = false
//let constraint = NSLayoutConstraint(item: floaty, attribute: NSLayoutConstraint.Attribute.bottomMargin, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.tabBarController?.tabBar, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100)
floaty.sticky = true
floaty.paddingX = inset
floaty.paddingY = inset
self.view.addSubview(floaty)
//let constraint = NSLayoutConstraint(item: floaty, attribute: .bottomMargin, relatedBy: .equal, toItem: self.navigationController?.navigationBar, attribute: .top, multiplier: 1, constant: 100)
//floaty.addConstraint(constraint)
self.tableView.dataSource = self
// Do any additional setup after loading the view.
}
}
While I'm not familiar with Floaty, my guess would be that it's your tableview being underneath the tab bar that's causing it to fluctuate in it's location.
For example if I added a generic UIView like this:
let greenView : UIView = UIView(frame: CGRect(x: self.view.frame.size.width - 100, y: self.view.frame.size.height - 100, width: 100, height: 100))
greenView.backgroundColor = UIColor.green
self.view.addSubview(greenView)
It would be underneath the tab bar.
If you're looking for a quick fix, and don't necessarily need your tableview to extend behind your tab bar, you should just be able to add this line:
self.edgesForExtendedLayout.remove(UIRectEdge.bottom)
before setting up the Floaty in viewDidLoad and your tableview won't extend below the bottom tab bar.
Related
First off, please do not propose a "clever" solution suggesting I remove my TableViewController as a child view. Thank you.
Summary
I am adding a Tableviewcontroller programatically , as a child of a view with a fixed size of 216. I have been messing with constraints....and using the View Hierachy Debugger, I see the TableView always has a height of 852...which is basically the full size of the screen. How can I properly size the TableView to its containing view?
enter image description here
Below is a bunch of the code I am trying to use to constrain things...to no avail. Thank you.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var xyz: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let child = UITableViewController()
xyz.addSubview(child.view)
self.addChild(child)
child.didMove(toParent: self)
//child.view.translatesAutoresizingMaskIntoConstraints = false
let safeArea = xyz.layoutMarginsGuide
var height = child.view.heightAnchor.constraint(equalToConstant: 292)
height = height.constraintWithMultiplier(2000)
height.isActive = true
child.view.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
child.view.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
child.view.leftAnchor.constraint(equalTo: safeArea.leftAnchor).isActive = true
child.view.rightAnchor.constraint(equalTo: safeArea.rightAnchor).isActive = true
}
}
extension NSLayoutConstraint {
func constraintWithMultiplier(_ multiplier: CGFloat) -> NSLayoutConstraint {
return NSLayoutConstraint(item: self.firstItem!, attribute: self.firstAttribute, relatedBy: self.relation, toItem: self.secondItem, attribute: self.secondAttribute, multiplier: multiplier, constant: self.constant)
}
}
Uncomment this line of code
child.view.translatesAutoresizingMaskIntoConstraints = false
I want to add a view with a tableview in it in code.
I define the view in storyboard like in the image.
In my view controller I set the place and size using the following code.
override func viewDidLoad() {
super.viewDidLoad()
// bunch of non-related code
timeTableView.delegate = self
timeTableView.dataSource = self
timeTableView.rowHeight = UITableViewAutomaticDimension
timeTableView.estimatedRowHeight = 32
timeChooser.translatesAutoresizingMaskIntoConstraints=false
}
#IBAction func onTimeLabelPressed(_ sender: AnyObject){
view.addSubview(timeChooser)
let topConstraint = timeChooser.topAnchor.constraint(equalTo: viewdayTime.bottomAnchor)
let leftConstraint = timeChooser.leftAnchor.constraint(equalTo: viewdayTime.leftAnchor)
//let heightConstraint = timeChooser.heightAnchor.constraint(greaterThanOrEqualToConstant: 128)
//let widthConstraint = timeChooser.widthAnchor.constraint(lessThanOrEqualTo: viewdayTime.widthAnchor)
NSLayoutConstraint.activate([topConstraint,leftConstraint, /*heightConstraint, widthconstraint*/])
view.layoutIfNeeded()
timeChooser.alpha=0
UIView.animate(withDuration: 0.2){
self.timeChooser.alpha=1.0}
}
The function onTimeLabelPressed makes the view appear under and left aligned with a label.
Problem is the tableview will not scroll or respond otherwise as long as the line
timeChooser.translatesAutoresizingMaskIntoConstraints=false
is present. Removing it breaks the positioning constraints but the tableview behaves normally.
Anybody any ideas? Did not find any question describing my witnessed behavior.
You need to give your timeChooser constraints to establish its width and height. It would probably be best to set right and bottom constraints.
let rightConstraint = timeChooser.rightAnchor.constraint(equalTo: view.rightAnchor)
let bottomConstraint = timeChooser.bottomAnchor.constraint(equalTo: view.bottomAnchor)
So I'm trying to clone the Apple weather app. My ViewHierarchy looks like this:
ViewControllerA contains a UIScrollView which in turn contains a UIView (UIView1) and the UIView contains child elements.There is also a button below the UIScrollView to add more UIView inside the UIScrollView.
This UIViewController is designed in IB using AutoLayout.
When I click this button ideally I want to clone the UIView1 and add it to the UIScrollView, to the righthandside end of the currentView inside the UIScrollView. This is a horizontal scroll function. This is where I am stuck.
What I have tried are the following:
Create a copy of UIView1 in a xib and load that.
Create a UIView programmatically and load that.
In both the cases I'm facing the auto layout constraints issue. When I load the second view, it's overwritten on top of the existing view.
I can hardcode the frame sizes for the cloned UIView and get it to work but obviously that won't work across devices.
So I'm adding constraints - something like this:
func buildView(startX:CGFloat, model:CityModel) -> UIView {
var frame:CGRect = CGRectMake(0, 0, insideScrollView.bounds.width, insideScrollView.bounds.height)
var cityView:UIView = UIView(frame: frame)
var lFrame:CGRect = CGRectMake(0, 0, 100,50)
var cLabel:UILabel = UILabel(frame: lFrame)
cLabel.text = model.name
cLabel.sizeToFit()
cLabel.textAlignment = .Center
//cityView.addSubview(cLabel)
cityView.layer.backgroundColor = UIColor.blueColor().CGColor
cityView.setTranslatesAutoresizingMaskIntoConstraints(false)
var constX = NSLayoutConstraint(item: cityView, attribute: NSLayoutAttribute.LeftMargin, relatedBy: NSLayoutRelation.Equal, toItem: cityScrollView, attribute: NSLayoutAttribute.LeftMargin, multiplier: 1, constant: 0)
cityView.addConstraint(constX)
return cityView
}
The app crashes unable to load this constraint indicating that the view hierarchy does not support this constraint as all the views are not loaded.
I'll keep digging on how to resolve this but any help will be greatly appreciated. Here's my viewDidLoad method. Cities is an array of models containing the view data
override func viewDidLoad() {
super.viewDidLoad()
var startScroll = insideScrollView.bounds.width
if cities != nil {
for model in cities.cityModels {
if model.selected {
cityScrollView.addSubview(buildView(startScroll, model: model))
startScroll += cityScrollView.bounds.width
}
}
} else {
var cityModel: CityModel = CityModel(name: "default")
cityModel.selected = false
cities = SearchCityModels.sharedInstance
cities.cityModels.append(cityModel)
}
}
Posting the answer on behalf of #k6sandeep. I had not loaded the view inside the scrollview but was adding constraints to it. So fixed the same by adding constraints after the insideview was loaded.
EDIT
Thanks to #LeoNatan I have now got a complete working solution. If anyone finds this and would like the solution, it's available on GitHub.
Original Question
I'm trying to get iAds (or any other view for that matter, although it may be specific to ADBannerView) to be displayed just above a UITabBar. I've gone about a few different ways of doing this, but haven't come up with a solution that satifies the following:
Works on iOS 7 and 8
Works with and without the iAd displayed
Works in landscape and portrait
Works on iPhone and iPad
UITableViews insets correctly update
The only solution I have so far that has worked has been to have my UITableView inside a UIViewController, and adding the UITableView and ADBannerView to the view property of the UIViewController. I moved away from this for 2 reasons:
The UITableView did not extend its edges below the bottom UITabBar
I need to subclass UITableViewController, not UIViewController
I have a bannerView property on my AppDelegate and a shouldShowBannerView property to decide whether or not to show the iAd, and share a single instance. The AppDelegate then sends out notifications when iAds should be displayed or hidden (i.e., when an iAd is loaded and when the user has paid to remove the iAds). The "base" of the code works as such:
func showiAds(animated: Bool) {
if !self.showingiAd {
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
if let bannerView = delegate.bannerView {
println("Showing iAd")
self.showingiAd = true
if (bannerView.superview != self.view) {
bannerView.removeFromSuperview()
}
// let bannersSuperview = self.view.superview! // Bottom inset incorrect
let bannersSuperview = self.view // Banner is shown at the top screen. Crashes on iOS 7 (at bannersSuperview.layoutIfNeeded())
// let bannersSuperview = self.tableView // The is the same as self.view (duh)
// let bannersSuperview = self.tabBarController!.view // Bottom inset incorrect
// Added the view and the left/right constraints allow for the proper height
// to be returned when bannerView.frame.size.height is called (iOS 7 fix mainly)
bannersSuperview.addSubview(bannerView)
bannersSuperview.addConstraints([
NSLayoutConstraint(item: bannerView, attribute: .Left, relatedBy: .Equal, toItem: bannersSuperview, attribute: .Left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: bannerView, attribute: .Right, relatedBy: .Equal, toItem: bannersSuperview, attribute: .Right, multiplier: 1, constant: 0),
])
bannersSuperview.layoutIfNeeded()
let bannerViewHeight = bannerView.frame.size.height
var offset: CGFloat = -self.bottomLayoutGuide.length
if (UIDevice.currentDevice().systemVersion as NSString).floatValue < 8 {
// Seems to be needed for some reason
offset -= bannerViewHeight
}
let bannerBottomConstraint = NSLayoutConstraint(item: bannerView, attribute: .Bottom, relatedBy: .Equal, toItem: bannersSuperview, attribute: .Bottom, multiplier: 1, constant: offset + bannerViewHeight)
// self.bannerBottomConstraint = bannerBottomConstraint
bannersSuperview.addConstraint(bannerBottomConstraint)
bannersSuperview.layoutSubviews()
// bannerSuperview.setNeedsLayout()
bannersSuperview.layoutIfNeeded()
// Previously, this values was the height of the banner view, so that it starts off screen.
// Setting this to 0 and then doing an animation makes it slide in from below
bannerBottomConstraint.constant = offset
bannersSuperview.setNeedsUpdateConstraints()
UIView.animateWithDuration(animated ? 10 : 0, animations: { () -> Void in
// Calling layoutIfNeeded here will animate the layout constraint cosntant change made above
bannersSuperview.layoutIfNeeded()
})
} else {
println("Cannot show iAd when bannerView is nil")
}
}
}
func hideiAds() {
if self.showingiAd {
self.showingiAd = false
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
if let bannerView = delegate.bannerView {
if bannerView.superview == self.view {
bannerView.removeFromSuperview()
}
}
}
}
I then check in my viewWillAppear: and viewDidDisappear: methods if an iAds is/should be displayed and calling showiAds(false) and hideiAds() as required.
No matter what I do, I don't seem to be able to get it to work. A couple of other things I've tried but scrapped the code for:
Adding the iAd in the UITabBarController, which then alerts the UITableViewControllers that the iAd was shown/hidden. Modifying the content/scroll indicator insets did not work well, and was ofter reset by the UITableViewController to fit above/below the navigation/tab bar.
(as above) setting the content/scroll indicator insets myself, but I could not get it consistent without attempting to emulate (using (top|bottom)LayoutGuide) in viewDidLayoutSubviews, but this seems very costly?
I did, at one point, have it working by adding the ADBannerView to some view from within the UITableViewController, but it would crash on iOS 7 (something about tableView must call super -layoutSubviews)
EDIT
I have created a UIViewController subclass with the intent of using it to house UITableViewControllers via a Container View. Here is what I have so far, followed by a couple of issues:
class AdvertContainerViewController: UIViewController {
var tableViewController: UITableViewController?
var showingiAd = false
var bannerBottomConstraint: NSLayoutConstraint?
private var bannerTopOffset: CGFloat {
get {
var offset: CGFloat = 0
if let tabBar = self.tabBarController?.tabBar {
offset -= CGRectGetHeight(tabBar.frame)
}
if let bannerView = AppDelegate.instance.bannerView {
let bannerViewHeight = bannerView.frame.size.height
offset -= bannerViewHeight
}
return offset
}
}
override func viewDidLoad() {
super.viewDidLoad()
if self.childViewControllers.count > 0 {
if let tableViewController = self.childViewControllers[0] as? UITableViewController {
self.tableViewController = tableViewController
tableViewController.automaticallyAdjustsScrollViewInsets = false
self.navigationItem.title = tableViewController.navigationItem.title
}
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if AppDelegate.instance.shouldShowBannerView {
self.showiAds(false)
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let delegate = AppDelegate.instance
NSNotificationCenter.defaultCenter().addObserver(self, selector: "showiAds", name: "BannerViewDidLoadAd", object: delegate)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "hideiAds", name: "RemoveBannerAds", object: delegate)
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
if self.showingiAd {
self.hideiAds()
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
println("View did layout subviews")
if self.showingiAd {
if let bannerView = AppDelegate.instance.bannerView {
let bannerViewHeight = CGRectGetHeight(bannerView.frame)
if let bottomConstraint = self.bannerBottomConstraint {
let bannerTopOffset = self.bottomLayoutGuide.length + bannerViewHeight
if bottomConstraint.constant != bannerTopOffset {
println("Setting banner top offset to \(bannerTopOffset)")
bottomConstraint.constant = -bannerTopOffset
bannerView.superview?.setNeedsUpdateConstraints()
bannerView.superview?.updateConstraintsIfNeeded()
}
}
println("Bottom layout guide is \(self.bottomLayoutGuide.length)")
let insets = UIEdgeInsetsMake(self.topLayoutGuide.length, 0, self.bottomLayoutGuide.length + bannerViewHeight, 0)
self.updateTableViewInsetsIfRequired(insets)
}
}
}
private func updateTableViewInsetsIfRequired(insets: UIEdgeInsets) {
if let tableView = self.tableViewController?.tableView {
if !UIEdgeInsetsEqualToEdgeInsets(tableView.contentInset, insets) {
println("Updating content insets to \(insets.top), \(insets.bottom)")
tableView.contentInset = insets
}
if !UIEdgeInsetsEqualToEdgeInsets(tableView.scrollIndicatorInsets, insets) {
println("Updating scroll insets to \(insets.top), \(insets.bottom)")
tableView.scrollIndicatorInsets = insets
}
}
}
func showiAds() {
self.showiAds(true)
// self.showiAds(false)
}
func showiAds(animated: Bool) {
if !self.showingiAd {
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
if let bannerView = delegate.bannerView {
println("Showing iAd")
self.showingiAd = true
if (bannerView.superview != self.view) {
bannerView.removeFromSuperview()
}
let bannersSuperview = self.view.superview!
// Added the view and the left/right constraints allow for the proper height
// to be returned when bannerView.frame.size.height is called (iOS 7 fix mainly)
bannersSuperview.addSubview(bannerView)
bannersSuperview.addConstraints([
NSLayoutConstraint(item: bannerView, attribute: .Left, relatedBy: .Equal, toItem: bannersSuperview, attribute: .Left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: bannerView, attribute: .Right, relatedBy: .Equal, toItem: bannersSuperview, attribute: .Right, multiplier: 1, constant: 0),
])
bannersSuperview.layoutIfNeeded()
let bannerBottomConstraint = NSLayoutConstraint(item: bannerView, attribute: .Top, relatedBy: .Equal, toItem: bannersSuperview, attribute: .Bottom, multiplier: 1, constant: 0)
self.bannerBottomConstraint = bannerBottomConstraint
bannersSuperview.addConstraint(bannerBottomConstraint)
bannersSuperview.layoutSubviews()
bannersSuperview.layoutIfNeeded()
let topInset = self.navigationController?.navigationBar.frame.size.height ?? 0
let insets = UIEdgeInsetsMake(topInset, 0, -self.bannerTopOffset, 0)
// Previously, this values was the height of the banner view, so that it starts off screen.
// Setting this to 0 and then doing an animation makes it slide in from below
bannerBottomConstraint.constant = self.bannerTopOffset
bannersSuperview.setNeedsUpdateConstraints()
UIView.animateWithDuration(animated ? 0.5 : 0, animations: { () -> Void in
// Calling layoutIfNeeded here will animate the layout constraint cosntant change made above
self.updateTableViewInsetsIfRequired(insets)
bannersSuperview.layoutIfNeeded()
})
} else {
println("Cannot show iAd when bannerView is nil")
}
}
}
func hideiAds() {
if self.showingiAd {
self.showingiAd = false
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
if let bannerView = delegate.bannerView {
if bannerView.superview == self.view {
bannerView.removeFromSuperview()
}
}
}
}
}
Issues so far:
Using self.view as the superview causes a crash on rotate Auto Layout still required after sending -viewDidLayoutSubviews to the view controller. Gathered.AdvertContainerViewController's implementation needs to send -layoutSubviews to the view to invoke auto layout.
I'm not calculating the content insets correctly; when the iAd is shown, the top jumps up slightly and the bottom in below the top of the banner
The table view doesn't show the scroll indicators. This seems to be a known issue but I cannot find a solution
At the request of Leo Natan I have create a repo on GitHub that I will update with any attempts I make, and explain issues here. Currently, the issues are as follows:
First Tab:
Top of table moves down when iAd is shown (iOS 8)
Table cannot be scrolled (iOS 7)
Top of table view jumps when iAd shows (iOS 7)
Rotation often breaks the offset of the iAd, hiding it behind the tab bar (iOS 7 and 8)
Second Tab:
There are no scroll bars (iOS 7 and 8)
Scroll inset it not set (iOS 7)
Rotation often breaks the offset of the iAd, hiding it behind the tab bar (iOS 7 and 8)
The best solution is to use view controller containment. Use a view controller subclass that will house both the ad view and the table view controller's view, and add the table view controller as a child of the container view controller. This should take care of content insets correctly. On each layout of the container controller's view, position the table controller view hierarchy correctly after positioning the ad view. If you wish to hide the ad view, simply hide or remove it from the container hierarchy, and extend the table controller's view hierarchy fully. When working with hierarchies, remember to always use the table controller's view and not the tableView directly.
My answer was adapted into the following GitHub repo:
https://github.com/JosephDuffy/iAdContainer
The best that is that you download the AD suite from Apple site, there are tabbar controller and navigation controller containment example.
Apple provides you an abstract view controller that can handle by itself the ADBanner flow without interrupting its presentation, maximizing the showing time.
You can use this https://developer.apple.com/library/ios/samplecode/iAdSuite/Introduction/Intro.html apple sample and modified it according to your needs. Such as bool variable to take care of when iAds is shown or not.
There in code you can see BannerViewController class that contains all the logic. You can also write ADmob code there to use.
Ok so I'm struggling here and haven't been able to find a working solution. I've been self learning Swift without Objective C experience (I know, I know).
In my app, I have my main UIViewController, a subview that is transparent but slides in from the bottom of the screen, and then 4 subviews of the sliding subview that are all working UIScrollViews. I have paging enabled and it works great but I'd like to add a UIPageControl for each of them. I seriously can't grasp delegates and how to implement the using swift. Any help would be much appreciated!
Also, I'm doing this all programmatically, so no IB please. Happy to provide code if it'll help. Thanks
I think you and/or anyone else looking for how to do this will find this answer helpful. The code example enabled me to create a page control indicator on my scrollView, and it was the first time attempting to do this. I found it very clear.
The lines you probably need to add to your project are:
1: add UIScrollViewDelegate as a protocol when you first name your view controller class.
2: in the class declaration create a pageControl variable. You will need to play with the frame numbers to get it to appear where you want it. the current numbers made one in the middle of the window for me.
For reference the numbers mean (x position for top left corner of indicator, y coordinate for top left corner, width of page indicator, height of page indicator)
var pageControl : UIPageControl = UIPageControl(frame: CGRectMake(50, 300, 200, 20))
in viewDidLoad set the scrollView delegate and call `configurePageControl():
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
configurePageControl()
}
you need to add two methods after viewDidLoad. one is called in viewDidLoad
func configurePageControl() {
self.pageControl.numberOfPages = <some reference to the number of pages>
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.redColor()
self.pageControl.pageIndicatorTintColor = UIColor.blackColor()
self.pageControl.currentPageIndicatorTintColor = UIColor.greenColor()
self.view.addSubview(pageControl)
}
and
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
The scrollView delegate is actually very simple to set up. Add UIScollViewDelegate as a protocol that your ViewController class will implement by adding it after the class declaration: class YourClassName: UIScrollViewDelegate. And then in viewDidLoad(), you complete the delegate setup by assigning the scroll view's delegate property to your class with the line scrollView.delegate = self. (again see the example I linked for if you need further clarification of where these commands go)
Just setup it in code like this:
private var pageControl = UIPageControl(frame: .zero)
private func setupPageControl() {
pageControl.numberOfPages = controllers.count
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.currentPageIndicatorTintColor = UIColor.orange
pageControl.pageIndicatorTintColor = UIColor.lightGray.withAlphaComponent(0.8)
let leading = NSLayoutConstraint(item: pageControl, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0)
let trailing = NSLayoutConstraint(item: pageControl, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)
let bottom = NSLayoutConstraint(item: pageControl, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
view.insertSubview(pageControl, at: 0)
view.bringSubview(toFront: pageControl)
view.addConstraints([leading, trailing, bottom])
}
UIPageControl Integration using Swift 4
func configurePageControl() {
self.pageview.numberOfPages = items.count
self.pageview.currentPage = 0
self.pageview.tintColor = UIColor.red
self.pageview.pageIndicatorTintColor = UIColor.black
self.pageview.currentPageIndicatorTintColor = UIColor.green
}
Delegates are just methods of a class, that can be palmed off to another class. These methods are usually callbacks.
e.g. A callback for a TextField, when the user hits return, can be implemented in a Delegate. The delegate can be implemented in the ViewController class.
Now when the user hits return the TextField Object will call the delegate method in the ViewController object. The great thing about this is, you can access all the variables and methods of the ViewController object from the delegated method. Otherwise you would need a handle to the ViewController object within the TextField object itself.
Delegates are implemented as protocols, which are just interfaces. So if the ViewController implements the TextFieldDelegate protocol, all your textfield callbacks can be called from within the ViewController object.
I hope this helps.