Created
June 23, 2020 11:50
-
-
Save agelessman/ed514f2d6dc3378375faf0e64006048e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// SafelyUpdatingViewStateDemo | |
// | |
// Created by 马超 on 2020/6/22. | |
// Copyright © 2020 马超. All rights reserved. | |
// | |
import SwiftUI | |
struct ContentView: View { | |
var body: some View { | |
Example1() | |
} | |
} | |
class MyObservable: ObservableObject { | |
@Published var name = "你是章三" | |
} | |
struct Example4: View { | |
@EnvironmentObject var obj: MyObservable | |
var body: some View { | |
Text("\(obj.name)") | |
} | |
} | |
struct Example3: View { | |
@State private var width: CGFloat = 0.0 | |
var body: some View { | |
Text("Width = \(self.width)") | |
.font(.custom("Cochin", size: 30)) | |
.background(WidthGetter(width: self.$width)) | |
} | |
struct WidthGetter: View { | |
@Binding var width: CGFloat | |
var body: some View { | |
GeometryReader { proxy -> AnyView in | |
DispatchQueue.main.async { | |
self.width = proxy.frame(in: .local).width | |
print(self.width) | |
} | |
return AnyView(Color.clear) | |
} | |
} | |
} | |
} | |
struct Example2: View { | |
@State private var show = false | |
@State private var direction = "" | |
var body: some View { | |
print("更新body direction = \(self.direction) ") | |
return VStack { | |
CPUWheel() | |
.frame(height: 150) | |
Text("\(self.direction)") | |
.font(.largeTitle) | |
Image(systemName: "location.north.fill") | |
.resizable() | |
.frame(width: 100, height: 100) | |
.foregroundColor(.green) | |
.modifier(RotateEffect(direction: self.$direction, angle: self.show ? 360 : 0)) | |
Button("开始") { | |
withAnimation(.easeInOut(duration: 3.0)) { | |
self.show.toggle() | |
} | |
} | |
.padding(.top, 50) | |
} | |
} | |
} | |
struct RotateEffect: GeometryEffect { | |
@Binding var direction: String | |
var angle: Double | |
var animatableData: Double { | |
get { | |
angle | |
} | |
set { | |
angle = newValue | |
} | |
} | |
func effectValue(size: CGSize) -> ProjectionTransform { | |
DispatchQueue.main.async { | |
self.direction = self.getDirection(self.angle) | |
print("更新effectValue direction = \(self.direction) ") | |
} | |
let rotation = CGAffineTransform(rotationAngle: CGFloat(angle * (Double.pi / 180.0))) | |
let offset1 = CGAffineTransform(translationX: size.width / 2.0, y: size.height / 2.0) | |
let offset2 = CGAffineTransform(translationX: -size.width / 2.0, y: -size.height / 2.0) | |
return ProjectionTransform(offset2.concatenating(rotation).concatenating(offset1)) | |
} | |
func getDirection(_ angle: Double) -> String { | |
switch angle { | |
case 0..<45: | |
return "北" | |
case 45..<135: | |
return "东" | |
case 135..<225: | |
return "南" | |
case 225..<315: | |
return "西" | |
default: | |
return "北" | |
} | |
} | |
} | |
struct Example1: View { | |
@State private var show = false | |
var body: some View { | |
VStack { | |
CPUWheel() | |
.frame(height: 150) | |
if show { | |
OutOfControlView() | |
} | |
Button(self.show ? "隐藏" : "显示") { | |
self.show.toggle() | |
} | |
} | |
} | |
} | |
struct OutOfControlView: View { | |
@State private var count: Int = 0 | |
var body: some View { | |
DispatchQueue.main.async { | |
self.count += 1 | |
} | |
return Text("计算次数:\(self.count)") | |
.multilineTextAlignment(.center) | |
} | |
} | |
struct CPUWheel: View { | |
@State private var cpu: Int = 0 | |
var timer = Timer.publish(every: 0.1, on: .current, in: .common).autoconnect() | |
var body: some View { | |
let gradient = AngularGradient(gradient: Gradient(colors: [.orange, .green, .blue]), center: .center, angle: .degrees(0)) | |
return Circle() | |
.stroke(lineWidth: 3) | |
.foregroundColor(.primary) | |
.background(Circle().fill(gradient).clipShape(CPUClip(pct: Double(self.cpu)))) | |
.shadow(radius: 4) | |
.overlay(CPULabel(pct: self.cpu)) | |
.onReceive(timer) { _ in | |
withAnimation { | |
self.cpu = Int(Self.cpuUsage()) | |
} | |
} | |
} | |
struct CPUClip: Shape { | |
let pct: Double | |
func path(in rect: CGRect) -> Path { | |
var path = Path() | |
let cp = CGPoint(x: rect.midX, y: rect.midY) | |
path.move(to: cp) | |
path.addArc(center: cp, radius: rect.height / 2, startAngle: .degrees(0), endAngle: .degrees(pct / 100.0 * 360.0), clockwise: false) | |
path.closeSubpath() | |
return path | |
} | |
} | |
struct CPULabel: View { | |
let pct: Int | |
var body: some View { | |
VStack { | |
Text("\(self.pct) %") | |
.font(.largeTitle) | |
Text("CPU") | |
.font(.body) | |
} | |
.transaction { $0.animation = nil } | |
} | |
} | |
// Source for cpuUsage(): based on https://stackoverflow.com/a/44134397/7786555 | |
static func cpuUsage() -> Double { | |
var kr: kern_return_t | |
var task_info_count: mach_msg_type_number_t | |
task_info_count = mach_msg_type_number_t(TASK_INFO_MAX) | |
var tinfo = [integer_t](repeating: 0, count: Int(task_info_count)) | |
kr = task_info(mach_task_self_, task_flavor_t(TASK_BASIC_INFO), &tinfo, &task_info_count) | |
if kr != KERN_SUCCESS { | |
return -1 | |
} | |
return [thread_act_t]().withUnsafeBufferPointer { bufferPointer in | |
var thread_list: thread_act_array_t? = UnsafeMutablePointer(mutating: bufferPointer.baseAddress) | |
var thread_count: mach_msg_type_number_t = 0 | |
defer { | |
if let thread_list = thread_list { | |
vm_deallocate(mach_task_self_, vm_address_t(bitPattern: thread_list), vm_size_t(Int(thread_count) * MemoryLayout<thread_t>.stride)) | |
} | |
} | |
kr = task_threads(mach_task_self_, &thread_list, &thread_count) | |
if kr != KERN_SUCCESS { | |
return -1 | |
} | |
var tot_cpu: Double = 0 | |
if let thread_list = thread_list { | |
for j in 0..<Int(thread_count) { | |
var thread_info_count = mach_msg_type_number_t(THREAD_INFO_MAX) | |
var thinfo = [integer_t](repeating: 0, count: Int(thread_info_count)) | |
kr = thread_info(thread_list[j], thread_flavor_t(THREAD_BASIC_INFO), | |
&thinfo, &thread_info_count) | |
if kr != KERN_SUCCESS { | |
return -1 | |
} | |
let threadBasicInfo = convertThreadInfoToThreadBasicInfo(thinfo) | |
if threadBasicInfo.flags != TH_FLAGS_IDLE { | |
tot_cpu += (Double(threadBasicInfo.cpu_usage) / Double(TH_USAGE_SCALE)) * 100.0 | |
} | |
} // for each thread | |
} | |
return tot_cpu | |
} | |
} | |
static func convertThreadInfoToThreadBasicInfo(_ threadInfo: [integer_t]) -> thread_basic_info { | |
var result = thread_basic_info() | |
result.user_time = time_value_t(seconds: threadInfo[0], microseconds: threadInfo[1]) | |
result.system_time = time_value_t(seconds: threadInfo[2], microseconds: threadInfo[3]) | |
result.cpu_usage = threadInfo[4] | |
result.policy = threadInfo[5] | |
result.run_state = threadInfo[6] | |
result.flags = threadInfo[7] | |
result.suspend_count = threadInfo[8] | |
result.sleep_time = threadInfo[9] | |
return result | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment