I have a custom class named CodeReadModel, which contains another custom class named CodeDataModel. The former contains the latter as an array like the following.
class CodeReadModel: NSObject, NSSecureCoding {
class var supportsSecureCoding: Bool { true }
let identifier: String
let codeDataModels: [CodeDataModel]
init(identifier: String, codeDataModels: [CodeDataModel]) {
self.identifier = identifier
self.codeDataModels = codeDataModels
}
required init(coder decoder: NSCoder) {
self.identifier = decoder.decodeObject(forKey: "identifier") as! String
self.codeDataModels = decoder.decodeObject(forKey: "codeDataModels") as! [CodeDataModel]
}
func encode(with coder: NSCoder) {
coder.encode(identifier, forKey: "identifier")
coder.encode(codeDataModels, forKey: "codeDataModels")
}
}
And I want to unarchive an object with the following.
func importCodeReaderSnippetNext(fileURL: URL) {
do {
NSKeyedUnarchiver.setClass(CodeReadModel.self, forClassName: "CodeReadModel")
NSKeyedUnarchiver.setClass(CodeDataModel.self, forClassName: "CodeDataModel")
let data = try! Data(contentsOf: fileURL)
if let codeReadModel = try NSKeyedUnarchiver.unarchivedObject(ofClass: CodeReadModel.self, from: data) {
}
} catch {
print("Error: \(error.localizedDescription)")
}
}
And I will get an error because codeReadModel contains another custom class, which cannot be decoded. How can I resolve this problem? Muchas thankos.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I have a SwiftUI app. It fetches records through CoreData. And I want to show some records on a widget. I understand that I need to use AppGroup to share data between an app and its associated widget.
import Foundation
import CoreData
import CloudKit
class DataManager {
static let instance = DataManager()
let container: NSPersistentContainer
let context: NSManagedObjectContext
init() {
container = NSPersistentCloudKitContainer(name: "DataMama")
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: group identifier)!.appendingPathComponent("Trash.sqlite"))]
container.loadPersistentStores(completionHandler: { (description, error) in
if let error = error as NSError? {
print("Unresolved error \(error), \(error.userInfo)")
}
})
context = container.viewContext
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType)
}
func save() {
do {
try container.viewContext.save()
print("Saved successfully")
} catch {
print("Error in saving data: \(error.localizedDescription)")
}
}
}
// ViewModel //
import Foundation
import CoreData
import WidgetKit
class ViewModel: ObservableObject {
let manager = DataManager()
@Published var records: [Little] = []
init() {
fetchRecords()
}
func fetchRecords() {
let request = NSFetchRequest<Little>(entityName: "Little")
do {
records = try manager.context.fetch(request)
records.sort { lhs, rhs in
lhs.trashDate! < rhs.trashDate!
}
} catch {
print("Fetch error for DataManager: \(error.localizedDescription)")
}
WidgetCenter.shared.reloadAllTimelines()
}
}
So I have a view model that fetches data for the app as shown above.
Now, my question is how should my widget get data from CoreData? Should the widget get data from CoreData through DataManager? I have read some questions here and also read some articles around the world. This article ( https://dev.classmethod.jp/articles/widget-coredate-introduction/ ) suggests that you let the Widget struct access CoreData through DataManager. If that's a correct fashion, how should the getTimeline function in the TimelineProvider struct get data? This question also suggests the same. Thank you for your reading my question.
I need to check the network connection with NWPathMonitor.
import Foundation
import Network
class NetworkViewModel: ObservableObject {
let monitor = NWPathMonitor()
let queue = DispatchQueue(label: "NetworkViewModel")
@Published var isConnected = false
var connectionDescription: String {
if isConnected {
return "You are connected."
} else {
return "You are NOT connected."
}
}
init() {
monitor.pathUpdateHandler = { path in
DispatchQueue.main.async {
self.isConnected = path.status == .satisfied
}
}
monitor.start(queue: queue)
}
}
import SwiftUI
struct ContentView: View {
@StateObject private var networkViewModel = NetworkViewModel()
var body: some View {
VStack {
}
.onAppear {
if networkViewModel.isConnected {
print("You are connected.")
}
else {
print("You are NOT connected.")
}
}
}
}
So there is nothing special, not at all. Yet, if I test it with a totally new Xcode project for iOS, it fails and return !isConnected. I've tested it with a macOS application. And it fails. I've tested it with an actual device. It fails. I've tested it with an old project. It still does work. I have no mere idea why new Xcode projects all fail to detect the WiFi connection. This is a total nightmare. Does anybody have a clue? thanks.
Again, I have a vertical stack of horizontal stacks of buttons as follows.
import SwiftUI
struct ContentView: View {
		@State private var eventPresented = Bool()
		@State private var selectedEventIndex = Int()
		@State private var monthSelection = Int()
		
		var body: some View {
				VStack {
						VStack(spacing: 0.0) {
								HStack(spacing: 0.0) {
										ForEach((0...6), id: \.self) {
												index in
												Button(buttonTitles[index] ?? "") {
														eventPresented = true
														selectedEventIndex = index + 1 - self.weekIndex
												}
												.foregroundColor(titleColors[index])
												.overlay(Text(eventNumbers[index] ?? "").font(.footnote).foregroundColor(.blue).offset(x: -16, y: -16))
												.buttonStyle(BorderlessButtonStyle())
												.frame(width: 48, height: 48, alignment: .center)
												.background(RoundedRectangle(cornerRadius: 2)
																				.fill(fillColors[index])
																				.shadow(color: shadowColors[index], radius: 2, x: 0, y: 0)
												)
												.sheet(isPresented: $eventPresented) {
														EventView(eventVisible: self.$eventPresented, dayFromParent: self.$selectedEventIndex, monthFromParent: self.$monthSelection)
												}
										}
								}
								...
								...
								HStack(alignment: .top, spacing: 0.0) {
										ForEach((35...36), id: \.self) {
												index in
												Button(buttonTitles[index] ?? "") {
														eventPresented = true
														selectedEventIndex = index + 1 - self.weekIndex
												}
												.foregroundColor(titleColors[index])
												.overlay(Text(eventNumbers[index] ?? "").font(.footnote).foregroundColor(.blue).offset(x: -16, y: -16))
												.buttonStyle(BorderlessButtonStyle())
												.frame(width: 48, height: 48, alignment: .center)
												.background(RoundedRectangle(cornerRadius: 2)
																				.fill(fillColors[index])
																				.shadow(color: shadowColors[index], radius: 2, x: 0, y: 0)
												)
												.sheet(isPresented: $eventPresented) {
														EventView(eventVisible: self.$eventPresented, dayFromParent: self.$selectedEventIndex, monthFromParent: self.$monthSelection)
												}
										}
								}
								.frame(width: 336.0, height: 48.0, alignment: .leading)
						}
				}
		}
}
And I want to send a few variables to EventView when the user clicks on a button.
struct EventView: View {
		@Binding var eventVisible: Bool
		@Binding var dayFromParent: Int
		@Binding var monthFromParent: Int
		var body: some View {
				VStack {
						Text("Window sheet.")
						Button("OK") {
								self.eventVisible = false
								print("month from parent: \(monthFromParent)")
								print("day from parent: \(dayFromParent)")
						}
				}
				.frame(width: 240, height: 180)
		}
}
If I just want to send two variables to it,
EventView(eventVisible: self.$eventPresented, dayFromParent: self.$selectedEventIndex)
the compiler didn't complain. For the third variable, it says
SwiftUI the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions I know what it means. Some say it could resolve the issue by making the data types of variables clearer. Others say you could use a function to return a variable for a somewhat complex algebra equation. But what can I do in my case? Does anybody have any suggestions?
Thank you
I could do it with completionHandler, but I'm trying to get server data with Combine. The following is what I have.
// UIViewController //
import UIKit
import Combine
class ViewController: UIViewController {
// MARK: - Variables
private var cancellableSet: Set<AnyCancellable> = []
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
let urlStr = "https://api.github.com/repos/ReactiveX/RxSwift/events"
let viewModel = ViewModel(urlStr: urlStr, waitTime: 2.0)
viewModel.fetchData(urlText: viewModel.urlStr, timeInterval: viewModel.waitTime)
.sink { completion in
print("complete")
} receiveValue: { dataSet in
print("count: \(dataSet)")
}
.store(in: &cancellableSet)
print("Yeah...")
}
}
struct DataModel: Hashable, Decodable {
let id: String
let type: String
}
// ViewModel //
import UIKit
import Combine
class ViewModel: NSObject {
var cancellables = [AnyCancellable]()
var urlStr: String
var waitTime: Double
init(urlStr: String, waitTime: Double) {
self.urlStr = urlStr
self.waitTime = waitTime
}
func fetchData(urlText: String, timeInterval: Double) -> Future<[DataModel], Error> {
return Future<[DataModel], Error> { [weak self] promise in
guard let strongSelf = self else { return }
if let url = URL(string: urlText) {
var request = URLRequest(url: url)
request.timeoutInterval = timeInterval
let sessionConfiguration = URLSessionConfiguration.default
let publisher = URLSession(configuration: sessionConfiguration).dataTaskPublisher(for: request)
publisher.sink { completion in
print("complete")
} receiveValue: { (data: Data, response: URLResponse) in
do {
let dataModels = try JSONDecoder().decode([DataModel].self, from: data)
promise(.success(dataModels))
} catch {
print("Error while parsing: \(error)")
promise(.failure("Failure" as! Error))
}
}
.store(in: &strongSelf.cancellables)
} else {
promise(.failure("Failure" as! Error))
}
}
}
}
If I run it, I don't get an error. The app doesn't crash, either. The view controller doesn't deliver anything. What am I doing wrong? Muchos thankos.
I have three sets of Text and TextField. And I need to filter each TextField entry. I have gotten a function to filter the TextField entry from this website (https://zenn.dev/yorifuji/articles/swiftui-textfield-filter). Finally, I have the following lines of code.
import SwiftUI
struct ContentView: View {
@State var username = ""
@State var password = ""
@State var tenantID = ""
var body: some View {
VStack {
makeForm(label: "Username: ", placeHolder: "123456", text: $username)
makeForm(label: "Password: ", placeHolder: "abcdefg", text: $password)
makeForm(label: "Shop ID: ", placeHolder: "123456", text: $tenantID)
}.padding(.horizontal, 40.0)
}
@ViewBuilder
private func makeForm(label: String, placeHolder: String, text: Binding<String>) -> some View {
HStack {
let newText = Binding<String>(
get: { text.wrappedValue },
set: { filter(value: $0) }
)
Text(label)
TextField(placeHolder, text: newText)
}
}
func filter(value: String) -> String {
let validCodes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
let sets = CharacterSet(charactersIn: validCodes)
return String(value.unicodeScalars.filter(sets.contains).map(Character.init))
}
}
Well, I don't know how to use the Binding with get and set, which I believe is what I need. Yet, I get a warning at the following line.
set: { filter(value: $0) }
What I need to do is set the filtered value to each TextField. What am I doing wrong? Thanks.
If I want to subscribe to four @Published variables at the same time, I can do something like the following.
Publishers.CombineLatest4($variable0, $variable1, $variable2, $variable3)
I wonder if there is any solution to subscribing to more than four variables at the same time?
Muchos thankos
I have the following lines of code to show multiple instances of View (MyTextView)
import SwiftUI
struct ContentView: View {
@State var myTextViews = [MyTextView]()
var body: some View {
VStack {
Button {
} label: {
Text("Show me your current text strings")
}.padding(.vertical, 10.0)
VStack {
ForEach(0 ..< myTextViews.count, id: \.self) { _ in
MyTextView()
}
}
Button {
myTextViews.append(MyTextView())
} label: {
Text("Add me!")
}.padding(.vertical, 10.0)
}
}
}
struct MyTextView: View {
@State var text = ""
var body: some View {
ZStack {
TextField("Enter some text", text: $text)
}.padding(.horizontal, 50.0)
}
}
According to the screenshot, I have three instances, each of which contains TextField. After I tap the top button (Show me your current...), I want to show the result from each TextField. How can I do that? Thanks.
I have a sample SwiftUI iOS app. As shown in the screenshot below, my project has three configurations: Debug, MyDebug, Release.
If I select the Debug or MyDebug scheme, I get a preview. But if I select the Release scheme, I get an error that says the following.
”***.app” needs -Onone Swift optimization level to use previews (current setting is -O)
, where *** is the app name.
It probably has nothing to do with the Preview error, but the Info.plist has a dictionary such that the key name is devMode, and the value is $(DEVMODE). And I have a user-defined setting as shown below.
My ContentView has the following.
import SwiftUI
struct ContentView: View {
@State private var state: String = ""
var body: some View {
VStack {
Text("Hello, world!: \(state)")
}
.onAppear {
if let devMode = Bundle.main.object(forInfoDictionaryKey: "devMode") as? String {
print("Development mode: \(devMode)")
state = devMode
}
if let path = Bundle.main.path(forResource: "Info", ofType: "plist") {
if let dict = NSDictionary(contentsOfFile: path) {
print("**** \(dict)")
}
}
#if DEBUG
print("Debug")
#elseif MYDEBUG
print("MyDebug")
#else
print("Que?")
#endif
}
}
}
#Preview {
ContentView()
}
So my question is how I get the preview for all three build schemes? Muchos thankos.
I have a SwiftUI desktop application. And I need to open a window sheet from a storyboard with a click of a button, which works. But I have a problem.
The opening window sheet is very big. Its size is 1,400 x 300 pixels. (I don't know the exact height.) I don't know where this size comes from. But I need to make it smaller. If I try to do it with the view controller, it doesn't work. How can I control the opening window sheet size?
// SwiftUI View //
import SwiftUI
struct ContentView: View {
		@State private var sheetPresented = false
		@State private var selectionIndex = 3
		
		var body: some View {
				ZStack {
						VStack {
								Button(action: {
										sheetPresented = true
								}) {
										Text("Show me a sheet")
								}
								.sheet(isPresented: $sheetPresented) {
										SheetViewControllerRepresentation(message: String(selectionIndex))
								}
						}
				}.frame(minWidth: 360, idealWidth: 360, maxWidth: 360, minHeight: 240, idealHeight: 240, maxHeight: 240, alignment: .center)
		}
}
// View controller //
import Cocoa
import SwiftUI
class SheetViewController: NSViewController {
		// MARK: -
		var message = String()
				
		// MARK: - IBOutlet
		@IBOutlet weak var messageLabel: NSTextField!
		// MARK: - IBAction		
		@IBAction func closeClicked(_ sender: NSButton) {
				/* closing window */
				self.view.window?.setIsVisible(false)
				self.view.window?.close()
		}
		// MARK: - Life cycle
		override func viewDidLoad() {
				super.viewDidLoad()
				// Do view setup here.
		}
		
		override func viewWillAppear() {
				super.viewWillAppear()
				
				messageLabel.stringValue = message
		}
		
		override func viewDidAppear() {
				super.viewDidAppear()
				
				view.setFrameSize(CGSize(width: 320, height: 220))
		}
}
struct SheetViewControllerRepresentation: NSViewControllerRepresentable {
		var message = String()
		
		func makeNSViewController(context: NSViewControllerRepresentableContext<SheetViewControllerRepresentation>) -> SheetViewController {
				let mainStoryboard = NSStoryboard(name: "Main", bundle: nil)
				let sheetViewController = mainStoryboard.instantiateController(withIdentifier: "SheetView") as! SheetViewController
				sheetViewController.message = self.message
				return sheetViewController
		}
		
		func updateNSViewController(_ nsViewController: SheetViewController, context: NSViewControllerRepresentableContext<SheetViewControllerRepresentation>) {
		}
}
Thank you.
I have some lines of code below where I can make multiple selections.
import SwiftUI
struct ContentView: View {
@State private var selectedUsers: Set<String> = []
@State var users = ["Susan", "Kate", "Natalie", "Kimberly", "Taylor", "Sarah", "Nancy", "Katherine", "Nicole", "Linda", "Jane", "Mary", "Olivia", "Barbara"]
var body: some View {
VStack {
List(selection: $selectedUsers) {
ForEach(users, id: \.self) { user in
Text(user)
}
}
.environment(\.editMode, .constant(.active))
}
}
}
So the blue selection symbol appears as shown in the screenshot above. That's good. But that's not what I'm after. I just want to select one row at a time.
import SwiftUI
struct ContentView: View {
@State var selectedUser: String?
@State var users = ["Susan", "Kate", "Natalie", "Kimberly", "Taylor", "Sarah", "Nancy", "Katherine", "Nicole", "Linda", "Jane", "Mary", "Olivia", "Barbara"]
var body: some View {
VStack {
List(selection: $selectedUser) {
ForEach(users, id: \.self) { user in
Text(user)
}
}
.environment(\.editMode, .constant(.active))
}
}
}
In the lines of code above, I only let myself select one row at a time. And I don't get the blue selection symbol. I wonder why? I find two or three websites where they have similar lines of code and where they select one row at a time. And they have the blue selection symbol. Why don't I get it?
Mucho thankos for reading.
I am perplexed as to how to use async await. In the following example, I don't use GCD or performSelector(inBackground:with:). The view controller is NSViewController, but it doesn't make any difference if it's NSViewController or UIViewController.
import Cocoa
class ViewController: NSViewController {
func startWriteImages() {
Task{
let bool = await startWriteImagesNext()
if bool {
print("I'm done!")
}
}
}
func startWriteImagesNext() async -> Bool {
// pictures is a path to a folder in the sandbox folder
// appDelegate.defaultFileManager is a variable pointing to FileManager.default in AppDelegate
let pictURL = URL(fileURLWithPath: pictures)
if let filePaths = try? self.appDelegate.defaultFileManager.contentsOfDirectory(atPath: pictURL.path) {
for file in filePaths {
let fileURL = pictURL.appending(component: file)
if self.appDelegate.defaultFileManager.fileExists(atPath: fileURL.path) {
let newURL = self.folderURL.appending(component: file)
do {
try self.appDelegate.defaultFileManager.copyItem(at: fileURL, to: newURL)
} catch {
print("Ugghhh...")
}
}
}
return true
}
return false
}
func startWriteImagesNext2() async -> Bool {
let pictURL = URL(fileURLWithPath: pictures)
if let filePaths = try? self.appDelegate.defaultFileManager.contentsOfDirectory(atPath: pictURL.path) {
DispatchQueue.global().async() {
for file in filePaths {
let fileURL = pictURL.appending(component: file)
if self.appDelegate.defaultFileManager.fileExists(atPath: fileURL.path) {
let newURL = self.folderURL.appending(component: file)
do {
try self.appDelegate.defaultFileManager.copyItem(at: fileURL, to: newURL)
} catch {
print("Ugghhh...")
}
}
}
}
return true
}
return false
}
}
In the code above, I'm saving each file in the folder to user-selected folder (self.folderURL). And the application will execute the print guy only when work is done. Since it's heavy-duty work, I want to use CCD or performSelector(inBackground:with:). If I use the former (startWriteImagesNext2), the application will execute the print guy right at the beginning. I suppose I cannot use GCD with async. So how can I perform heavy-duty work? Muchos thankos.
I have a very simple set of lines of code. It doesn't matter whether you run it under UIKit or SwiftUI. In SwiftUI, I have the following.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Button("Click on me") {
let tabLine = "1\tAnthony James\t139.9"
var item = ""
let tabs = tabLine.components(separatedBy: "\t")
for tab in tabs {
item += "'\(tab)'"
}
print("\(item)")
}
}
}
}
So I have tab-separated values. And I want to separate them and quote each value either with an apostrophe or a double quotation mark.
In the case above, I get the following print.
'1''Anthony James''139.9'
That's exactly what I want. Now, I have an array of three of those guys like the following.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Button("Click on me") {
let tabLine0 = "1\tAnthony James\t139.9"
let tabLine1 = "2\tKim Harbaugh\t181.4"
let tabLine2 = "3\tAnthony James\t212.4"
let tabTextLines = [tabLine0, tabLine1, tabLine2]
var strings = [String]()
for tabLine in tabTextLines {
var item = ""
let tabs = tabLine.components(separatedBy: "\t")
for tab in tabs {
item += "'\(tab)'"
}
strings.append(item)
}
print("\(strings)")
}
}
.frame(width: 360, height: 240)
}
}
And I get the following print.
This is a nightmare situation. Each value is quoted with an escaped apostrophe. I can't even remove the escapees with replacingOccurrences(of:with:). How does that happen when you have an array of strings? If I try quoting the values with a unicode character, things are the same. Is there a workaround? Muchos thankos.
Topic:
Programming Languages
SubTopic:
Swift
Its document says openDocument can open a document at a specific URL. So I've saved a model as a JSON object with its URL and a bookmark as Data. With its security-scoped bookmark data resolved, I am able to open a document except that the app will crash right after opening a document. Console says
should only be called in the main thread
struct ContentView: View {
@EnvironmentObject var bookmarkViewModel: BookmarkViewModel
var body: some View {
VStack {
}
.onAppear {
loadBookmarks()
}
}
extension ContentView {
func loadBookmarks() {
print("1 \(Thread.current)") // NSMainThread
Task {
for bookmarkItem in bookmarkViewModel.bookmarkItems { // resolving a security-scoped bookmark
print("2 \(Thread.current)") // NSMainThread
if let _ = resolveBookmark(bookmarkData: bookmarkItem.bookmarkData) {
print("3 \(Thread.current)") // NSMainThread
do {
print("4 \(Thread.current)") // NSMainThread
try await openDocument(at: bookmarkItem.bookmarkURL)
print("5 \(Thread.current)") // NSMainThread
} catch {
print("\(error.localizedDescription)")
}
}
}
}
}
}
Well, the application is on the main thread. I've checked every line before and after opening a document with its URL. Call what on the main thread? This is confusing. Thanks.
class BookmarkViewModel: ObservableObject {
@Published var bookmarkItems: [BookmarkItem] = []
var defaultFileManager: FileManager {
return FileManager.default
}
var documentURL: URL? {
...
}
init() {
fetchBookmarkItems()
}
func fetchBookmarkItems() {
bookmarkItems.removeAll()
if let documentURL {
let bookmarkFolderURL = documentURL.appending(path: "MyApp").appending(path: "Bookmarks")
do {
let contents = try defaultFileManager.contentsOfDirectory(atPath: bookmarkFolderURL.path)
for content in contents {
...
let fileURL = bookmarkFolderURL.appending(path: content)
let data = try Data(contentsOf: fileURL)
let bookmarkItem = try JSONDecoder().decode(BookmarkItem.self, from: data)
bookmarkItems.append(bookmarkItem)
}
} catch {
print("Error fetching folder content: \(error.localizedDescription)")
}
}
}
}
struct BookmarkItem: Codable, Hashable {
let bookmarkURL: URL
let date: Date
let bookmarkData: Data
let open: Bool
}
I'm a novice in RealityKit and ARKit. I'm using ARKit in SwiftUI to show a cube with a number as shown below.
import SwiftUI
import RealityKit
import ARKit
struct ContentView : View {
var body: some View {
return ARViewContainer()
}
}
#Preview {
ContentView()
}
struct ARViewContainer: UIViewRepresentable {
typealias UIViewType = ARView
func makeUIView(context: UIViewRepresentableContext<ARViewContainer>) -> ARView {
let arView = ARView(frame: .zero, cameraMode: .ar, automaticallyConfigureSession: true)
arView.enableTapGesture()
return arView
}
func updateUIView(_ uiView: ARView, context: UIViewRepresentableContext<ARViewContainer>) {
}
}
extension ARView {
func enableTapGesture() {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(recognizer:)))
self.addGestureRecognizer(tapGestureRecognizer)
}
@objc func handleTap(recognizer: UITapGestureRecognizer) {
let tapLocation = recognizer.location(in: self) // print("Tap location: \(tapLocation)")
guard let rayResult = self.ray(through: tapLocation) else { return }
let results = self.raycast(from: tapLocation, allowing: .estimatedPlane, alignment: .any)
if let firstResult = results.first {
let position = simd_make_float3(firstResult.worldTransform.columns.3)
placeObject(at: position)
}
}
func placeObject(at position: SIMD3<Float>) {
let mesh = MeshResource.generateBox(size: 0.3)
let material = SimpleMaterial(color: UIColor.systemRed, roughness: 0.3, isMetallic: true)
let modelEntity = ModelEntity(mesh: mesh, materials: [material])
var unlitMaterial = UnlitMaterial()
if let textureResource = generateTextResource(text: "1", textColor: UIColor.white) {
unlitMaterial.color = .init(tint: .white, texture: .init(textureResource))
modelEntity.model?.materials = [unlitMaterial]
let id = UUID().uuidString
modelEntity.name = id
modelEntity.transform.scale = [0.3, 0.1, 0.3]
modelEntity.generateCollisionShapes(recursive: true)
let anchorEntity = AnchorEntity(world: position)
anchorEntity.addChild(modelEntity)
self.scene.addAnchor(anchorEntity)
}
}
func generateTextResource(text: String, textColor: UIColor) -> TextureResource? {
if let image = text.image(withAttributes: [NSAttributedString.Key.foregroundColor: textColor], size: CGSize(width: 18, height: 18)), let cgImage = image.cgImage {
let textureResource = try? TextureResource(image: cgImage, options: TextureResource.CreateOptions.init(semantic: nil))
return textureResource
}
return nil
}
}
I tap the floor and get a cube with '1' as shown below.
The background color of the cube is black, I guess. Where does this color come from and how can I change it into, say, red? Thanks.