There doesn't appear to be support for UITabBar in SwiftUI. Workarounds? - uitabbar

SwiftUI doesn't appear to support UITabBar. How can I integrate that capability?
Merely wrapping the view like one would a (eg) MKMapView, doesn't work because of its need for deep integration with NavigationView. Using UINavigationView is too un-SwiftUI-ish.

The 'TabbedView' is the closest thing. It can be used similar to the following:
struct TabView : View {
#State private var selection = 1
var body: some View {
TabbedView (selection: $selection) {
InboxList()
.tabItemLabel(selection == 1 ? Image("second") : Image("first"))
.tag(1)
PostsList()
.tabItemLabel(Image("first"))
.tag(2)
Spacer()
.tabItemLabel(Image("first"))
.tag(3)
Spacer()
.tabItemLabel(Image("second"))
.tag(4)
}
}
}

If you aren't happy with TabbedView, you can always roll your own! Here's a quick base implementation:
import SwiftUI
struct ContentView : View {
let tabs = [TabItemView(title: "Home", content: { Text("Home page text") }), TabItemView(title: "Other", content: { Text("Other page text") }), TabItemView(title: "Pictures", content: { Text("Pictures page text") })]
var body: some View {
TabBar(tabs: tabs, selectedTab: tabs[0])
}
}
struct TabItemView<Content> : Identifiable where Content : View {
var id = UUID()
var title: String
var content: Content
init(title: String, content: () -> Content) {
self.title = title
self.content = content()
}
var body: _View { content }
typealias Body = Never
}
struct TabBar<Content>: View where Content : View {
let tabButtonHeight: Length = 60
var tabs: [TabItemView<Content>]
#State var selectedTab: TabItemView<Content>
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0) {
self.selectedTab.content.frame(width: geometry.size.width, height: geometry.size.height - self.tabButtonHeight)
Divider()
HStack(spacing: 0) {
ForEach(self.tabs) { tab in
Button(action: { self.selectedTab = tab}) {
Text(tab.title)
}.frame(width: geometry.size.width / CGFloat(Double(self.tabs.count)), height: self.tabButtonHeight)
}
}
.background(Color.gray.opacity(0.4))
}
.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}

UITabBar seems to be working now on Xcode 13.3, SwiftUI 3, iOS15+
It works even though I didn't import UIKit, not sure if that has any effect but it's working for me
struct LandingView: View {
#Binding var selectedTab: String
//hiding tab bar
init(selectedTab: Binding<String>) {
self._selectedTab = selectedTab
UITabBar.appearance().isHidden = true
}
var body: some View {
//Tab view with tabs
TabView(selection: $selectedTab) {
//Views
Home()
.tag("Home")
PlaylistView()
.tag("My Playlists")
HistoryView()
.tag("History")
}
}
}

I misstated the question as I was trying to make ToolBar... below is the code I ended up with... thanks to all.
struct ToolBarItem : Identifiable {
var id = UUID()
var title : String
var imageName : String
var action: () -> Void
}
struct TooledView<Content> : View where Content : View{
var content : Content
var items : [ToolBarItem]
let divider = Color.black.opacity(0.2)
init(items : [ToolBarItem], content: () -> Content){
self.items = items
self.content = content()
}
var body : some View{
VStack(spacing: 0){
self.content
self.divider.frame(height: 1)
ToolBar(items: self.items).frame(height: ToolBar.Height)
}
}
}
struct ToolBar : View{
static let Height : Length = 60
var items : [ToolBarItem]
var body: some View {
GeometryReader { geometry in
HStack(spacing: 0){
ForEach(self.items){ item in
Button(action: item.action){
Image(systemName: item.imageName).imageScale(.large)
Text(item.title).font(.caption)
}.frame(width: geometry.size.width / CGFloat(Double(self.items.count)))
}
}
.frame(height: ToolBar.Height)
.background(Color.gray.opacity(0.10))
}
}
}

Related

How To Put A Number In Front Of Every Suggestion Corretcly?

