swiftUI

Mutating Core Data object

Problem: How to mutate a fetched CD object in child views


import SwiftUI import CoreData struct ContentView: View { @Environment(\.managedObjectContext) private var viewContext @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)], animation: .default) var items: FetchedResults<Item> var body: some View { NavigationView { List { ForEach(items) { item in NavigationLink { ItemDetail(item: item) } label: { Text("itemview1 at \(item.timestamp!, formatter: itemFormatter) name: \((item.name ?? "").isEmpty ? "": item.name! )") } } .onDelete(perform: deleteItems) } .toolbar { #if os(iOS) ToolbarItem(placement: .navigationBarTrailing) { EditButton() } #endif ToolbarItem { Button(action: addItem) { Label("Add Item", systemImage: "plus") } } } Text("Select an item") } } private func addItem() { withAnimation { let newItem = Item(context: viewContext) newItem.timestamp = Date() do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } } } private func deleteItems(offsets: IndexSet) { withAnimation { offsets.map { items[$0] }.forEach(viewContext.delete) do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } } } } private let itemFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .medium return formatter }() struct ItemDetail: View { @ObservedObject var item: Item @State var name: String = "" var body: some View { Form { TextField("Enter name", text: $name ).onChange(of: name) { newValue in item.name = name } Button("Submit") { // nav back } Spacer() NavigationLink { ItemDetail(item: item) } label: { Text("itemview1 at \(item.timestamp!, formatter: itemFormatter) name: \((item.name ?? "").isEmpty ? "": item.name! )") } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) } }

Swift App from Terminal

ObjC article by chris eidhof

// Run any SwiftUI view as a Mac app.

import Cocoa
import SwiftUI

NSApplication.shared.run {
    VStack {
        Text("Hello, World")
            .padding()
            .background(Capsule().fill(Color.blue))
            .padding()
    }
    .frame(maxWidth: .infinity, maxHeight: .infinity)

}

extension NSApplication {
    public func run<V: View>(@ViewBuilder view: () -> V) {
        let appDelegate = AppDelegate(view())
        NSApp.setActivationPolicy(.regular)
        mainMenu = customMenu
        delegate = appDelegate
        run()
    }
}

// Inspired by https://www.cocoawithlove.com/2010/09/minimalist-cocoa-programming.html

extension NSApplication {
    var customMenu: NSMenu {
        let appMenu = NSMenuItem()
        appMenu.submenu = NSMenu()
        let appName = ProcessInfo.processInfo.processName
        appMenu.submenu?.addItem(NSMenuItem(title: "About \(appName)", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: ""))
        appMenu.submenu?.addItem(NSMenuItem.separator())
        let services = NSMenuItem(title: "Services", action: nil, keyEquivalent: "")
        self.servicesMenu = NSMenu()
        services.submenu = self.servicesMenu
        appMenu.submenu?.addItem(services)
        appMenu.submenu?.addItem(NSMenuItem.separator())
        appMenu.submenu?.addItem(NSMenuItem(title: "Hide \(appName)", action: #selector(NSApplication.hide(_:)), keyEquivalent: "h"))
        let hideOthers = NSMenuItem(title: "Hide Others", action: #selector(NSApplication.hideOtherApplications(_:)), keyEquivalent: "h")
        hideOthers.keyEquivalentModifierMask = [.command, .option]
        appMenu.submenu?.addItem(hideOthers)
        appMenu.submenu?.addItem(NSMenuItem(title: "Show All", action: #selector(NSApplication.unhideAllApplications(_:)), keyEquivalent: ""))
        appMenu.submenu?.addItem(NSMenuItem.separator())
        appMenu.submenu?.addItem(NSMenuItem(title: "Quit \(appName)", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))

        let windowMenu = NSMenuItem()
        windowMenu.submenu = NSMenu(title: "Window")
        windowMenu.submenu?.addItem(NSMenuItem(title: "Minmize", action: #selector(NSWindow.miniaturize(_:)), keyEquivalent: "m"))
        windowMenu.submenu?.addItem(NSMenuItem(title: "Zoom", action: #selector(NSWindow.performZoom(_:)), keyEquivalent: ""))
        windowMenu.submenu?.addItem(NSMenuItem.separator())
        windowMenu.submenu?.addItem(NSMenuItem(title: "Show All", action: #selector(NSApplication.arrangeInFront(_:)), keyEquivalent: "m"))

        let mainMenu = NSMenu(title: "Main Menu")
        mainMenu.addItem(appMenu)
        mainMenu.addItem(windowMenu)
        return mainMenu
    }
}

class AppDelegate<V: View>: NSObject, NSApplicationDelegate, NSWindowDelegate {
    init(_ contentView: V) {
        self.contentView = contentView

    }
    var window: NSWindow!
    var hostingView: NSView?
    var contentView: V

    func applicationDidFinishLaunching(_ notification: Notification) {
        window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered, defer: false)
        window.center()
        window.setFrameAutosaveName("Main Window")
        hostingView = NSHostingView(rootView: contentView)
        window.contentView = hostingView
        window.makeKeyAndOrderFront(nil)
        window.delegate = self
        NSApp.activate(ignoringOtherApps: true)
    }
}

Navigation link button makes master detail setup on iPad

.navigationViewStyle(StackNavigationViewStyle()) does the trick...

NavigationView {
        Text("Hello world!")
    }
    .navigationViewStyle(StackNavigationViewStyle())

SwiftUI small techniques

do after 1 seconds

@State var reloadCount = 0
...
body
---
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  self.reloadCount += 1
}

Bool

var isBoolean = false
...
isBoolean.toggle()

List example

From RW: Ray Wenderlich Video Tut

import PlaygroundSupport
import SwiftUI
struct Employee: Identifiable {
    var id = UUID()
    var name: String
    var title: String
}

let testData: [Employee] = [Employee(name: "Ray Wenderlich", title: "Owner"),
                            Employee(name: "Victoria Wenderlich", title: "Digital Artist"),
                            Employee(name: "Andrea Lepley", title: "Video Team Lead"),
                            Employee(name: "Sam Davies", title: "CTO"),
                            Employee(name: "Katie Collins", title: "Customer Support Lead"),
                            Employee(name: "Tiffani Randolph", title: "Marketing Associate")]

struct ContentView: View {
    var employees:[Employee] = []
   
    var body: some View {
       
            List(employees) { employee in
                NavigationButton(destination: Text(employee.name)) {
                    VStack(alignment: .leading) {
                        Text(employee.name)
                        Text(employee.title).font(.footnote)
                    }
                }
            }.navigationBarTitle(Text("Ray's Employees"))
       
    }
}
let vc = UIHostingController(rootView: NavigationView() { ContentView(employees: testData)})

PlaygroundPage.current.liveView = vc

Tags: 

SwiftUI Elements

Text

Text("Hello World!")

Text("Hello World")
    .font(.largeTitle)
    .foregroundColor(Color.green)
    .lineSpacing(50)
    .lineLimit(nil)
    .padding()

multiline

Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse est leo, vehicula eu eleifend non, auctor ut arcu")
      .lineLimit(nil).padding()
struct ContentView: View {
    static let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        return formatter
    }()

    var now = Date()
    var body: some View {
        Text("Now is the Date: \(now, formatter: Self.dateFormatter)")
    }
}

Text with background

Text("Hello World").color(.red).font(.largeTitle).background(Image(systemName: "photo").resizable().frame(width: 100, height: 100))

Images

sytem icon images

Image(systemName: "photo")
            .foregroundColor(.red)
            .font(.largeTitle)
Image(systemName: "star").resizable().aspectRatio(contentMode: .fill).padding(.bottom)

cast from UIImage to Image

if let image = UIImage(named: "test.png") {
  Image(uiImage: image)
}

Stacks

HStack, VStack, ZStack with styling:

VStack (alignment: .leading, spacing: 20){
    Text("Hello")
    Divider()
    Text("World")
}

on top of each other:

ZStack() {
    Image("hello_world")
    Text("Hello World")
        .font(.largeTitle)
        .background(Color.black)
        .foregroundColor(.white)
}

Input

Toggle

@State var isShowing = true //state

Toggle(isOn: $isShowing) {
    Text("Hello World")
}.padding()

Button

struct ContentView : View {
  var body: some View {

    Button(action: {
        self.buttonPress()
      }, label: {
          Text("TickTock").color(.white)
      }).frame(minWidth: 0, maxWidth: .infinity)
        .padding(20)
        .background(Color.blue)
  }

  private func buttonPress() {
    print("tick tock")
  }

}

(new!) to next view with presentation button

struct NextView: View {
  var body: some View {
    Text("NextView")
  }
}

struct ContentView : View {
  var body: some View {

    PresentationButton(Text("Navigate to another View"), destination: NextView())
  }
}
Subscribe to RSS - swiftUI