I want my app to look like the image below.
I am using PageMenu to acomplish this result. The data is fetcehd from a web API. I want to dynamically create view controllers based on the number of days that the web API responds with so that each day can have a view controller so that it can display schedule for that day. How can I accomplish this?
reading from the link you provided yourself it has:
// Array to keep track of controllers in page menu
var controllerArray : [UIViewController] = []
// Create variables for all view controllers you want to put in the
// page menu, initialize them, and add each to the controller array.
// (Can be any UIViewController subclass)
// Make sure the title property of all view controllers is set
// Example:
var controller : UIViewController = UIViewController(nibName: "controllerNibName", bundle: nil)
controller.title = "SAMPLE TITLE"
controllerArray.append(controller)
So what you have to do is : have a function that returns a [dailyModel].
Your dailyModel would look something like this:
struct dailyModel {
let programStartingTime : String // 6:45
let item : [item] // [(Maghrib & Isha Prayer, 20, 0),(Ziarat-e-Ashura, 20, 1), ...otherItems...]
}
struct item{
let duration : Int? //minutes
let name : String // Maghrib and Isha prayers
let row : Int? // 0
}
Then loop through that dailyModel and using its parameters you instantiate & populate viewcontroller and then append each of them as the tutorial is doing.
This isn't the best code but I hope you get the idea.
Simply create a loop add same controller multiple times. i created addPageMenu function. call it when you get the response.
func addPageMenu(count: Int) {
var controllerArray : [UIViewController] = []
for i in count {
var controller : UIViewController = UIViewController(nibName: "controllerNibName", bundle: nil)
if i == 0 {
controller.title = "SUNDAY"
} else if i == 1 {
controller.title = "MONDAY"
}...
controllerArray.append(controller)
}
let pageMenu = CAPSPageMenu(viewControllers: controllerArray, frame: CGRectMake(0.0, 0.0, self.view.frame.width, self.view.frame.height), pageMenuOptions: parameters)
// Lastly add page menu as subview of base view controller view
// or use pageMenu controller in you view hierachy as desired
self.view.addSubview(pageMenu!.view)
}
Related
I got tab bar controller with three view controllers setup. One of those view controllers changes its tab bar item badgeValue when I open it. I would like to change this badgeValue already when I arrive at the first tab.
I created a Tabbarcontoller: UITabBarController class but don't know how to easily access the items of the sub views. Here is the code from my Tabbarcontoller class:
class TabBarController: UITabBarController, MainMethodsDelegate {
var myFriendsRequests: [UserInfo] = []
var friendRequestsCount: Int = 0
func getFriendsRequests_Methods_Destination(myFriendsRequest: UserInfo) {
myFriendsRequests.append(myFriendsRequest)
self.friendRequestsCount = myFriendsRequests.count
DispatchQueue.main.async {
//friendsBarItem.badgeValue = String(self.friendRequestsCount)
}
}
let mainMethods = MainMethods()
override func viewDidLoad() {
super.viewDidLoad()
mainMethods.delegate = self
mainMethods.getFriendsRequests()
}
}
And here the working code from the sub view controller:
class FriendsViewController: UIViewController, MainMethodsDelegate {
var myFriendsRequests: [UserInfo] = []
var friendRequestsCount: Int = 0
func getFriendsRequests_Methods_Destination(myFriendsRequest: UserInfo) {
myFriendsRequests.append(myFriendsRequest)
self.friendRequestsCount = myFriendsRequests.count
DispatchQueue.main.async {
self.friendsBarItem.badgeValue = String(self.friendRequestsCount)
}
}
let mainMethods = MainMethods()
override func viewDidLoad() {
super.viewDidLoad()
mainMethods.delegate = self
mainMethods.getFriendsRequests()
}
#IBOutlet weak var friendsBarItem: UITabBarItem!
}
I'm looking for a simple way to access the sub view controllers or at least the bar items where it says:
//friendsBarItem.badgeValue = String(self.friendRequestsCount)
I'm not sure if delegates is the right way to go?
I would leave the bar item out of the friends controller. It does not need to know about the tabBar. If I were you, I would create a protocol to let know your tabBarController that the number of friend requests has changed.
First, define the protocol:
protocol FriendsRequestDelegate {
func friendsRequestsDidChange(number: Int)
}
Then, add the variable to your FriendsViewController:
weak var delegate: FriendsRequestDelegate?
And, we need to trigger that func, still in FriendsViewController, after fetching the number of friends request, add;
delegate?.friendsRequestsDidChange(number: myFriendsRequests.count)
Finally, make your TabBarController conform to this protocol;
extension TabBarController: FriendsRequestDelegate {
func friendsRequestsDidChange(number: Int) {
friendsBarItem.badgeValue = String(number)
}
}
See what I mean? This way your friendsRequestController doesn't know about the tabBar and it'll keep your code clean.
I've found a quite simple way to access the barItems. I only had to access the array of tabBar items with:
tabBar.items![2].badgeValue = ""
Here is my code for the UITabBarController:
class TabBarController: UITabBarController, MainMethodsDelegate {
var myFriendsRequests: [UserInfo] = []
var friendRequestsCount: Int = 0
func getFriendsRequests_Methods_Destination(myFriendsRequest: UserInfo) {
myFriendsRequests.append(myFriendsRequest)
self.friendRequestsCount = myFriendsRequests.count
DispatchQueue.main.async {
self.tabBar.items![2].badgeValue = String(self.friendRequestsCount)
}
}
let mainMethods = MainMethods()
override func viewDidLoad() {
super.viewDidLoad()
mainMethods.delegate = self
mainMethods.getFriendsRequests()
}
}
The sub view controller does not need to check and update the badgeValue anymore. (This is fine in my case since the getFriendsRequests_Methods_Destination(myFriendsRequest: UserInfo) method gets triggered as soon as there is a new friends request anyways using a (Firestore) snapshot listener.)
My initial way was to access the tabItem from each of the sub view controllers individually using tabBarController!.tabBar.items![2].badgeValue = "" But this would have been redundant in my case.
I edited exactly what I want to achieve, this time different example without taken parameter in function.
What I want to achieve is:
var array = [String]()
func create() {
var arrayCount = array.count // will be 0
var nameForUI = "view/(arrayCount)" // will be view0
let nameForUI: UIVIew = {
let view = UIVIew()
return view
}()
array.append(nameForUI)
view.addSubview(nameForUI)
//
}
next time if I call create() func , the next view will be called "view1" So my question is, how to achieve this result? every time function will called it will create new element with new name.
Edit
To directly answer your question: No, you cannot do that.
Code written in Swift is compiled -- it is not an interpreted / scripted language.
So you cannot write code that creates a button named "littleButton" and then have a line of code littleButton.backgroundColor = .red
You can sort of do this by creating a Dictionary that maintains the "name" of the element as the key, and a reference to the element as the value.
Start with initializing an empty dictionary:
var buttonsDict: [String : UIButton] = [String : UIButton]()
Your "create" func can start like this:
func createButton(named str: String) -> Void {
// create a button
let b = UIButton()
// give it a default title
b.setTitle("Button", for: .normal)
// add it to our Dictionary
buttonsDict.updateValue(b, forKey: str)
}
When you want to create a button:
createButton(named: "littleButton")
When you want to access that button by name:
// use guard to make sure you get a valid button reference
guard let btn = buttonsDict["littleButton"] else { return }
view.addSubview(btn)
Edit 2
Another option, which is perhaps more similar to your edited question:
// initialize empty array of views
var viewsArray: [UIView] = [UIView]()
override func viewDidLoad() {
super.viewDidLoad()
// create 5 views
for _ in 0..<5 {
create()
}
// ...
// then, somewhere else in your code
viewsArray[0].backgroundColor = .red
viewsArray[1].backgroundColor = .green
viewsArray[2].backgroundColor = .blue
viewsArray[3].backgroundColor = .yellow
viewsArray[4].backgroundColor = .orange
}
func create() -> Void {
// create a view
let v = UIView()
// add it to our array
viewsArray.append(v)
// add it as a subview
view.addSubview(v)
}
As you see, instead of trying to reference the created views by name (which you cannot do), you can reference them by array index.
Just remember that arrays are zero-based... so the first element added to the array will be at [0] not [1].
I have tried looking at answers on similar questions to this, but I am not particularly experienced and have had trouble following them, so any help would be much appreciated! My situation is as follows: when I press a button in my Parent ViewController, the following code is used to call a Child ViewController (by the way, the Child is actually a TableViewController, but it seems to work fine "thinking" it's a normal ViewController?):
controller = (storyboard?.instantiateViewController(withIdentifier: "People"))
addChildViewController(controller!)
controller?.view.frame = CGRect(x: 10, y: 200, width: 394, height: 300)
self.view.addSubview((controller?.view)!)
controller?.didMove(toParentViewController: self)
What I would then like is to transfer an array from the Parent to the Child, where it will be used as the TableView's data?
Secondly, when I select a cell from the Child's TableView, I would like the relevant information to be sent to the Parent, and for the Child to disappear.
In case it is of interest, I have managed to close the Child under different circumstances (when a click occurs in the Parent while the Child is displayed) using the following:
controller?.willMove(toParentViewController: nil)
controller?.view.removeFromSuperview()
controller?.removeFromParentViewController()
I would really appreciate any advice, even if it's a link to something which would help!
You can pass value from Parent to Child Controller like this
controller = (storyboard?.instantiateViewController(withIdentifier: "People"))
addChildViewController(controller!)
controller?.view.frame = CGRect(x: 10, y: 200, width: 394, height: 300)
controller.tableDataSource = // Pass your required value to child controller
self.view.addSubview((controller?.view)!)
controller?.didMove(toParentViewController: self)
Now you want to transfer back your select value to Parent view controller. For this purpose your have a create a Delegate in ChildController like
#protocol ChildControllerDelegate : class {
func selectedValue(Value : String)
}
After that make a variable of that delegate in ChildController like this
weak var delegate : ChildControllerDelegate?
and when in rowDidSelect method add following code
if(delegate != nil) {
delegate.selectedValue(Value :"Your selected value")
}
Now step when you are going to show ChildController from ParentController at that time you have to set that delegate object to ParentController like this
controller = (storyboard?.instantiateViewController(withIdentifier: "People"))
addChildViewController(controller!)
controller?.view.frame = CGRect(x: 10, y: 200, width: 394, height: 300)
controller.delegate = self
self.view.addSubview((controller?.view)!)
controller?.didMove(toParentViewController: self)
and after that just implement the delegate method in ParentController like that
func selectedValue(Value : String) {
// you select val
}
Try This.
First Create Public Method For Add And Remove childVC.
For Add childVC.
public class func openChildViewController(parentVC:UIViewController, with childVC:UIViewController){
parentVC.addChildViewController(childVC)
childVC.view.frame = parentVC.view.frame
parentVC.view.addSubview(childVC.view)
parentVC.didMove(toParentViewController: childVC)
}
For Remove childVC.
public class func removeChildViewController(childVC:UIViewController){
childVC.willMove(toParentViewController: nil)
childVC.view.removeFromSuperview()
childVC.removeFromParentViewController()
}
Use Above Method.
1.ParentVC.swift
class ParentVC: UIViewController , ChildVCDelegate {
var arrType = NSMutableArray()
//add ChildVC
#IBAction func btnAddChildVC(_ sender: UIButton) {
let ChildVC = self.storyboard?.instantiateViewController(withIdentifier: "ChildVC") as! ChildVC
PickerVC.arrPass = arrType //for data passing create any object in ChildVC for ex. arrPass is NSMutableArray
ChildVC.delegate = self
openChildViewController(parentVC: self, with: ChildVC)
}
// MARK: ChildVC Delegate
func SetSelectedPickerValue(strSelectOption: String) {
print(strSelectOption)
}
}
}
2.ChildVC.swift
class ChildVC: UIViewController{
// MARK: Variable for ParentVCData Passing
var arrPass = NSMutableArray()
override func viewWillAppear(_ animated: Bool) {
print(arrPass)
}
//Remove ChildVC
#IBAction func btnRemoveChildVC(_ sender: UIButton) {
self.delegate?.SetSelectedPickerValue!(strSelectOption: “any String you pass ChildVC To ParentVC”)
removeChildViewController(childVC: self)
}
}
// MARK: Create Delegate Method
#objc protocol ChildVCDelegate{
#objc optional func SetSelectedPickerValue(strSelectOption:String)
}
You can:
Cast the controller to the appropriate class for the child view controller (I'm using ChildViewController below, but hopefully you have a more descriptive name); and
Pass the array (which I guessed you might have called people, but use whatever your array names are in these two respective view controllers) from the current view controller (the parent) to this new child view controller.
Thus:
let child = storyboard!.instantiateViewController(withIdentifier: "People") as! ChildViewController
addChildViewController(child)
child.people = people
child.view.frame = ...
view.addSubview(child.view)
child.didMove(toParentViewController: self)
Personally, I wouldn't hard code the child coordinates like you did in your original question. I'd set translatesAutoresizingMaskIntoConstraints to false and then add the appropriate leading/trailing/top/bottom constraints, but that's up to you. It was just too painful to see hardcoded coordinates in your example.
I have a table view with a tap gesture recognizer inside of it and when I double click on the cell, I wanted to pass data from the cell to another view controller. Now it works only once and doesnt update the string in the view controller. The string remains permanent. Now I need help updating the string so that when I try to double tap another cell, the string will update instead of only work once by keeping the value of the string in the view controller constant with that of the first cell tapped. Here is my code.
Code in tableview for double tap gesture recognizer.
func CellTappedTwice(sender: UITapGestureRecognizer!) {
if let index = sender.view?.tag{
if let object = objects?[index]{
if let objectId = object.objectId{
popupViewController.objectId = objectId
}
}
}
self.present(popupViewController, animated: true, completion: nil)
}
Code in view controller:
var objectId : String?
#IBOutlet weak var QrCode: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
if let object = objectId {
let qrcode = DCQRCode(info: object, size: CGSize(width: 170, height: 170))
qrcode.positionStyle = [
(UIImage(named: "RedOuterPosition")!, DCQRCodePosition.topRight),
(UIImage(named: "RedOuterPosition")!, DCQRCodePosition.topLeft),
(UIImage(named: "RedOuterPosition")!, DCQRCodePosition.bottomLeft)
]
qrcode.maskImage = UIImage(named: "RedColors")
QrCode.image = qrcode.image()
print("hello\(objectId)")
}
}
Move the code that handles objectId to your viewWillAppear(_:) method, not viewDidLoad. The viewDidLoad method only gets called once during the life of a view controller, so if you're using the same view controller over and over, you'll only every process objectId the first time it's displayed.
I'm developping application in Swift.
This application has many view and I would like to put a UIProgressView on all views
Can we get an array of all storyboard views ?
for exemple :
self.progressBar = UIProgressView(progressViewStyle: .Bar)
self.progressBar?.center = view.center
self.progressBar?.frame = CGRect(x: 0, y: 20, width: view.frame.width, height: CGFloat(1))
self.progressBar?.progress = 1/2
self.progressBar?.trackTintColor = UIColor.lightGrayColor();
self.progressBar?.tintColor = UIColor.redColor();
var arrayViewController : [UIViewController] = [...,...,...]
for controller in arrayViewController {
controller.view.addSubview(self.progressBar)
}
Thank you
Ysée
I assume that what you really want is to have the progress displayed on every view IF there is an operation in progress.
There are many ways to do that (using delegation, NSNotificationCenter, …) but the easiest I can think of would be to rely on viewWillAppear
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// Check if there's an operation in progress and add progressView if relevant
}
For the user, it will effectively look like you added the progress view to all views.
Why not create a base class that has a lazy stored property of type UIProgressView ? Optionally you can have two methods setProgressViewHidden(hidden : Bool) in order to easily show and hide the progress view and setProgress(progress : Float) to update the progress. Then all your view controllers can subclass this base class and conveniently interact with the progress view.
class ProgressViewController : UIViewController {
lazy var progressView : UIProgressView = {
[unowned self] in
var view = UIProgressView(frame: CGRectMake(0, 20, self.view.frame.size.width, 3))
view.progress = 0.5
view.trackTintColor = UIColor.lightGrayColor()
view.tintColor = UIColor.redColor()
self.view.addSubview(view)
return view
}()
}
To read more about lazy stored properties, check: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html