MDCTabBarViewController Get Bar To Top Position - material-components

I'm using iOS Material components in my app and I came across the Tabs here. Implementing it was easy and after I made a few minor changes to match my design the result was what I wanted:
I achieved this using the following code:
func setupTabBar() {
let customTabBar = MDCTabBar(frame: view.bounds)
customTabBar.items = [
UITabBarItem(title: "Cards", image: nil, tag: 0),
UITabBarItem(title: "Mobile", image: nil, tag: 0)
]
customTabBar.itemAppearance = .titledImages
customTabBar.alignment = .justified
customTabBar.itemAppearance = .titles
//colors
customTabBar.tintColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
customTabBar.selectedItemTintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
customTabBar.unselectedItemTintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
customTabBar.barTintColor = UIColor(red:0.00, green:0.33, blue:0.59, alpha:1.0)
tabBarContainerView.addSubview(customTabBar)
customTabBar.sizeToFit()
tabBarContainerView.layer.shadowOffset = CGSize(width: 0, height: 2)
tabBarContainerView.layer.shadowRadius = 8
tabBarContainerView.layer.shadowOpacity = 0.5
tabBarContainerView.layer.shadowColor = UIColor.black.cgColor
}
However, this is just the Tar bar, I need to have a functional tab view where the user can switch between the two pages. No problem, as the docs say just inherit from MDCTabBarViewController, this ensure my pages are being tabbed, but this also added another tab bar at the bottom, which is fine, now I do not net to create one myself, so I removed mine but now I would just like it to bo the top but cannot find a property to set to get it in the top position:
and here is the code for that:
func setupTabViews() {
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let cardsVC = storyboard.instantiateViewController(withIdentifier: "cardsWalletViewController")
let mobileVC = storyboard.instantiateViewController(withIdentifier: "mobileWalletViewController")
let vcArray = [cardsVC, mobileVC]
viewControllers = vcArray
let childVC = viewControllers.first
selectedViewController = childVC
tabBar?.delegate = self
tabBar?.items = [
UITabBarItem(title: "Cards", image: nil, tag: 0),
UITabBarItem(title: "Mobile", image: nil, tag: 0)
]
tabBar?.itemAppearance = .titledImages
tabBar?.alignment = .justified
tabBar?.itemAppearance = .titles
//colors
tabBar?.tintColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
tabBar?.selectedItemTintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
tabBar?.unselectedItemTintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
tabBar?.barTintColor = UIColor(red:0.00, green:0.33, blue:0.59, alpha:1.0)
tabBar?.selectedItem = tabBar?.items.first
}
I noted that on the documentation for MDCTabBarViewController it will create a bottom-anchored tab bar, but how does one use it in the way they clearly illustrate on the 'Tabs' components example where the tabs are located at the top?

Related

SwiftUI & Contentful Client Error: Type of expression is ambiguous without more context

The code below shows the code I am using to fetch data from Contentful API to my SwiftUI app. However, by calling the function client.fetchArray, I got an error message saying that the type of expression is ambiguous. I tried look up the documentation and the example code for client.fetchArray uses the exact parameters and hence I assume the error message should be gone? Can anyone please help me out here! I am super confused! Thank you!
import SwiftUI
import Contentful
import Combine
let client = Client(spaceId: "qqq2yu4vbtpl", environmentId: "master", accessToken: "LurTiklxcr0ARTpI9z7GVTz-mjMO6OFVsoSZ8V7GJ4c")
//let client = Client(spaceId: "0ge8xzmnbp2c", accessToken: "03010b0d79abcb655ca3fda453f2f493b5472e0aaa53664bc7dea5ef4fce2676")
func getArray(id: String, completion: #escaping([Entry]) -> ()) {
let query = Query.where(contentTypeId: id)
client.fetchArray(of: Entry.self, matching: query) { result in
switch result {
case .success(let array):
DispatchQueue.main.async {
completion(array.items)
}
case .error(let error):
print(error)
}
print(result)
}
}
class CourseStore: ObservableObject {
#Published var courses: [Course] = courseData
init() {
let colors = [#colorLiteral(red: 0.3635728061, green: 0, blue: 0.8927152753, alpha: 0.8470588235), #colorLiteral(red: 0.2461163998, green: 0.3063159585, blue: 1, alpha: 0.8470588235), #colorLiteral(red: 0.3414418995, green: 0.653152287, blue: 0.405282855, alpha: 0.8470588235), #colorLiteral(red: 0.8906980753, green: 0.684363246, blue: 0, alpha: 0.8470588235), #colorLiteral(red: 0.5725490451, green: 0, blue: 0.2313725501, alpha: 1), #colorLiteral(red: 0.5058823824, green: 0.3372549117, blue: 0.06666667014, alpha: 1), #colorLiteral(red: 0.1411764771, green: 0.3960784376, blue: 0.5647059083, alpha: 1), #colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1), #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)]
getArray(id: "course") { (items) in
items.forEach { (item) in
self.courses.append(Course(
title: item.fields["title"] as! String,
subtitle: item.fields["subtitle"] as! String,
image: item.fields["image"] as! String,
logo: item.fields["logo"] as! String,
color: colors.randomElement()!,
show: false))
}
}
}
}
For my Data.swift file, I have written the following code fyi:
import SwiftUI
struct Post: Codable, Identifiable {
var id = UUID()
var title : String
var body : String
}
class API {
func getPosts(completion: #escaping ([Post]) -> ()) {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
guard let data = data else { return }
let posts = try! JSONDecoder().decode([Post].self, from: data)
// Must be async to run API call
DispatchQueue.main.async {
completion(posts)
}
}
.resume()
}
}
And here is the code for my DataStore.swift, think it will be helpful for my data fetching script:
import SwiftUI
import Combine
class DataStore: ObservableObject {
#Published var posts: [Post] = []
init() {
getPosts()
}
func getPosts() {
API().getPosts { (posts) in
self.posts = posts
}
}
}

How can I highlight a UIImageView from an array when the view is tapped/clicked

I have an array of UIImageViews (8 of them) and I want to highlight which ever one is tapped or clicked. NOTE: It doesn't have to be highlighted I am open to other way as well. As you will see in my code I tried a different way.
As you will see below I tried to access the index.layer.borderColor and then created and if else statement. next I created a UITapGuestureRecognizer function and called this in the viewDidLoad. App is crashing with an "out of bounds" error.
func setHandleTap() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
imageArray[index(ofAccessibilityElement: 0..<7)].addGestureRecognizer(tapGesture)
}
#objc func handleTap() { // this funtion checks to see what the boarder image is and chages it to other once tapped.
if imageArray[index(ofAccessibilityElement: 0..<7)].layer.borderColor == #colorLiteral(red: 0.01680417731, green: 0.1983509958, blue: 1, alpha: 1).cgColor {
imageArray[index(ofAccessibilityElement: 0..<7)].layer.borderColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor
}else{
imageArray[index(ofAccessibilityElement: 0..<7)].layer.borderColor = #colorLiteral(red: 0.01680417731, green: 0.1983509958, blue: 1, alpha: 1).cgColor
}
}
}
I call the setHandelTap() in ViewDidLoad hoping it would run allowing the image to be highlighted.
Iterating through the array should be enough for this.
Maybe something like this in your handleTap():
let desiredColor = #colorLiteral(red: 0.01680417731, green: 0.1983509958, blue: 1, alpha: 1).cgColor
for imageView in imageArray {
if imageView.layer.borderColor == desiredColor {
imageView.layer.borderColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor // Sets it to Black Color.
}
}
Note: By the way there is no need for the else code because the current color is already the color you wanted to set on the else

Graphic Points are not working outside of viewDidLoad