Detail Of The Problem
As title, I am using Google App Script and Google Docs API's Batchupdate, trying to put number in front of every suggestion. However, I can place it correctly at the very first one, but it starts to deviate after the first one.
Result I Currently Have
Please refer to the image below.
What I have Tried
Below is the snippet I currently have
function markNumberInFrontOfMark(fileID) {
fileID = "MYFILEID";
let doc = Docs.Documents.get(fileID);
let requests = doc.body.content.flatMap(content => {
if (content.paragraph) {
let elements = content.paragraph.elements;
return elements.flatMap(element => element.textRun.suggestedDeletionIds ? {
insertText: {
text: "(1)",
location: {
index: element.startIndex
}
}
} : []);
}
return [];
});
Docs.Documents.batchUpdate({requests}, fileID);
return true;
}
Result I Want To Have
Please refer to the image below
Post I Refer to
How to change the text based on suggestions through GAS and Google DOC API
Here is an example of how to insert text. In this case I am adding 3 characters "(1)" for example. If the number of additions exceeds 9 you will have to adjust the number of characters added.
function markNumberInFrontOfMark() {
try {
let doc = DocumentApp.getActiveDocument();
let id = doc.getId();
doc = Docs.Documents.get(id);
let contents = doc.body.content;
let requests = [];
let num = 0;
contents.forEach( content => {
if( content.paragraph ) {
let elements = content.paragraph.elements;
elements.forEach( element => {
if( element.textRun.suggestedDeletionIds ) {
num++;
let text = "("+num+")"
let request = { insertText: { text, location: { index: element.startIndex+3*(num-1) } } };
requests.push(request);
}
}
);
}
}
);
if( requests.length > 0 ) {
Docs.Documents.batchUpdate({requests}, id);
}
}
catch(err) {
console.log(err)
}
}
And the resulting updated document.

Some MapKit questions in SwiftUI

I'm looking to make a setting view in my app where I could define 3 map parameters :
Map center initial position
Map initial zoom
Map initial type
Then show an example map and save in into the #AppStorage
My initial position is determined from an array of data which include :
Place name
Place Latitude
Place Longitude
Latitude and Longitude is find via a function which return a Double? :
func getApLat(ApName: String) -> Double?{
guard let foundAirport = FR_airportsDB.first(where: {$0.Icao == ApName}),
let lat = Double(foundAirport.Latitude) else { return nil }
return lat
}
func getApLong(ApName: String) -> Double?{
guard let foundAirport = FR_airportsDB.first(where: {$0.Icao == ApName}),
let longit = Double(foundAirport.Longitude) else { return nil }
return longit
}
Now my settingMapView is defined as follow :
struct MapOptionView: View {
#State private var showingAlert = false
#State private var latDouble = getApLat(ApName: UserDefaults.standard.string(forKey: "MAP_CenterInit") ?? "LFLI")
#State private var longDouble = getApLong(ApName: UserDefaults.standard.string(forKey: "MAP_CenterInit") ?? "LFLI")
#State private var typeExemple: MKMapType = getTypeFromUD()
#State private var exampleZoom: Int = 5
#AppStorage("MAP_CenterInit") private var MapCenterAirport = ""
#AppStorage("MAP_ZoomInit") private var MapZoom = 2
#AppStorage("MAP_TypeInit") private var MapType = 1
var body: some View {
List{
Section(header: Text("Initial location")){
HStack{
TextField("ICAO", text: $MapCenterAirport)
.padding()
.background(.white)
.cornerRadius(20.0)
.keyboardType(.default)
.textCase(.uppercase)
.onReceive(Just(MapCenterAirport)) { inputValue in
if inputValue.count > 4 {
self.MapCenterAirport.removeLast()
}
}
.disableAutocorrection(true)
.textCase(.uppercase)
if MapCenterAirport != "" {
Button {
//Here I update the center data after get it from the function
latDouble = getApLat(ApName: MapCenterAirport)
longDouble = getApLong(ApName: MapCenterAirport)
} label: {
Text("Check")
}
}
}
}
// Section where Zoom is defined
Section(header: Text("Initial zoom")){
HStack {
if MapZoom == 1{
Text("Zoom : Low")
}else if MapZoom == 2{
Text("Zoom : Medium")
}else if MapZoom == 3{
Text("Zoom : Large")
}
Spacer()
Stepper("", value: $MapZoom, in: 1...3)
}.padding(.vertical)
}
// Section where Type is defined
Section(header: Text("Initial type")){
HStack {
if MapType == 1{
Text("Type : Standard")
}else if MapType == 2{
Text("Type : Satellite")
}else if MapType == 3{
Text("Type : Satellite-flyover")
}else if MapType == 4{
Text("Type : Hybrid")
}else if MapType == 5{
Text("Type : Hybrid-flyover")
}
Spacer()
Stepper("", value: $MapType, in: 1...5)
.frame(width: 60)
.padding(.horizontal, 20)
}.padding(.vertical)
}
// Section where Exemple is Show but need to clickButton to update map ...
Section(header: Text("Example")){
Button {
//Here is zoom adaptation regarding stepper choice from $MapZoom
if MapZoom == 1{
exampleZoom = 60000
}else if MapZoom == 2{
exampleZoom = 110000
}else if MapZoom == 3{
exampleZoom = 160000
}
//Here is type adaptation regarding stepper choice from $MapType
if MapType == 1{
typeExemple = .standard
}else if MapType == 2{
typeExemple = .satellite
}else if MapType == 3{
typeExemple = .satelliteFlyover
}else if MapType == 4{
typeExemple = .hybrid
}else if MapType == 5{
typeExemple = .hybridFlyover
}
} label: {
HStack {
Text("Update example")
}
}
//Here is my Map definition
MapViewSetting(mapType: $typeExemple,
funcLat: $latDouble,
funcLong: $longDouble,
funcZoom: $exampleZoom
)
.edgesIgnoringSafeArea(.all)
.frame(height: 300)
}
}.navigationTitle("Map options")
.navigationBarItems(trailing: Button(action: {
showingAlert = true
//Here we save the 3 value into the AppStorage
MapCenterAirport = MapCenterAirport.uppercased()
UserDefaults.standard.set(self.MapCenterAirport, forKey: "MAP_CenterInit")
UserDefaults.standard.set(self.MapZoom, forKey: "MAP_ZoomInit")
UserDefaults.standard.set(self.MapType, forKey: "MAP_TypeInit")
}, label: {
HStack {
Text("Save")
}
})).alert("Informations saved !\nMust have to restart the app to apply it.", isPresented: $showingAlert) {
Button("OK", role: .cancel) { }
}
}
}
And finally I've my map struct defined as follow after following some post find on this forum.
struct MapViewSetting: UIViewRepresentable {
#Binding var mapType: MKMapType
#Binding var funcLat: Double
#Binding var funcLong: Double
#Binding var funcZoom: Int
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView(frame: .zero)
let center = CLLocationCoordinate2D(latitude: funcLat, longitude: funcLong)
let region = MKCoordinateRegion(center: center,
latitudinalMeters: CLLocationDistance(funcZoom),
longitudinalMeters: CLLocationDistance(funcZoom)
)
mapView.setRegion(region, animated: true)
mapView.mapType = mapType
mapView.showsScale = true
mapView.showsTraffic = false
mapView.showsCompass = true
mapView.showsUserLocation = false
mapView.showsBuildings = false
return mapView
}
func updateUIView(_ view: MKMapView, context: Context) {
view.mapType = self.mapType
}
}
But Two big mistake with my code :
First I got an error here :
MapViewSetting(mapType: $typeExemple,
/*regionFunc: $region,*/
funcLat: $latDouble ?? 46.192001,
funcLong: $longDouble ?? 6.26839,
funcZoom: $exampleZoom
)
On funcLat : and funcLong which say : Cannot convert value of type 'Binding<Double?>' to expected argument type 'Binding<Double>'
And If I define manually the Lat and Long in my code to test the two other parameters I could only change the type but not the Zoom ...
Hope to be as clear as I can
Thanks

SwiftUI: How to parse JSON arrays from url using Alamofire?

Environments:macOS Ventura Developer Beta 3Xcode 14.0 beta 3MacBook Pro (13 inch M1, 2020)
References: https://www.youtube.com/watch?v=aMes-DVVJg4https://github.com/TuenTuenna/SwiftUI_Alamofire_RandomUser_tutorial/tree/01_Alamofire
Hello. I'm building a simple ESD for macOS using SwiftUI.
I'm gonna get informations from json by my server, and show informations what it got from api like this:
GameESD_View
Game Informations, installation
but it doesn't show anything like this:
Not working
This is my code. You can see entire code here: https://github.com/bmplatina/BitmapMac
game.json (JSON file to decode and parse, watch http://developer.prodbybitmap.com/game.json):
{
"games": [
{
"gameIndex": 0,
"gameTitle": "The Humanity",
"gamePlatform": "Windows, macOS",
"gameEngine": "Unity",
"gameGenre": "어드벤처",
"gameDeveloper": "입학했더니 무한 팀플과 과제가 쌓여버린 건에 대하여",
"gamePublisher": "Bitmap Production",
"isEarlyAccess": true,
"gameReleasedDate": 20211210,
"gameWebsite": "http://prodbybitmap.com/wiki/The%20Humanity",
"gameImageURL": "http://www.prodbybitmap.com/w/images/9/99/TheHumanityPoster1.png",
"gameDescription": "Desc"
},
{
"gameIndex": 1,
"gameTitle": "OX",
"gamePlatform": "Windows",
"gameEngine": "Unreal Engine 5",
"gameGenre": "몰입형 VR 퍼즐 게임",
"gameDeveloper": "Team. Assertive",
"gamePublisher": "ENTER, Bitmap Production",
"isEarlyAccess": true,
"gameReleasedDate": 20220624,
"gameWebsite": "http://prodbybitmap.com/wiki/OX",
"gameImageURL": "http://www.prodbybitmap.com/w/images/f/f9/OX_CMYK.JPG",
"gameDescription": "Desc"
}
]
}
GameESD_View.swift:
import Foundation
import SwiftUI
import URLImage
struct GameESD_View: View {
#State private var searchField: String = ""
#ObservedObject var gameViewmodel = gameInfoViewmodel()
let gameInfoExam = exampleGameInfo()
let columnLayout = Array(repeating: GridItem(), count: 4)
var body: some View {
VStack {
ZStack {
Rectangle()
.fill(Color.init(hex: "4188F1"))
.frame(height: 42)
.shadow(radius: 4)
HStack {
Spacer()
Image("bitmapWebLogo")
.resizable()
.scaledToFit()
.frame(height: 30)
Spacer()
// if true {
// Text("Online")
// }
// else {
// Text("Offline Mode")
// }
TextField("Filter".localized(), text: $searchField)
}
}
ScrollView {
VStack(alignment: .leading) {
Text("Seoul Institute of the Arts Collection")
.font(.largeTitle)
.bold()
.padding([.top, .leading])
Text("collection.")
.padding(.leading)
Divider()
LazyVGrid(columns: columnLayout, alignment: .center, spacing: 2) {
// List(gameViewmodel.gameInfos) { aGameInfos in }
ForEach(0..<gameViewmodel.gameInfos.count, id: \.self) { aGameInfos in
GameButtons(gameViewmodel.gameInfos[aGameInfos])
}
}
Divider()
Text("Other Games")
.font(.largeTitle)
.bold()
.padding([.top, .leading])
Text("여러 창작자의 다양한 인디 컨텐츠.")
.padding(.leading)
}
}
.navigationTitle("Games".localized())
}
}
}
struct GameButtons: View {
#State private var showingPopover = false
var gameInfos: gameInfo
init(_ gameInfos: gameInfo) {
self.gameInfos = gameInfos
}
var body: some View {
VStack { }
Button {
showingPopover = true
} label: {
ZStack {
Image("unknownImage")
.resizable()
.scaledToFit()
.frame(width: 300)
URLImage(URL(string: gameInfos.gameImageURL)!) { image in
image
.resizable()
.scaledToFit()
.frame(width:300)
}
LinearGradient(gradient: Gradient(colors: [.clear, Color.black.opacity(0.5)]), startPoint: .top, endPoint: .bottom).frame(width: 300, height: 424)
VStack(alignment: .leading) {
Spacer()
Text(gameInfos.gameTitle)
.foregroundColor(.white)
.font(Font.largeTitle)
.bold()
Divider()
Text("Dev".localized() + ": " + gameInfos.gameDeveloper)
.foregroundColor(.white)
}
.frame(width:256)
.padding()
}
.cornerRadius(24)
.shadow(radius: 4)
.padding()
}
.buttonStyle(PlainButtonStyle())
.sheet(isPresented: $showingPopover) {
VStack(alignment: .leading) {
HStack {
VStack(alignment: .leading) {
Text("Bitmap Games")
.font(Font.largeTitle)
.bold()
Text("Bitmap Store".localized())
}
Spacer()
Button(action: { showingPopover = false }) {
Image(systemName: "x.circle")
.font(.title2)
}
.padding()
.background(Color.red)
.foregroundColor(.white)
.clipShape(Capsule())
.buttonStyle(PlainButtonStyle())
}
.padding()
GameDetailsView(gameIndex: gameInfos.gameIndex)
}
.frame(width: 1000, height: 600)
.fixedSize()
}
}
}
struct GameDetailsView: View {
#State private var installAlert = false
let gameInfo: exampleGameInfo = exampleGameInfo()
var gameIndex: Int
var body: some View {
Text("Temp")
}
}
#if DEBUG
struct ESD_Previews: PreviewProvider {
static var previews: some View {
GameESD_View()
// digitalArtsFestivalWebView()
}
}
#endif
FetchGameInformation.swift:
import Foundation
import Combine
import Alamofire
struct gameInfo: Codable, Identifiable {
var id = UUID()
var gameIndex: Int
var gameTitle: String
var gamePlatform: String
var gameEngine: String
var gameGenre: String
var gameDeveloper: String
var gamePublisher: String
var isEarlyAccess: Bool
var gameReleasedDate: Int
var gameWebsite: String
var gameImageURL: String
var gameDescription: String
var gameImage: URL {
get {
URL(string: gameImageURL)!
}
}
static func makeDummy() -> Self {
print(#fileID, #function, #line, "")
return gameInfo(gameIndex: 1, gameTitle: "OX", gamePlatform: "Windows", gameEngine: "Unreal Engine 5", gameGenre: "몰입형 VR 퍼즐 게임", gameDeveloper: "Team. Assertive", gamePublisher: "ENTER, Bitmap Production", isEarlyAccess: true, gameReleasedDate: 20220624, gameWebsite: "http://prodbybitmap.com/wiki/OX", gameImageURL: "http://www.prodbybitmap.com/w/images/f/f9/OX_CMYK.JPG", gameDescription: "This is Description")
}
}
struct gameResult: Codable, CustomStringConvertible {
var games: [gameInfo]
var description: String {
return "gameInfo.count: \(games.count)"
}
}
struct gamesResponse: Codable, CustomStringConvertible {
var games: [gameInfo]
var description: String {
return "games.count: \(games.count) / info : \(games[0].gameTitle)"
}
}
class gameInfoViewmodel: ObservableObject {
// MARK: Properties
var subscription = Set<AnyCancellable>()
#Published var gameInfos = [gameInfo]()
var url = "http://developer.prodbybitmap.com/game.json"
init() {
print(#fileID, #function, #line, "")
fetchGameInfo()
}
func fetchGameInfo() {
print(#fileID, #function, #line, "")
AF.request(url, method: .get)
.publishDecodable(type: gamesResponse.self)
.compactMap { $0.value }
.map { $0.games }
.sink(receiveCompletion: { completion in
print("Datastream Done")
}, receiveValue: { [weak self](receivedValue: [gameInfo]) in
guard let self = self else { return }
print("Data Value: \(receivedValue.description)")
self.gameInfos = receivedValue
}).store(in: &subscription)
}
}

Multiple NavigationLink with ForEach

I have a json file that contains an array of school classes name className, when I try to show the classes using Text(className[I]), I get this error:
Ambiguous subscript with base type '[String]' and index type '_'
(the i as in for loop).
Here is the code I am using
#EnvironmentObject private var userData: UserData
var classes: Classes
ForEach(self.userData.classes) { classes in
Section(header: Text(classes.schoolForm)) {
ForEach(self.classes.className.sorted(), id: \.self) { classNameId in
NavigationLink(destination: Text("Testing...")) {
Text(!self.classes.className[classNameId]) //The error shows here
}
}
}
}
The Answer is:
#EnvironmentObject private var userData: UserData
var classes: Classes
ForEach(self.userData.classes) { classes in
Section(header: Text(classes.schoolForm)) {
ForEach(self.classes.className.sorted(), id: \.self) { classNameId in
NavigationLink(destination: Text("Testing...")) {
Text(classNameId)
}
}
}
}
the classNameId is the data saved in the array of the classes

How to create notification badge in tableview

How to make a notification badge on the table view?
If there is new data it will display the badge on the table view, and if there is new data again, the badge will automatically be added.
I can strongly recommend you use the following Framework: swift-badge
use_frameworks!
target 'Your target name'
pod 'BadgeSwift', '~> 4.0'
Easy to use:
let badge = BadgeSwift()
view.addSubview(badge)
// Position the badge ...
Customization:
// Text
badge.text = "2"
// Insets
badge.insets = CGSize(width: 12, height: 12)
// Font
badge.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)
// Text color
badge.textColor = UIColor.yellow
// Badge color
badge.badgeColor = UIColor.black
// Shadow
badge.shadowOpacityBadge = 0.5
badge.shadowOffsetBadge = CGSize(width: 0, height: 0)
badge.shadowRadiusBadge = 1.0
badge.shadowColorBadge = UIColor.black
// No shadow
badge.shadowOpacityBadge = 0
// Border width and color
badge.borderWidth = 5.0
badge.borderColor = UIColor.magenta
// Customize the badge corner radius.
// -1 if unspecified. When unspecified, the corner is fully rounded. Default: -1.
badge.cornerRadius = 10
Also. If you don't want to use pods, here is the full and ready to use class It also includes functions I have created:
import UIKit
/**
Badge view control for iOS and tvOS.
Project home: https://github.com/marketplacer/swift-badge
*/
#IBDesignable public class BadgeSwift: UILabel {
/// Background color of the badge
#IBInspectable public var badgeColor = Colors.red {
didSet {
setNeedsDisplay()
}
}
/// Width of the badge border
#IBInspectable public var borderWidth: CGFloat = 0 {
didSet {
invalidateIntrinsicContentSize()
}
}
/// Color of the bardge border
#IBInspectable public var borderColor = Colors.white {
didSet {
invalidateIntrinsicContentSize()
}
}
/// Badge insets that describe the margin between text and the edge of the badge.
#IBInspectable public var insets: CGSize = CGSize(width: 5, height: 2) {
didSet {
invalidateIntrinsicContentSize()
}
}
// MARK: Badge shadow
// --------------------------
/// Opacity of the badge shadow
#IBInspectable public var shadowOpacityBadge: CGFloat = 0.5 {
didSet {
layer.shadowOpacity = Float(shadowOpacityBadge)
setNeedsDisplay()
}
}
/// Size of the badge shadow
#IBInspectable public var shadowRadiusBadge: CGFloat = 0.5 {
didSet {
layer.shadowRadius = shadowRadiusBadge
setNeedsDisplay()
}
}
/// Color of the badge shadow
#IBInspectable public var shadowColorBadge = Colors.black {
didSet {
layer.shadowColor = shadowColorBadge.cgColor
setNeedsDisplay()
}
}
/// Offset of the badge shadow
#IBInspectable public var shadowOffsetBadge: CGSize = CGSize(width: 0, height: 0) {
didSet {
layer.shadowOffset = shadowOffsetBadge
setNeedsDisplay()
}
}
/// Initialize the badge view
convenience public init() {
self.init(frame: CGRect())
}
/// Initialize the badge view
override public init(frame: CGRect) {
super.init(frame: frame)
setup()
}
/// Initialize the badge view
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
/// Add custom insets around the text
override public func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
let rect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
var insetsWithBorder = actualInsetsWithBorder()
let rectWithDefaultInsets = rect.insetBy(dx: -insetsWithBorder.width, dy: -insetsWithBorder.height)
// If width is less than height
// Adjust the width insets to make it look round
if rectWithDefaultInsets.width < rectWithDefaultInsets.height {
insetsWithBorder.width = (rectWithDefaultInsets.height - rect.width) / 2
}
let result = rect.insetBy(dx: -insetsWithBorder.width, dy: -insetsWithBorder.height)
return result
}
/// Draws the label with insets
override public func drawText(in rect: CGRect) {
layer.cornerRadius = rect.height / 2
let insetsWithBorder = actualInsetsWithBorder()
let insets = UIEdgeInsets(
top: insetsWithBorder.height,
left: insetsWithBorder.width,
bottom: insetsWithBorder.height,
right: insetsWithBorder.width)
let rectWithoutInsets = UIEdgeInsetsInsetRect(rect, insets)
super.drawText(in: rectWithoutInsets)
}
/// Draw the background of the badge
override public func draw(_ rect: CGRect) {
let rectInset = rect.insetBy(dx: borderWidth/2, dy: borderWidth/2)
let path = UIBezierPath(roundedRect: rectInset, cornerRadius: rect.height/2)
badgeColor.setFill()
path.fill()
if borderWidth > 0 {
borderColor.setStroke()
path.lineWidth = borderWidth
path.stroke()
}
super.draw(rect)
}
private func setup() {
textAlignment = NSTextAlignment.center
clipsToBounds = false // Allows shadow to spread beyond the bounds of the badge
}
/// Size of the insets plus the border
private func actualInsetsWithBorder() -> CGSize {
return CGSize(
width: insets.width + borderWidth,
height: insets.height + borderWidth
)
}
/// Draw the stars in interface builder
#available(iOS 8.0, *)
override public func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setup()
setNeedsDisplay()
}
}
func createBadge(_ who: BadgeSwift, _ view: UIView, _ value: String) {
if !view.subviews.contains(who) {
view.addSubview(who)
}
configureBadge(who, view, value)
positionBadge(who, view)
}
func removeBadge(_ who: BadgeSwift, _ view: UIView) {
if view.subviews.contains(who) {
who.removeFromSuperview()
}
}
func configureBadge(_ badge: BadgeSwift, _ view: UIView, _ value: String) {
badge.text = value
badge.insets = CGSize(width: 2, height: 2)
badge.font = UIFont.boldSystemFont(ofSize: 12)
badge.textColor = Colors.white
badge.badgeColor = Colors.badgeRed
}
func positionBadge(_ badge: UIView, _ view: UIView) {
badge.translatesAutoresizingMaskIntoConstraints = false
var constraints = [NSLayoutConstraint]()
constraints.append(NSLayoutConstraint(
item: badge,
attribute: NSLayoutAttribute.centerY,
relatedBy: NSLayoutRelation.equal,
toItem: view,
attribute: NSLayoutAttribute.top,
multiplier: 1, constant: 5)
)
constraints.append(NSLayoutConstraint(
item: badge,
attribute: NSLayoutAttribute.centerX,
relatedBy: NSLayoutRelation.equal,
toItem: view,
attribute: NSLayoutAttribute.right,
multiplier: 1, constant: -5)
)
view.addConstraints(constraints)
}
func calculateCount(_ items: [UITabBarItem]) -> String {
var countInt = 0
for i in items {
if let countString = i.badgeValue {
countInt = countInt + Int(countString)!
}
}
return String(countInt)
}
For your purpose to create a Badge inside a UITableViewCell, you could use:
let badge = BadgeSwift()
createBadge(badge, MyCell, "10")
That would give you a Badge of 10.