I've created a Graphic Class to show for user a graphic points choose accordingly with user information.
If I use the graphicView object and add points at viewDidLoad, the graphic is presented correctly, if not there, the graphic presents no data.
See below the code for the Graphics and the code when I am requesting to mark the points.
class GraphView: UIView {
private struct Constants {
static let cornerRadiusSize = CGSize(width: 8.0, height: 8.0)
static let margin: CGFloat = 40.0
static let topBorder: CGFloat = 30
static let bottomBorder: CGFloat = 40
static let colorAlpha: CGFloat = 0.3
static let circleDiameter: CGFloat = 5.0
}
//1 - the properties for the gradient
var startColor: UIColor = UIColor.rgb(red: 14, green: 40, blue: 80)
var endColor: UIColor = UIColor.rgb(red: 14, green: 40, blue: 80)
//Weekly sample data
var graphPoints: [Int] = [0]
override func draw(_ rect: CGRect) {
let width = rect.width
let height = rect.height
let path = UIBezierPath(roundedRect: rect,
byRoundingCorners: UIRectCorner.allCorners,
cornerRadii: Constants.cornerRadiusSize)
path.addClip()
//2 - get the current context
let context = UIGraphicsGetCurrentContext()!
let colors = [startColor.cgColor, endColor.cgColor]
//3 - set up the color space
let colorSpace = CGColorSpaceCreateDeviceRGB()
//4 - set up the color stops
let colorLocations: [CGFloat] = [0.0, 1.0]
//5 - create the gradient
let gradient = CGGradient(colorsSpace: colorSpace,
colors: colors as CFArray,
locations: colorLocations)!
//6 - draw the gradient
var startPoint = CGPoint.zero
var endPoint = CGPoint(x: 0, y: self.bounds.height)
context.drawLinearGradient(gradient,
start: startPoint,
end: endPoint,
options: CGGradientDrawingOptions(rawValue: 0))
//calculate the x point
let margin = Constants.margin
let columnXPoint = { (column:Int) -> CGFloat in
//Calculate gap between points
let spacer = (width - margin * 2 - 4) / CGFloat((self.graphPoints.count - 1))
var x: CGFloat = CGFloat(column) * spacer
x += margin + 2
return x
}
// calculate the y point
let topBorder: CGFloat = Constants.topBorder
let bottomBorder: CGFloat = Constants.bottomBorder
let graphHeight = height - topBorder - bottomBorder
let maxValue = graphPoints.max()!
let columnYPoint = { (graphPoint:Int) -> CGFloat in
var y:CGFloat = CGFloat(graphPoint) / CGFloat(maxValue) * graphHeight
y = graphHeight + topBorder - y // Flip the graph
return y
}
// draw the line graph
UIColor.white.setFill()
UIColor.white.setStroke()
//set up the points line
let graphPath = UIBezierPath()
//go to start of line
graphPath.move(to: CGPoint(x:columnXPoint(0), y:columnYPoint(graphPoints[0])))
//add points for each item in the graphPoints array
//at the correct (x, y) for the point
for i in 1..<graphPoints.count {
let nextPoint = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints[i]))
graphPath.addLine(to: nextPoint)
}
//Create the clipping path for the graph gradient
//1 - save the state of the context (commented out for now)
context.saveGState()
//2 - make a copy of the path
let clippingPath = graphPath.copy() as! UIBezierPath
//3 - add lines to the copied path to complete the clip area
clippingPath.addLine(to: CGPoint(x: columnXPoint(graphPoints.count - 1), y:height))
clippingPath.addLine(to: CGPoint(x:columnXPoint(0), y:height))
clippingPath.close()
//4 - add the clipping path to the context
clippingPath.addClip()
let highestYPoint = columnYPoint(maxValue)
startPoint = CGPoint(x:margin, y: highestYPoint)
endPoint = CGPoint(x:margin, y:self.bounds.height)
context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(rawValue: 0))
context.restoreGState()
//draw the line on top of the clipped gradient
graphPath.lineWidth = 3.0
graphPath.stroke()
//Draw the circles on top of graph stroke
for i in 0..<graphPoints.count {
var point = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints[i]))
point.x -= Constants.circleDiameter / 2
point.y -= Constants.circleDiameter / 2
let circle = UIBezierPath(ovalIn: CGRect(origin: point, size: CGSize(width: Constants.circleDiameter, height: Constants.circleDiameter)))
circle.fill()
}
//Draw horizontal graph lines on the top of everything
let linePath = UIBezierPath()
//top line
linePath.move(to: CGPoint(x:margin, y: topBorder))
linePath.addLine(to: CGPoint(x: width - margin, y:topBorder))
//center line
linePath.move(to: CGPoint(x:margin, y: graphHeight/2 + topBorder))
linePath.addLine(to: CGPoint(x:width - margin, y:graphHeight/2 + topBorder))
//bottom line
linePath.move(to: CGPoint(x:margin, y:height - bottomBorder))
linePath.addLine(to: CGPoint(x:width - margin, y:height - bottomBorder))
let color = UIColor(white: 1.0, alpha: Constants.colorAlpha)
color.setStroke()
linePath.lineWidth = 1.0
linePath.stroke()
}
}
I am trying to mark the points accordingly with the user input - see below:
func budgetAvailableCalculationFunction() {
let bankValue = (userSalary as NSString).integerValue
let bankPorcentage: Int = 100
let expenses = (userExpenses as NSString).integerValue
let calculation1: Int = expenses * bankPorcentage
let calculation2: Int = calculation1 / bankValue
let cashPorcentageAvailable = calculation2
let value: [Int] = [expenses]
self.setupGraphy(points: value)
progressView.progress = 0.0
progress.completedUnitCount = Int64(cashPorcentageAvailable)
progressView.setProgress(Float(self.progress.fractionCompleted), animated: true)
progressView.progressTintColor = UIColor.rgb(red: 239, green: 75, blue: 92)
progressView.backgroundColor = UIColor.rgb(red: 239, green: 75, blue: 92)
progressView.trackTintColor = .white
progressView.clipsToBounds = false
progressView.translatesAutoresizingMaskIntoConstraints = false
progressView.layer.cornerRadius = 0
porcentageLabelForMonth.text = "\(Int(self.progress.fractionCompleted * 100)) %"
}
The setupGraphy is just a function that returns an array of indexes that the user adds it.
Please note that using the same function at the viewDidLoad works:
self.setupGraphy(points: [100, 400, 200, 250])
enter image description here
Anyone?
Code for viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.rgb(red: 245, green: 246, blue: 250)
tableView.delegate = self
tableView.dataSource = self
tableView.register(LastTransactionsCell.self, forCellReuseIdentifier: "LastTransactionsCell")
tableView.backgroundColor = .clear
let now = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy - LLLL"
let nameOfMonth = dateFormatter.string(from: now)
currentMonthLabel.text = nameOfMonth
setupUIXViews()
fetchUserInfo()
//static data for now
*self.setupGraphy(points: [100, 400, 200, 250])*
DispatchQueue.main.asyncAfter(deadline: .now() + 2){
self.budgetAvailableCalculationFunction()
}
}
The way I managed to fix (seems like a temporary fix) is by creating a new Int variable at the top of the project and passing the fetch data from the DB to these variables. That way I can use them at viewDidLoad instead of inside the fetch function.

Create an array of images then press button to remove one of the images in the array

I am creating a drawing app in Swift. I which every time a user draws a line on the UIImageVIew it rasterizes a new image. I would like to create an undo button which goes to the previous image in the array.
Here is my code -
func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
// 1
UIGraphicsBeginImageContext(view.frame.size)
tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))
let context = UIGraphicsGetCurrentContext()
// 2
context?.move(to: CGPoint(x: fromPoint.x, y: fromPoint.y))
context?.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))
// 3
context?.setBlendMode(CGBlendMode.normal)
context?.setLineCap(CGLineCap.round)
context?.setLineWidth(brushWidth)
context?.setStrokeColor(UIColor(red:red, green:green, blue:blue, alpha: 1.0).cgColor)
// 4
context?.strokePath()
// 5
tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
tempImageView.alpha = opacity
UIGraphicsEndImageContext()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = true
if let touch = touches.first {
let currentPoint = touch.location(in: view)
drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
lastPoint = currentPoint
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if !swiped {
// draw a single point
drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
}
// Merge tempImageView into mainImageView
UIGraphicsBeginImageContext(mainImageView.frame.size)
mainImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height),blendMode: CGBlendMode.normal, alpha: 1.0)
tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: CGBlendMode.normal, alpha: opacity)
mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
tempImageView.image = nil
floaty.isHidden = false
postBtn.isHidden = false
resetBtn.isHidden = false
undoBtn.isHidden = false
dismissBtn.isHidden = false
}

Multiple MapKit overlay colours from array

How do I make each bubble a different colour?
Here is my array of colours:
var tableColors = [UIColor(red: 220.0/255.0, green: 95.0/255.0, blue: 19.0/255.0, alpha: 0.9), // Rust
UIColor(red: 255.0/255.0, green: 191.0/255.0, blue: 59.0/255.0, alpha: 0.9), // Yellow
UIColor(red: 255.0/255.0, green: 91.0/255.0, blue: 51.0/255.0, alpha: 0.9), // Red
UIColor(red: 0.0/255.0, green: 160.0/255.0, blue: 174.0/255.0, alpha: 0.9), // KAL
UIColor(red: 0.0/255.0, green: 121.0/255.0, blue: 95.0/255.0, alpha: 0.9), // Green
UIColor(red: 104.0/255.0, green: 68.0/255.0, blue: 88.0/255.0, alpha: 0.9), // Purple
UIColor(red: 244.0/255.0, green: 97.0/255.0, blue: 119.0/255.0, alpha: 0.9), // Pink
UIColor(red: 1.0/255.0, green: 116.0/255.0, blue: 200.0/255.0, alpha: 0.9), // Blue
UIColor(red: 0.0/255.0, green: 188.0/255.0, blue: 111.0/255.0, alpha: 0.9), // Light Green
UIColor(red: 0.0/255.0, green: 196.0/255.0, blue: 179.0/255.0, alpha: 0.9), // Aqua
UIColor(red: 246.0/255.0, green: 50.0/255.0, blue: 62.0/255.0, alpha: 0.9)] // Hot
Here is the code which creates the locations from CloudKit data.
func fetchBubble() {
let query = CKQuery(recordType: "Collection", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
publicDB!.performQuery(query, inZoneWithID: nil) { results, error in
if error == nil {
for collection in results! {
let collectionLocation = collection.valueForKey("Location") as? CLLocation
let collectionName = collection.valueForKey("Name") as! String
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if CLLocationManager.isMonitoringAvailableForClass(CLCircularRegion.self) {
let intrepidLat: CLLocationDegrees = (collectionLocation?.coordinate.latitude)!
let intrepidLong: CLLocationDegrees = (collectionLocation?.coordinate.longitude)!
let title = collectionName
let coordinate = CLLocationCoordinate2DMake(intrepidLat, intrepidLong)
let regionRadius = 50.0
let region = CLCircularRegion(center: CLLocationCoordinate2D(latitude: coordinate.latitude,
longitude: coordinate.longitude), radius: regionRadius, identifier: title)
self.locationManager.startMonitoringForRegion(region)
let restaurantAnnotation = MKPointAnnotation()
self.mapView.addAnnotation(restaurantAnnotation)
restaurantAnnotation.coordinate = coordinate
let circle = MKCircle(centerCoordinate: coordinate, radius: regionRadius)
self.mapView.addOverlay(circle)
self.numberOfObjectsInMyArray()
}
else {
print("System can't track regions")
}
})
}
}
else {
print(error)
}
}
}
Then I draw the circles with the following code
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let circleRenderer = MKCircleRenderer(overlay: overlay)
for index in 1...5 {
circleRenderer.fillColor = self.tableColors[index + 1]
}
return circleRenderer
}
I've tried a few ways of looping through the colours but at the moment it is applying the same colour from the array to each of the bubbles.
Any help would be hugely appreciated!
In the rendererForOverlay callout method you are setting same last color to all the circleRenderer. So instead of your code
for index in 1...5 {
circleRenderer.fillColor = self.tableColors[index + 1]
}
replace with
circleRenderer.fillColor = self.randomTableColors()
And implement this method to give the random color as follow.
func randomTableColors() -> UIColor {
let randomIndex = Int(arc4random_uniform(UInt32(tableColors.count)))
return tableColors[randomIndex]
}

Resources