Browse Source

接入跟音功能

Steven 2 years ago
parent
commit
0c8f51556c
54 changed files with 6304 additions and 1 deletions
  1. 263 0
      KulexiuForTeacher/KulexiuForTeacher.xcodeproj/project.pbxproj
  2. 14 0
      KulexiuForTeacher/KulexiuForTeacher.xcworkspace/xcshareddata/swiftpm/Package.resolved
  3. 1 1
      KulexiuForTeacher/KulexiuForTeacher.xcworkspace/xcuserdata/wangzhi.xcuserdatad/WorkspaceSettings.xcsettings
  4. 55 0
      KulexiuForTeacher/KulexiuForTeacher/Common/Base/KSAccompanyWebViewController.m
  5. 16 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/SmallToolViewController.h
  6. 66 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/SmallToolViewController.m
  7. 16 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/ToneTuningViewController.h
  8. 265 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/ToneTuningViewController.m
  9. 16 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/WidgetViewController.h
  10. 353 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/WidgetViewController.m
  11. 35 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Model/KSMetronomePlayer.h
  12. 249 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Model/KSMetronomePlayer.m
  13. 4 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Model/TuningFunction/KulexiuForTeacher-Bridging-Header.h
  14. 213 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Model/TuningFunction/Tuner.swift
  15. 48 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Model/TuningFunction/TunerForkManager.swift
  16. 24 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetBottomButtonView.h
  17. 57 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetBottomButtonView.m
  18. 62 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetBottomButtonView.xib
  19. 44 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetDotView.h
  20. 383 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetDotView.m
  21. 22 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetDotView.xib
  22. 40 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetFunctionView.h
  23. 139 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetFunctionView.m
  24. 98 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetFunctionView.xib
  25. 22 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetNavView.h
  26. 43 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetNavView.m
  27. 67 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetNavView.xib
  28. 41 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetSpeedView.h
  29. 145 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetSpeedView.m
  30. 81 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetSpeedView.xib
  31. 27 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/SmallToolBodyView.h
  32. 77 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/SmallToolBodyView.m
  33. 168 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/SmallToolBodyView.xib
  34. 77 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeView.h
  35. 820 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeView.m
  36. 58 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeViewStyle.h
  37. 32 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeViewStyle3D.h
  38. 131 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeViewStyle3D.m
  39. 29 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeViewStyleFlatThin.h
  40. 128 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeViewStyleFlatThin.m
  41. 19 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/DialPlateView.h
  42. 65 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/DialPlateView.m
  43. 55 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/ToneTuningBodyView.h
  44. 127 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/ToneTuningBodyView.m
  45. 390 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/ToneTuningBodyView.xib
  46. 16 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TunerSettingView.h
  47. 20 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TunerSettingView.m
  48. 18 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TunerSettingView.xib
  49. 29 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningForkSettingView.h
  50. 202 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningForkSettingView.m
  51. 768 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningForkSettingView.xib
  52. 22 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningNavView.h
  53. 52 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningNavView.m
  54. 92 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningNavView.xib

+ 263 - 0
KulexiuForTeacher/KulexiuForTeacher.xcodeproj/project.pbxproj

@@ -1019,6 +1019,37 @@
 		BCFE54152817918600AD6786 /* MyIncomeListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFE54132817918600AD6786 /* MyIncomeListCell.xib */; };
 		BCFE54182817BDFD00AD6786 /* IncomeHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFE54172817BDFD00AD6786 /* IncomeHeaderView.m */; };
 		BCFE541A2817BE0800AD6786 /* IncomeHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFE54192817BE0800AD6786 /* IncomeHeaderView.xib */; };
+		BCFF4814290172C600E9293A /* AudioKit in Frameworks */ = {isa = PBXBuildFile; productRef = BCFF4813290172C600E9293A /* AudioKit */; };
+		BCFF51692901743400E9293A /* WidgetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF51332901743300E9293A /* WidgetViewController.m */; };
+		BCFF516A2901743400E9293A /* ToneTuningViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF51342901743300E9293A /* ToneTuningViewController.m */; };
+		BCFF516B2901743400E9293A /* SmallToolViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF51362901743300E9293A /* SmallToolViewController.m */; };
+		BCFF516C2901743400E9293A /* Tuner.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFF513B2901743300E9293A /* Tuner.swift */; };
+		BCFF516D2901743500E9293A /* TunerForkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFF513C2901743300E9293A /* TunerForkManager.swift */; };
+		BCFF516E2901743500E9293A /* KSMetronomePlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF513D2901743300E9293A /* KSMetronomePlayer.m */; };
+		BCFF516F2901743500E9293A /* SmallToolBodyView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF513F2901743300E9293A /* SmallToolBodyView.m */; };
+		BCFF51702901743500E9293A /* SmallToolBodyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFF51402901743300E9293A /* SmallToolBodyView.xib */; };
+		BCFF51712901743500E9293A /* TuningNavView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFF51422901743300E9293A /* TuningNavView.xib */; };
+		BCFF51722901743500E9293A /* TuningNavView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF51442901743300E9293A /* TuningNavView.m */; };
+		BCFF51732901743500E9293A /* TuningForkSettingView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFF51452901743300E9293A /* TuningForkSettingView.xib */; };
+		BCFF51742901743500E9293A /* TunerSettingView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFF51492901743300E9293A /* TunerSettingView.xib */; };
+		BCFF51752901743500E9293A /* TunerSettingView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF514A2901743300E9293A /* TunerSettingView.m */; };
+		BCFF51762901743500E9293A /* TuningForkSettingView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF514B2901743300E9293A /* TuningForkSettingView.m */; };
+		BCFF51772901743500E9293A /* DialPlateView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF514C2901743300E9293A /* DialPlateView.m */; };
+		BCFF51782901743500E9293A /* ToneTuningBodyView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF514E2901743400E9293A /* ToneTuningBodyView.m */; };
+		BCFF51792901743500E9293A /* ToneTuningBodyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFF514F2901743400E9293A /* ToneTuningBodyView.xib */; };
+		BCFF517A2901743500E9293A /* WMGaugeViewStyleFlatThin.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF51542901743400E9293A /* WMGaugeViewStyleFlatThin.m */; };
+		BCFF517B2901743500E9293A /* WMGaugeViewStyle3D.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF51562901743400E9293A /* WMGaugeViewStyle3D.m */; };
+		BCFF517C2901743500E9293A /* WMGaugeView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF51572901743400E9293A /* WMGaugeView.m */; };
+		BCFF517D2901743500E9293A /* WidgetFunctionView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF515A2901743400E9293A /* WidgetFunctionView.m */; };
+		BCFF517E2901743500E9293A /* WidgetNavView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF515B2901743400E9293A /* WidgetNavView.m */; };
+		BCFF517F2901743500E9293A /* WidgetSpeedView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFF515C2901743400E9293A /* WidgetSpeedView.xib */; };
+		BCFF51802901743500E9293A /* WidgetBottomButtonView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF515D2901743400E9293A /* WidgetBottomButtonView.m */; };
+		BCFF51812901743500E9293A /* WidgetDotView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF515F2901743400E9293A /* WidgetDotView.m */; };
+		BCFF51822901743500E9293A /* WidgetNavView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFF51602901743400E9293A /* WidgetNavView.xib */; };
+		BCFF51832901743500E9293A /* WidgetFunctionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFF51612901743400E9293A /* WidgetFunctionView.xib */; };
+		BCFF51842901743500E9293A /* WidgetDotView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFF51622901743400E9293A /* WidgetDotView.xib */; };
+		BCFF51852901743500E9293A /* WidgetSpeedView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFF51632901743400E9293A /* WidgetSpeedView.m */; };
+		BCFF51862901743500E9293A /* WidgetBottomButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFF51652901743400E9293A /* WidgetBottomButtonView.xib */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -2757,6 +2788,56 @@
 		BCFE54162817BDFD00AD6786 /* IncomeHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IncomeHeaderView.h; sourceTree = "<group>"; };
 		BCFE54172817BDFD00AD6786 /* IncomeHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IncomeHeaderView.m; sourceTree = "<group>"; };
 		BCFE54192817BE0800AD6786 /* IncomeHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IncomeHeaderView.xib; sourceTree = "<group>"; };
+		BCFF4815290172E400E9293A /* KulexiuForTeacher-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "KulexiuForTeacher-Bridging-Header.h"; sourceTree = "<group>"; };
+		BCFF51312901743300E9293A /* ToneTuningViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ToneTuningViewController.h; sourceTree = "<group>"; };
+		BCFF51322901743300E9293A /* SmallToolViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SmallToolViewController.h; sourceTree = "<group>"; };
+		BCFF51332901743300E9293A /* WidgetViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetViewController.m; sourceTree = "<group>"; };
+		BCFF51342901743300E9293A /* ToneTuningViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ToneTuningViewController.m; sourceTree = "<group>"; };
+		BCFF51352901743300E9293A /* WidgetViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetViewController.h; sourceTree = "<group>"; };
+		BCFF51362901743300E9293A /* SmallToolViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SmallToolViewController.m; sourceTree = "<group>"; };
+		BCFF51382901743300E9293A /* KSMetronomePlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSMetronomePlayer.h; sourceTree = "<group>"; };
+		BCFF513B2901743300E9293A /* Tuner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tuner.swift; sourceTree = "<group>"; };
+		BCFF513C2901743300E9293A /* TunerForkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunerForkManager.swift; sourceTree = "<group>"; };
+		BCFF513D2901743300E9293A /* KSMetronomePlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSMetronomePlayer.m; sourceTree = "<group>"; };
+		BCFF513F2901743300E9293A /* SmallToolBodyView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SmallToolBodyView.m; sourceTree = "<group>"; };
+		BCFF51402901743300E9293A /* SmallToolBodyView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SmallToolBodyView.xib; sourceTree = "<group>"; };
+		BCFF51422901743300E9293A /* TuningNavView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TuningNavView.xib; sourceTree = "<group>"; };
+		BCFF51432901743300E9293A /* TunerSettingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TunerSettingView.h; sourceTree = "<group>"; };
+		BCFF51442901743300E9293A /* TuningNavView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TuningNavView.m; sourceTree = "<group>"; };
+		BCFF51452901743300E9293A /* TuningForkSettingView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TuningForkSettingView.xib; sourceTree = "<group>"; };
+		BCFF51462901743300E9293A /* DialPlateView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DialPlateView.h; sourceTree = "<group>"; };
+		BCFF51472901743300E9293A /* TuningForkSettingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TuningForkSettingView.h; sourceTree = "<group>"; };
+		BCFF51482901743300E9293A /* ToneTuningBodyView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ToneTuningBodyView.h; sourceTree = "<group>"; };
+		BCFF51492901743300E9293A /* TunerSettingView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TunerSettingView.xib; sourceTree = "<group>"; };
+		BCFF514A2901743300E9293A /* TunerSettingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TunerSettingView.m; sourceTree = "<group>"; };
+		BCFF514B2901743300E9293A /* TuningForkSettingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TuningForkSettingView.m; sourceTree = "<group>"; };
+		BCFF514C2901743300E9293A /* DialPlateView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DialPlateView.m; sourceTree = "<group>"; };
+		BCFF514D2901743300E9293A /* TuningNavView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TuningNavView.h; sourceTree = "<group>"; };
+		BCFF514E2901743400E9293A /* ToneTuningBodyView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ToneTuningBodyView.m; sourceTree = "<group>"; };
+		BCFF514F2901743400E9293A /* ToneTuningBodyView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ToneTuningBodyView.xib; sourceTree = "<group>"; };
+		BCFF51502901743400E9293A /* SmallToolBodyView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SmallToolBodyView.h; sourceTree = "<group>"; };
+		BCFF51522901743400E9293A /* WMGaugeViewStyle3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMGaugeViewStyle3D.h; sourceTree = "<group>"; };
+		BCFF51532901743400E9293A /* WMGaugeViewStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMGaugeViewStyle.h; sourceTree = "<group>"; };
+		BCFF51542901743400E9293A /* WMGaugeViewStyleFlatThin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMGaugeViewStyleFlatThin.m; sourceTree = "<group>"; };
+		BCFF51552901743400E9293A /* WMGaugeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMGaugeView.h; sourceTree = "<group>"; };
+		BCFF51562901743400E9293A /* WMGaugeViewStyle3D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMGaugeViewStyle3D.m; sourceTree = "<group>"; };
+		BCFF51572901743400E9293A /* WMGaugeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMGaugeView.m; sourceTree = "<group>"; };
+		BCFF51582901743400E9293A /* WMGaugeViewStyleFlatThin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMGaugeViewStyleFlatThin.h; sourceTree = "<group>"; };
+		BCFF515A2901743400E9293A /* WidgetFunctionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetFunctionView.m; sourceTree = "<group>"; };
+		BCFF515B2901743400E9293A /* WidgetNavView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetNavView.m; sourceTree = "<group>"; };
+		BCFF515C2901743400E9293A /* WidgetSpeedView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WidgetSpeedView.xib; sourceTree = "<group>"; };
+		BCFF515D2901743400E9293A /* WidgetBottomButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetBottomButtonView.m; sourceTree = "<group>"; };
+		BCFF515E2901743400E9293A /* WidgetSpeedView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetSpeedView.h; sourceTree = "<group>"; };
+		BCFF515F2901743400E9293A /* WidgetDotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetDotView.m; sourceTree = "<group>"; };
+		BCFF51602901743400E9293A /* WidgetNavView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WidgetNavView.xib; sourceTree = "<group>"; };
+		BCFF51612901743400E9293A /* WidgetFunctionView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WidgetFunctionView.xib; sourceTree = "<group>"; };
+		BCFF51622901743400E9293A /* WidgetDotView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WidgetDotView.xib; sourceTree = "<group>"; };
+		BCFF51632901743400E9293A /* WidgetSpeedView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetSpeedView.m; sourceTree = "<group>"; };
+		BCFF51642901743400E9293A /* WidgetBottomButtonView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetBottomButtonView.h; sourceTree = "<group>"; };
+		BCFF51652901743400E9293A /* WidgetBottomButtonView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WidgetBottomButtonView.xib; sourceTree = "<group>"; };
+		BCFF51662901743400E9293A /* WidgetNavView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetNavView.h; sourceTree = "<group>"; };
+		BCFF51672901743400E9293A /* WidgetFunctionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetFunctionView.h; sourceTree = "<group>"; };
+		BCFF51682901743400E9293A /* WidgetDotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetDotView.h; sourceTree = "<group>"; };
 		CC310B10C17622ABB179BC7C /* libPods-KulexiuForTeacher.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-KulexiuForTeacher.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
 
@@ -2766,6 +2847,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				BC8B6E5A2856ED0600866917 /* UMShare.framework in Frameworks */,
+				BCFF4814290172C600E9293A /* AudioKit in Frameworks */,
 				BC8B6E642856ED0700866917 /* TencentOpenAPI.framework in Frameworks */,
 				2779334727E3148E0010E277 /* GLKit.framework in Frameworks */,
 				2779334527E314870010E277 /* VideoToolbox.framework in Frameworks */,
@@ -3853,6 +3935,7 @@
 		277935D927E325B90010E277 /* Module */ = {
 			isa = PBXGroup;
 			children = (
+				BCFF512F2901743300E9293A /* Widget */,
 				BC71D17E2887FDAB0010F14B /* AnimationLaunch */,
 				BC24570E286C437D00D1F7C0 /* SoundFontFile */,
 				BC245708286C436E00D1F7C0 /* beat */,
@@ -6769,6 +6852,119 @@
 			path = View;
 			sourceTree = "<group>";
 		};
+		BCFF512F2901743300E9293A /* Widget */ = {
+			isa = PBXGroup;
+			children = (
+				BCFF51302901743300E9293A /* Controller */,
+				BCFF51372901743300E9293A /* Model */,
+				BCFF513E2901743300E9293A /* View */,
+			);
+			path = Widget;
+			sourceTree = "<group>";
+		};
+		BCFF51302901743300E9293A /* Controller */ = {
+			isa = PBXGroup;
+			children = (
+				BCFF51322901743300E9293A /* SmallToolViewController.h */,
+				BCFF51362901743300E9293A /* SmallToolViewController.m */,
+				BCFF51312901743300E9293A /* ToneTuningViewController.h */,
+				BCFF51342901743300E9293A /* ToneTuningViewController.m */,
+				BCFF51352901743300E9293A /* WidgetViewController.h */,
+				BCFF51332901743300E9293A /* WidgetViewController.m */,
+			);
+			path = Controller;
+			sourceTree = "<group>";
+		};
+		BCFF51372901743300E9293A /* Model */ = {
+			isa = PBXGroup;
+			children = (
+				BCFF51382901743300E9293A /* KSMetronomePlayer.h */,
+				BCFF513D2901743300E9293A /* KSMetronomePlayer.m */,
+				BCFF51392901743300E9293A /* TuningFunction */,
+			);
+			path = Model;
+			sourceTree = "<group>";
+		};
+		BCFF51392901743300E9293A /* TuningFunction */ = {
+			isa = PBXGroup;
+			children = (
+				BCFF4815290172E400E9293A /* KulexiuForTeacher-Bridging-Header.h */,
+				BCFF513B2901743300E9293A /* Tuner.swift */,
+				BCFF513C2901743300E9293A /* TunerForkManager.swift */,
+			);
+			path = TuningFunction;
+			sourceTree = "<group>";
+		};
+		BCFF513E2901743300E9293A /* View */ = {
+			isa = PBXGroup;
+			children = (
+				BCFF51592901743400E9293A /* Metronome */,
+				BCFF51502901743400E9293A /* SmallToolBodyView.h */,
+				BCFF513F2901743300E9293A /* SmallToolBodyView.m */,
+				BCFF51402901743300E9293A /* SmallToolBodyView.xib */,
+				BCFF51412901743300E9293A /* toneTuning */,
+				BCFF51512901743400E9293A /* WMGaugeView */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
+		BCFF51412901743300E9293A /* toneTuning */ = {
+			isa = PBXGroup;
+			children = (
+				BCFF51422901743300E9293A /* TuningNavView.xib */,
+				BCFF51432901743300E9293A /* TunerSettingView.h */,
+				BCFF51442901743300E9293A /* TuningNavView.m */,
+				BCFF51452901743300E9293A /* TuningForkSettingView.xib */,
+				BCFF51462901743300E9293A /* DialPlateView.h */,
+				BCFF51472901743300E9293A /* TuningForkSettingView.h */,
+				BCFF51482901743300E9293A /* ToneTuningBodyView.h */,
+				BCFF51492901743300E9293A /* TunerSettingView.xib */,
+				BCFF514A2901743300E9293A /* TunerSettingView.m */,
+				BCFF514B2901743300E9293A /* TuningForkSettingView.m */,
+				BCFF514C2901743300E9293A /* DialPlateView.m */,
+				BCFF514D2901743300E9293A /* TuningNavView.h */,
+				BCFF514E2901743400E9293A /* ToneTuningBodyView.m */,
+				BCFF514F2901743400E9293A /* ToneTuningBodyView.xib */,
+			);
+			path = toneTuning;
+			sourceTree = "<group>";
+		};
+		BCFF51512901743400E9293A /* WMGaugeView */ = {
+			isa = PBXGroup;
+			children = (
+				BCFF51522901743400E9293A /* WMGaugeViewStyle3D.h */,
+				BCFF51532901743400E9293A /* WMGaugeViewStyle.h */,
+				BCFF51542901743400E9293A /* WMGaugeViewStyleFlatThin.m */,
+				BCFF51552901743400E9293A /* WMGaugeView.h */,
+				BCFF51562901743400E9293A /* WMGaugeViewStyle3D.m */,
+				BCFF51572901743400E9293A /* WMGaugeView.m */,
+				BCFF51582901743400E9293A /* WMGaugeViewStyleFlatThin.h */,
+			);
+			path = WMGaugeView;
+			sourceTree = "<group>";
+		};
+		BCFF51592901743400E9293A /* Metronome */ = {
+			isa = PBXGroup;
+			children = (
+				BCFF515A2901743400E9293A /* WidgetFunctionView.m */,
+				BCFF515B2901743400E9293A /* WidgetNavView.m */,
+				BCFF515C2901743400E9293A /* WidgetSpeedView.xib */,
+				BCFF515D2901743400E9293A /* WidgetBottomButtonView.m */,
+				BCFF515E2901743400E9293A /* WidgetSpeedView.h */,
+				BCFF515F2901743400E9293A /* WidgetDotView.m */,
+				BCFF51602901743400E9293A /* WidgetNavView.xib */,
+				BCFF51612901743400E9293A /* WidgetFunctionView.xib */,
+				BCFF51622901743400E9293A /* WidgetDotView.xib */,
+				BCFF51632901743400E9293A /* WidgetSpeedView.m */,
+				BCFF51642901743400E9293A /* WidgetBottomButtonView.h */,
+				BCFF51652901743400E9293A /* WidgetBottomButtonView.xib */,
+				BCFF51662901743400E9293A /* WidgetNavView.h */,
+				BCFF51672901743400E9293A /* WidgetFunctionView.h */,
+				BCFF51682901743400E9293A /* WidgetDotView.h */,
+			);
+			path = Metronome;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -6788,6 +6984,9 @@
 			dependencies = (
 			);
 			name = KulexiuForTeacher;
+			packageProductDependencies = (
+				BCFF4813290172C600E9293A /* AudioKit */,
+			);
 			productName = KulexiuForTeacher;
 			productReference = 275E8A6427E18F2300DD3F6E /* KulexiuForTeacher.app */;
 			productType = "com.apple.product-type.application";
@@ -6839,6 +7038,7 @@
 				TargetAttributes = {
 					275E8A6327E18F2300DD3F6E = {
 						CreatedOnToolsVersion = 13.2.1;
+						LastSwiftMigration = 1400;
 					};
 					275E8A7E27E18F2800DD3F6E = {
 						CreatedOnToolsVersion = 13.2.1;
@@ -6860,6 +7060,9 @@
 				"zh-Hans",
 			);
 			mainGroup = 275E8A5B27E18F2300DD3F6E;
+			packageReferences = (
+				BCFF4812290172C600E9293A /* XCRemoteSwiftPackageReference "AudioKit" */,
+			);
 			productRefGroup = 275E8A6527E18F2300DD3F6E /* Products */;
 			projectDirPath = "";
 			projectRoot = "";
@@ -6928,6 +7131,7 @@
 				BCC583FC28A9FA8100BAB4CF /* cloud_animation_19.png in Resources */,
 				BCB908FD2850C9CB00F5FF69 /* MusicChooseSearchView.xib in Resources */,
 				BC221FA228C72B9500F99802 /* MyStyleVideoCell.xib in Resources */,
+				BCFF517F2901743500E9293A /* WidgetSpeedView.xib in Resources */,
 				275E8A7227E18F2300DD3F6E /* Main.storyboard in Resources */,
 				BC3673DE28A606A500059721 /* musicRoom_animation_1.png in Resources */,
 				2780C92927E490CA00A95A4F /* VefiBodyView.xib in Resources */,
@@ -6943,6 +7147,7 @@
 				BC71D2052887FDD40010F14B /* img_27.png in Resources */,
 				BC542E3828406F8000633781 /* UserAuthBodyView.xib in Resources */,
 				27A54CEE27E9B986007309A3 /* ModifyNameBodyView.xib in Resources */,
+				BCFF51862901743500E9293A /* WidgetBottomButtonView.xib in Resources */,
 				BC221FA128C72B9500F99802 /* MyStyleVideoView.xib in Resources */,
 				BCC583FA28A9FA8100BAB4CF /* cloud_animation_26.png in Resources */,
 				2708565E27EDA83100EC8E72 /* GroupMemberListCell.xib in Resources */,
@@ -6997,7 +7202,9 @@
 				275B16F127EAF9DD0081FDEF /* ChatNavView.xib in Resources */,
 				2773205327EDB75B008FAECA /* GroupNoticeCell.xib in Resources */,
 				2755C08627ED5770007D9070 /* GroupApplyMemberCell.xib in Resources */,
+				BCFF51832901743500E9293A /* WidgetFunctionView.xib in Resources */,
 				BC71D297288811BF0010F14B /* tabbar2.json in Resources */,
+				BCFF51702901743500E9293A /* SmallToolBodyView.xib in Resources */,
 				27BC3B3027F2FC7200D81E30 /* VideoCourseCell.xib in Resources */,
 				BC7CFFB62817E90700CAEB21 /* KSSegView.xib in Resources */,
 				2779362227E334470010E277 /* KSUpdateAlert.xib in Resources */,
@@ -7006,6 +7213,7 @@
 				BC71D2082887FDD40010F14B /* img_20.png in Resources */,
 				BC7705FF287676DC003EFA7F /* HomeActionView.xib in Resources */,
 				BCC5840028A9FA8100BAB4CF /* cloud_animation_15.png in Resources */,
+				BCFF51792901743500E9293A /* ToneTuningBodyView.xib in Resources */,
 				BC4BCE772823AA3F00522C8B /* areainfo.json in Resources */,
 				BC71D1F62887FDD40010F14B /* img_29.png in Resources */,
 				BCF1BA5627F5CBA100FA36C4 /* LiveSeatApplyView.xib in Resources */,
@@ -7017,6 +7225,7 @@
 				BCC583FB28A9FA8100BAB4CF /* cloud_animation_27.png in Resources */,
 				BC12637E28FEB5B900509E90 /* UserDetailNavView.xib in Resources */,
 				BCB9FA4F28717BBD005D766B /* MyMusicSearchView.xib in Resources */,
+				BCFF51732901743500E9293A /* TuningForkSettingView.xib in Resources */,
 				BCC5840A28A9FA8100BAB4CF /* cloud_animation_6.png in Resources */,
 				BC483233282A6473005F534C /* HomeRecentCourseView.xib in Resources */,
 				2708565927ED961900EC8E72 /* ApplyBottomView.xib in Resources */,
@@ -7063,6 +7272,7 @@
 				BCC5841628A9FA9D00BAB4CF /* AccompanyLoadingView.xib in Resources */,
 				BC2456EA286BEF8000D1F7C0 /* MineEmptyGroupCell.xib in Resources */,
 				BCB6340527F6A2F000ACFDCF /* tock.wav in Resources */,
+				BCFF51842901743500E9293A /* WidgetDotView.xib in Resources */,
 				BCC03F9A2805681100461B7C /* MyLessonSearchView.xib in Resources */,
 				BCC5840B28A9FA8100BAB4CF /* cloud_animation_7.png in Resources */,
 				BCC03F9F280579A500461B7C /* MyLiveCourseCell.xib in Resources */,
@@ -7146,6 +7356,8 @@
 				BC221FAF28C736C300F99802 /* MyCreateGroupBottomView.xib in Resources */,
 				BCC5841128A9FA8100BAB4CF /* cloud_animation_2.png in Resources */,
 				275E3DE327F467410010EC30 /* BaseEmoji.plist in Resources */,
+				BCFF51822901743500E9293A /* WidgetNavView.xib in Resources */,
+				BCFF51712901743500E9293A /* TuningNavView.xib in Resources */,
 				BCC5842028AA559700BAB4CF /* cloud_animation_29.png in Resources */,
 				273C75A027E9681700F7C26F /* SettingBodyView.xib in Resources */,
 				BCC5840528A9FA8100BAB4CF /* cloud_animation_17.png in Resources */,
@@ -7160,6 +7372,7 @@
 				BC71D2032887FDD40010F14B /* img_24.png in Resources */,
 				BC71D1F52887FDD40010F14B /* img_17.png in Resources */,
 				BCA9CE5127FD954800D558C6 /* AccompanyRemarkCell.xib in Resources */,
+				BCFF51742901743500E9293A /* TunerSettingView.xib in Resources */,
 				BCB9FA682872C8F0005D766B /* FinishedLiveCell.xib in Resources */,
 				BC513E7C28A4D868003F58C4 /* musicRoom_animation.json in Resources */,
 				BCC03F89280460C000461B7C /* InstrumentHeaderView.xib in Resources */,
@@ -7296,10 +7509,12 @@
 				BCA1135328A242FD007FAFB9 /* HomeBannerView.m in Sources */,
 				BC8831042873D67C00C702A0 /* LiveVideoCollectionViewCell.m in Sources */,
 				BC12638428FEB5B900509E90 /* RecentMusicView.m in Sources */,
+				BCFF516F2901743500E9293A /* SmallToolBodyView.m in Sources */,
 				BCB633F527F6A18200ACFDCF /* NewClassRoomViewController.m in Sources */,
 				BC4BCE6C28239EEB00522C8B /* MyAddressListCell.m in Sources */,
 				BCD6D16228195A17009A773E /* WithdrawApplyViewController.m in Sources */,
 				277932B627E30FFE0010E277 /* UIView+SDExtension.m in Sources */,
+				BCFF51722901743500E9293A /* TuningNavView.m in Sources */,
 				BCC9F42227F69BD200647449 /* ToolPanelView.m in Sources */,
 				2779335C27E317CD0010E277 /* KSBaseModel.m in Sources */,
 				BC7CFFB72817E90700CAEB21 /* KSSegView.m in Sources */,
@@ -7327,6 +7542,7 @@
 				2780C92727E490BD00A95A4F /* VefiBodyView.m in Sources */,
 				277932B827E30FFE0010E277 /* SDCollectionViewCell.m in Sources */,
 				2779326527E30FD80010E277 /* FSCalendarDelegationFactory.m in Sources */,
+				BCFF51812901743500E9293A /* WidgetDotView.m in Sources */,
 				277931E127E30FC20010E277 /* NSString+zh_SafeAccess.m in Sources */,
 				BCEA75302819336200886A86 /* WithdrawBodyView.m in Sources */,
 				BC76146A280D4F670080FD1F /* HomeworkDetailModel.m in Sources */,
@@ -7380,6 +7596,7 @@
 				277935EB27E32A930010E277 /* KSBaseWKWebViewController.m in Sources */,
 				BCA1134C28A23221007FAFB9 /* HomeHotMusicCellView.m in Sources */,
 				275B172627EB1C6C0081FDEF /* KSBaseTableViewController.m in Sources */,
+				BCFF516D2901743500E9293A /* TunerForkManager.swift in Sources */,
 				BC2456E9286BEF8000D1F7C0 /* MineEmptyGroupCell.m in Sources */,
 				BCC9F43D27F69BD200647449 /* InviteUpgradeMessage.m in Sources */,
 				BC7CFFCD2817FE8C00CAEB21 /* BandCardViewController.m in Sources */,
@@ -7394,6 +7611,7 @@
 				BCD6D16A28195FBE009A773E /* CashRecordHeadView.m in Sources */,
 				277931F027E30FC20010E277 /* UIView+AddConstraints.m in Sources */,
 				277D432527E9A46A00107DB7 /* ModifyPhoneCheckController.m in Sources */,
+				BCFF516C2901743400E9293A /* Tuner.swift in Sources */,
 				BCC9F43327F69BD200647449 /* KSRemoteUserManager.m in Sources */,
 				27F9CAF727EC1A16003E0FE4 /* UITableView+SCIndexView.m in Sources */,
 				275E3DC827F460720010EC30 /* KSLiveChatroomKickOut.m in Sources */,
@@ -7401,6 +7619,7 @@
 				BCC03F95280562F700461B7C /* MyLessonBodyView.m in Sources */,
 				BCE6A09027F823BE00C97704 /* LiveCourseCell.m in Sources */,
 				BCC03F98280567CC00461B7C /* MyLessonSearchView.m in Sources */,
+				BCFF516A2901743400E9293A /* ToneTuningViewController.m in Sources */,
 				BCE6A07F27F7FACA00C97704 /* UIView+SubViewExtension.m in Sources */,
 				BCA9CE5027FD954800D558C6 /* AccompanyRemarkCell.m in Sources */,
 				BCDE359B289BC02200A9A560 /* HomeHotAlbumCell.m in Sources */,
@@ -7417,6 +7636,7 @@
 				BCE6A09F27F84E4500C97704 /* MineIntroduceCell.m in Sources */,
 				BC12638C28FEB5E600509E90 /* ChatUserInfo.m in Sources */,
 				BCF1BA5A27F5CF3C00FA36C4 /* LiveSeatApplyCell.m in Sources */,
+				BCFF517D2901743500E9293A /* WidgetFunctionView.m in Sources */,
 				BCC9F43527F69BD200647449 /* DisplayCommandMessage.m in Sources */,
 				BC7CFFAF2817E6C900CAEB21 /* IncomeCountBottomView.m in Sources */,
 				BCC9F43F27F69BD200647449 /* DeviceMessage.m in Sources */,
@@ -7439,6 +7659,7 @@
 				27F9030E27E875DD00C08A19 /* AudioPlayManager.m in Sources */,
 				BCC9F41127F69BD200647449 /* MessageCell.m in Sources */,
 				277935E227E327F00010E277 /* KSTabBarViewController.m in Sources */,
+				BCFF517A2901743500E9293A /* WMGaugeViewStyleFlatThin.m in Sources */,
 				2779322727E30FC30010E277 /* KSInputView.m in Sources */,
 				BC71D293288811BF0010F14B /* AnimationHelper.m in Sources */,
 				BC60E3C0287D447F00B05441 /* DeleteAccountBodyView.m in Sources */,
@@ -7499,6 +7720,7 @@
 				BCC9F40927F69BD200647449 /* ZoomControl.m in Sources */,
 				275B171A27EB1BBC0081FDEF /* KSSearchResultModel.m in Sources */,
 				27F9CB0B27EC5C06003E0FE4 /* KSSelectConversationViewController.m in Sources */,
+				BCFF51762901743500E9293A /* TuningForkSettingView.m in Sources */,
 				BC8B6DBF28532DB800866917 /* MusicSheetVoList.m in Sources */,
 				2755C08D27ED5DB2007D9070 /* GroupApplyChooseCell.m in Sources */,
 				BC02BCF128B32771005CB483 /* KSDownSeatAllMessage.m in Sources */,
@@ -7513,6 +7735,7 @@
 				BC1191F6280EBC7D00A716F7 /* AccompanyDetailBottomView.m in Sources */,
 				BCB909042851E25D00F5FF69 /* KSShareChooseViewController.m in Sources */,
 				277932B927E30FFE0010E277 /* TADotView.m in Sources */,
+				BCFF517E2901743500E9293A /* WidgetNavView.m in Sources */,
 				2773204D27EDB72B008FAECA /* LFPopupMenu.m in Sources */,
 				BCC03F8F2805484200461B7C /* StyleVideoModel.m in Sources */,
 				BC0167A627FC06D600AE66A1 /* MyCourseViewController.m in Sources */,
@@ -7522,6 +7745,7 @@
 				2779320127E30FC30010E277 /* KSStatusView.m in Sources */,
 				2779323027E30FC30010E277 /* ALCalendarPicker.m in Sources */,
 				BCB9FA13286C7C6C005D766B /* KSShareGroupViewController.m in Sources */,
+				BCFF517B2901743500E9293A /* WMGaugeViewStyle3D.m in Sources */,
 				2779323E27E30FC30010E277 /* VoCacheManager.m in Sources */,
 				277931DE27E30FC20010E277 /* NSObject+AssociatedObject.m in Sources */,
 				27D83F4927F3EBC400062476 /* CreateLiveViewController.m in Sources */,
@@ -7567,6 +7791,7 @@
 				277931E727E30FC20010E277 /* UILabel+Extension.m in Sources */,
 				2780C92227E4902800A95A4F /* PasswordBodyView.m in Sources */,
 				275E8A6927E18F2300DD3F6E /* AppDelegate.m in Sources */,
+				BCFF51782901743500E9293A /* ToneTuningBodyView.m in Sources */,
 				2779361E27E3338E0010E277 /* KSUpdateManager.m in Sources */,
 				BCC9F42827F69BD200647449 /* WhiteUtils.m in Sources */,
 				BCB635BF27F7256B00ACFDCF /* KSDocument.m in Sources */,
@@ -7645,6 +7870,7 @@
 				2779322A27E30FC30010E277 /* ALCalendarCell.m in Sources */,
 				275FA1A027E7250700CFEA2E /* KSAccompanyWebViewController.m in Sources */,
 				BC1365CC280D478F00EB03E2 /* NotiferMessageModel.m in Sources */,
+				BCFF51752901743500E9293A /* TunerSettingView.m in Sources */,
 				2755C07727EC945D007D9070 /* GroupMemberViewController.m in Sources */,
 				2773205227EDB75B008FAECA /* GroupNoticeCell.m in Sources */,
 				BC0238092865C601005560CA /* KSRCShopRushMessage.m in Sources */,
@@ -7656,13 +7882,16 @@
 				277931E027E30FC20010E277 /* UIDevice+zhDeviceType.m in Sources */,
 				BCD457B72865651A0010B493 /* LiveMoreDisplayView.m in Sources */,
 				2779329227E30FEB0010E277 /* MSSBrowseActionSheet.m in Sources */,
+				BCFF51772901743500E9293A /* DialPlateView.m in Sources */,
 				27F9CB0027EC3D42003E0FE4 /* GroupListViewCell.m in Sources */,
 				BC6C303A27F586A60044BC0F /* KSRCMessageModel.m in Sources */,
+				BCFF517C2901743500E9293A /* WMGaugeView.m in Sources */,
 				BC8B6DC028532DB800866917 /* MusicShareModel.m in Sources */,
 				2780C91C27E48A0700A95A4F /* PasswordLoginController.m in Sources */,
 				BCC9F43827F69BD200647449 /* TicketExpiredMessage.m in Sources */,
 				2779336027E31CB40010E277 /* UIView+KSExtension.m in Sources */,
 				BC0A22A7284751F80065C1AB /* AccompanyProgressView.m in Sources */,
+				BCFF51852901743500E9293A /* WidgetSpeedView.m in Sources */,
 				2779321C27E30FC30010E277 /* prodectButton.m in Sources */,
 				BC7E770C2900DD8E00EB37AF /* HomeDragButton.m in Sources */,
 				BCE6A08B27F823A300C97704 /* AccompanyCourseCell.m in Sources */,
@@ -7702,6 +7931,7 @@
 				BCA9CE1527FD339400D558C6 /* AuthDisplayView.m in Sources */,
 				2779329B27E30FEB0010E277 /* UIView+MSSLayout.m in Sources */,
 				2779322B27E30FC30010E277 /* ALCalendarConfig.m in Sources */,
+				BCFF51692901743400E9293A /* WidgetViewController.m in Sources */,
 				BC1365BB280D162400EB03E2 /* MyVideoSearchView.m in Sources */,
 				BC8B6E72285838D200866917 /* KSQRCreateManager.m in Sources */,
 				2779323527E30FC30010E277 /* LLPhotoBrowser.m in Sources */,
@@ -7718,6 +7948,7 @@
 				BC14A61228A0AC820086395C /* MineTeachToolView.m in Sources */,
 				BC02380F2865C6F9005560CA /* RCChatroomLikeCount.m in Sources */,
 				2779321727E30FC30010E277 /* UITextField_Toolbar.m in Sources */,
+				BCFF51802901743500E9293A /* WidgetBottomButtonView.m in Sources */,
 				BCB633F927F6A18200ACFDCF /* ClassVideoListView.m in Sources */,
 				BC2456E3286BE85A00D1F7C0 /* MineStyleEmptyView.m in Sources */,
 				277D432F27E9A50800107DB7 /* PhoneChangeBodyView.m in Sources */,
@@ -7725,6 +7956,7 @@
 				BCB908FB2850C9C300F5FF69 /* MusicChooseSearchView.m in Sources */,
 				BC0A22AA284751F80065C1AB /* FullVideoCell.m in Sources */,
 				BCC9F41627F69BD200647449 /* TimeStampMessage.m in Sources */,
+				BCFF516E2901743500E9293A /* KSMetronomePlayer.m in Sources */,
 				BC2456EE286BEFC600D1F7C0 /* MineEmptyIntroduceCell.m in Sources */,
 				2779326C27E30FD80010E277 /* FSCalendarExtensions.m in Sources */,
 				BCF61BEC28042D1A0000ACFE /* InstrumentMessageModel.m in Sources */,
@@ -7859,6 +8091,7 @@
 				275E3DD427F463580010EC30 /* KSLiveChatroomSeatResponse.m in Sources */,
 				BCC9F41927F69BD200647449 /* VideoListCell.m in Sources */,
 				BCEA751D2818F6C900886A86 /* MyCardModel.m in Sources */,
+				BCFF516B2901743400E9293A /* SmallToolViewController.m in Sources */,
 				2779327027E30FD80010E277 /* FSCalendarTransitionCoordinator.m in Sources */,
 				BCC9F42C27F69BD200647449 /* InputTextField.m in Sources */,
 				2779320727E30FC30010E277 /* KSRecordStatusView.m in Sources */,
@@ -8114,6 +8347,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = KulexiuForTeacher/KulexiuForTeacher.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1.4.0;
@@ -8166,6 +8400,9 @@
 				PRODUCT_BUNDLE_IDENTIFIER = com.Colexiu.KulexiuForTeacher;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "KulexiuForTeacher/Module/Widget/Model/TuningFunction/KulexiuForTeacher-Bridging-Header.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
 			name = Debug;
@@ -8176,6 +8413,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = KulexiuForTeacher/KulexiuForTeacher.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1.4.0;
@@ -8228,6 +8466,8 @@
 				PRODUCT_BUNDLE_IDENTIFIER = com.Colexiu.KulexiuForTeacher;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "KulexiuForTeacher/Module/Widget/Model/TuningFunction/KulexiuForTeacher-Bridging-Header.h";
+				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
 			name = Release;
@@ -8235,6 +8475,7 @@
 		275E8A9727E18F2900DD3F6E /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
@@ -8253,6 +8494,7 @@
 		275E8A9827E18F2900DD3F6E /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
@@ -8271,6 +8513,7 @@
 		275E8A9A27E18F2900DD3F6E /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = P664H7S5LL;
@@ -8287,6 +8530,7 @@
 		275E8A9B27E18F2900DD3F6E /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = P664H7S5LL;
@@ -8340,6 +8584,25 @@
 			defaultConfigurationName = Release;
 		};
 /* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+		BCFF4812290172C600E9293A /* XCRemoteSwiftPackageReference "AudioKit" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/AudioKit/AudioKit.git";
+			requirement = {
+				kind = exactVersion;
+				version = 5.0.0;
+			};
+		};
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+		BCFF4813290172C600E9293A /* AudioKit */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = BCFF4812290172C600E9293A /* XCRemoteSwiftPackageReference "AudioKit" */;
+			productName = AudioKit;
+		};
+/* End XCSwiftPackageProductDependency section */
 	};
 	rootObject = 275E8A5C27E18F2300DD3F6E /* Project object */;
 }

+ 14 - 0
KulexiuForTeacher/KulexiuForTeacher.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -0,0 +1,14 @@
+{
+  "pins" : [
+    {
+      "identity" : "audiokit",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/AudioKit/AudioKit.git",
+      "state" : {
+        "revision" : "36ea09c87bf703291a60e694742d56ff221c56ae",
+        "version" : "5.0.0"
+      }
+    }
+  ],
+  "version" : 2
+}

+ 1 - 1
KulexiuForTeacher/KulexiuForTeacher.xcworkspace/xcuserdata/wangzhi.xcuserdatad/WorkspaceSettings.xcsettings

@@ -3,7 +3,7 @@
 <plist version="1.0">
 <dict>
 	<key>BuildLocationStyle</key>
-	<string>UseTargetSettings</string>
+	<string>UseAppPreferences</string>
 	<key>CustomBuildLocationType</key>
 	<string>RelativeToDerivedData</string>
 	<key>DerivedDataLocationStyle</key>

+ 55 - 0
KulexiuForTeacher/KulexiuForTeacher/Common/Base/KSAccompanyWebViewController.m

@@ -19,6 +19,7 @@
 #import "AccompanyLoadingView.h"
 
 #define KSMidiSongFileKey (@"KSDownloadMidiSong")
+#import "KulexiuForTeacher-swift.h"
 
 @interface KSAccompanyWebViewController ()<KSAQRecordManagerDelegate,KSAudioSessionManagerDelegate,PlayerEngineDelegate>
 
@@ -70,6 +71,10 @@
 
 @property (nonatomic, strong) AccompanyLoadingView *loadingView;
 
+@property (nonatomic, assign) BOOL isTunerRuning;
+
+@property (nonatomic, strong) Tuner *tuner;
+
 @end
 
 @implementation KSAccompanyWebViewController
@@ -288,6 +293,7 @@
         [self.playerEngine cleanup];
         self.playerEngine = nil;
     }
+    [self stopTuner];
     
 }
 
@@ -652,6 +658,16 @@
                     [self removeCustomLoadingView];
                 }
             }
+            else if ([[parm stringValueForKey:@"api"] isEqualToString:@"cloudToggleFollow"]) { // 跟音
+                NSDictionary *content = [parm dictionaryValueForKey:@"content"];
+                NSString *status = [content stringValueForKey:@"state"];
+                if ([status isEqualToString:@"start"]) { // 开始
+                    [self startTuner];
+                }
+                else if ([status isEqualToString:@"end"]) { // 结束
+                    [self stopTuner];
+                }
+            }
             else {
                 [super handleScriptMessageSource:parm];
             }
@@ -1118,6 +1134,45 @@
 - (void)removeCustomLoadingView {
     [self.loadingView stopLoading];
 }
+
+#pragma mark ----- 跟音模块
+- (void)startTuner {
+    if (self.isTunerRuning == NO) {
+        self.isTunerRuning = YES;
+        [self.tuner start];
+    }
+}
+
+- (void)stopTuner {
+    if (self.isTunerRuning) {
+        self.isTunerRuning = NO;
+        [self.tuner stop];
+    }
+}
+
+- (Tuner *)tuner {
+    if (!_tuner) {
+        _tuner = [[Tuner alloc] initWithThreshold:0 smoothing:0.25];
+        _tuner.delegate = self;
+    }
+    return _tuner;
+}
+
+- (void)tunerDidUpdate:(Tuner *)tuner output:(TunerOutput *)output {
+    if (output.amplitude < 0.01) {
+        
+    }
+    else {
+        // 回调频率
+        NSDictionary *parm = @{
+            @"api" : @"cloudFollowTime",
+            @"content" : @{@"frequency" : [NSNumber numberWithDouble:output.frequency]}
+        };
+        [self postMessage:parm];
+    }
+    NSLog(@"-------- %@%zd --- distance :%f frequence : %f" , output.pitch, output.octave, output.distance, output.frequency);
+    
+}
 /*
 #pragma mark - Navigation
 

+ 16 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/SmallToolViewController.h

@@ -0,0 +1,16 @@
+//
+//  SmallToolViewController.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "KSBaseViewController.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SmallToolViewController : KSBaseViewController
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 66 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/SmallToolViewController.m

@@ -0,0 +1,66 @@
+//
+//  SmallToolViewController.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "SmallToolViewController.h"
+#import "WidgetViewController.h"
+#import "SmallToolBodyView.h"
+#import "ToneTuningViewController.h"
+@interface SmallToolViewController ()
+
+
+@end
+
+@implementation SmallToolViewController
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    // Do any additional setup after loading the view.
+    [self allocTitle:@"小工具"];
+    [self configUI];
+}
+
+- (void)configUI {
+    SmallToolBodyView *bodyView = [SmallToolBodyView shareInstance];
+    bodyView.frame = CGRectMake(0, 0, KPortraitWidth, KPortraitHeight - kNaviBarHeight);
+    [self.scrollView addSubview:bodyView];
+    
+    MJWeakSelf;
+    [bodyView chooseToolCallback:^(TOOLTYPE type) {
+        [weakSelf chooseToolFunction:type];
+    }];
+}
+
+- (void)chooseToolFunction:(TOOLTYPE)type {
+    switch (type) {
+        case TOOLTYPE_METRONOME:
+        {
+            WidgetViewController *ctrl = [[WidgetViewController alloc] init];
+            [self.navigationController pushViewController:ctrl animated:YES];
+        }
+            break;
+        case TOOLTYPE_TONE:
+        {
+            ToneTuningViewController *ctrl = [[ToneTuningViewController alloc] init];
+            [self.navigationController pushViewController:ctrl animated:YES];
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+/*
+#pragma mark - Navigation
+
+// In a storyboard-based application, you will often want to do a little preparation before navigation
+- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
+    // Get the new view controller using [segue destinationViewController].
+    // Pass the selected object to the new view controller.
+}
+*/
+
+@end

+ 16 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/ToneTuningViewController.h

@@ -0,0 +1,16 @@
+//
+//  ToneTuningViewController.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "KSBaseViewController.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ToneTuningViewController : KSBaseViewController
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 265 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/ToneTuningViewController.m

@@ -0,0 +1,265 @@
+//
+//  ToneTuningViewController.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "ToneTuningViewController.h"
+#import "TuningNavView.h"
+#import "KulexiuForTeacher-swift.h"
+#import "ToneTuningBodyView.h"
+#import "DialPlateView.h"
+#import "TuningForkSettingView.h"
+#import <AVFoundation/AVFoundation.h>
+
+@interface ToneTuningViewController ()<TunerDelegate>
+
+@property (nonatomic, strong) TuningNavView *navView;
+
+@property (nonatomic, assign) BOOL isRuning;
+
+@property (nonatomic, strong) Tuner *tuner;
+
+@property (nonatomic, strong) ToneTuningBodyView *bodyView;
+
+@property (nonatomic, strong) DialPlateView *plateView;
+
+@property (nonatomic, assign) BOOL isTransfer;
+
+@property (nonatomic, strong) TuningForkSettingView *forkView;
+
+@property (nonatomic, assign) NSInteger chooseFrequence;
+
+@property (nonatomic, strong) TunerForkManager *forkManager;
+
+@end
+
+@implementation ToneTuningViewController
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    // Do any additional setup after loading the view.
+    self.ks_prefersNavigationBarHidden = YES;
+    self.chooseFrequence = 440;
+    [self configUI];
+    [self setupAudioSession];
+}
+
+- (void)setupAudioSession {
+    
+    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
+    NSError *err = nil;
+    AVAudioSessionCategory category = AVAudioSessionCategoryPlayAndRecord;
+    if (@available(iOS 10.0, *)) {
+        [audioSession setCategory:category mode:AVAudioSessionModeDefault options:AVAudioSessionCategoryOptionAllowBluetooth|AVAudioSessionCategoryOptionAllowBluetoothA2DP|AVAudioSessionCategoryOptionDefaultToSpeaker|AVAudioSessionCategoryOptionMixWithOthers error:&err];
+    }
+    else {
+        [audioSession setCategory:category withOptions:AVAudioSessionCategoryOptionAllowBluetooth|AVAudioSessionCategoryOptionDefaultToSpeaker|AVAudioSessionCategoryOptionMixWithOthers error:&err];
+    }
+    [audioSession setActive:YES error:&err];
+}
+
+- (void)configUI {
+    [self.scrollView removeFromSuperview];
+    CAGradientLayer *layer = [self createGradientLayerFromColor:HexRGB(0x1D2027) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0x17181C) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, KPortraitWidth, KPortraitHeight)];
+    layer.cornerRadius = 14.0f;
+    layer.masksToBounds = YES;
+    [self.view.layer addSublayer:layer];
+    
+    [self.view addSubview:self.navView];
+    [self.navView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.top.mas_equalTo(self.view);
+        make.height.mas_equalTo(kNaviBarHeight);
+    }];
+
+    [self.view addSubview:self.bodyView];
+    self.bodyView.A4Frequence = self.chooseFrequence;
+    [self.bodyView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.mas_equalTo(self.view);
+        make.top.mas_equalTo(self.navView.mas_bottom);
+        make.bottom.mas_equalTo(self.view.mas_bottom).offset(-iPhoneXSafeBottomMargin);
+    }];
+    [self.bodyView.plateView addSubview:self.plateView];
+    [self.plateView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.top.bottom.left.right.mas_equalTo(self.bodyView.plateView);
+    }];
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [self startTuner];
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+    [super viewWillDisappear:animated];
+    [self stopTuner];
+    [self stopForkPlay];
+}
+
+- (void)startTuner {
+    if (self.isRuning == NO) {
+        self.isRuning = YES;
+        [self.tuner start];
+    }
+}
+
+- (void)stopTuner {
+    if (self.isRuning) {
+        self.isRuning = NO;
+        [self.tuner stop];
+    }
+}
+
+- (CAGradientLayer *)createGradientLayerFromColor:(UIColor *)fromColor startPoint:(CGPoint)startPoint endColor:(UIColor *)endColor endPoint:(CGPoint)endPoint bounds:(CGRect)bounds {
+    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
+    gradientLayer.colors = @[(__bridge id)fromColor.CGColor, (__bridge id)endColor.CGColor];
+    gradientLayer.startPoint = startPoint;
+    gradientLayer.endPoint = endPoint;
+    gradientLayer.frame = bounds;
+    gradientLayer.locations = @[@(0),@(1.0f)];
+    return gradientLayer;
+}
+
+- (TuningNavView *)navView {
+    if (!_navView) {
+        _navView = [TuningNavView shareInstance];
+        MJWeakSelf;
+        [_navView navActionCallback:^(BOOL isBack) {
+            if (isBack) {
+                [weakSelf backAction];
+            }
+            else {
+                [weakSelf showSettingView];
+            }
+        }];
+    }
+    return _navView;
+}
+
+- (void)showSettingView {
+    
+}
+
+- (Tuner *)tuner {
+    if (!_tuner) {
+        _tuner = [[Tuner alloc] initWithThreshold:0 smoothing:0.25];
+        _tuner.delegate = self;
+    }
+    return _tuner;
+}
+
+- (void)tunerDidUpdate:(Tuner *)tuner output:(TunerOutput *)output {
+    if (output.amplitude < 0.01) {
+        
+    }
+    else {
+//        self.plateView.gaugeView.value = output.distance;
+        [self.plateView.gaugeView setValue:output.distance animated:YES];
+        if (self.isTransfer == NO) {
+            self.bodyView.nomalPitch.text = output.pitch;
+            self.bodyView.pitchFrequenceLabel.text = [NSString stringWithFormat:@"%@%zd:%.0fHz", output.pitch, output.octave, output.frequency];
+        }
+        else {
+            
+        }
+    }
+//    NSLog(@"-------- %@%zd --- distance :%f frequence : %f" , output.pitch, output.octave, output.distance, output.frequency);
+    
+}
+
+#pragma mark ---- lazying
+- (ToneTuningBodyView *)bodyView {
+    if (!_bodyView) {
+        _bodyView = [ToneTuningBodyView shareInstance];
+        MJWeakSelf;
+        [_bodyView tuningViewAction:^(TUNINGACTION action, NSInteger frequence) {
+            [weakSelf tuningAction:action frequence:frequence];
+        }];
+    }
+    return _bodyView;
+}
+
+
+- (void)tuningAction:(TUNINGACTION)action frequence:(NSInteger)frequence {
+    switch (action) {
+        case TUNINGACTION_SETTING: // 乐器选择
+        {
+            
+        }
+            break;
+        case TUNINGACTION_FORKSETTING:  // 音叉设置
+        {
+            [self.forkView showView];
+        }
+            break;
+        case TUNINGACTION_FREQUENCE: // 调整频率
+        {
+            
+        }
+            break;
+        case TUNINGACTION_PLAY:      // 播放音叉
+        {
+            [self startForkPlay];
+        }
+            break;
+        case TUNINGACTION_STOP:      // 停止音叉
+        {
+            [self stopForkPlay];
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+- (void)startForkPlay {
+    [self.forkManager setupWithFrequence:self.chooseFrequence];
+}
+
+- (void)stopForkPlay {
+    [self.forkManager stop];
+}
+
+- (DialPlateView *)plateView {
+    if (!_plateView) {
+        _plateView = [[DialPlateView alloc] init];
+    }
+    return _plateView;
+}
+
+
+#pragma mark ------ forkSetting
+- (TuningForkSettingView *)forkView {
+    if (!_forkView) {
+        _forkView = [TuningForkSettingView shareInstance];
+        MJWeakSelf;
+        [_forkView forkSettingAction:^(NSInteger frequence) {
+            NSLog(@"----- frequence %zd", frequence);
+            weakSelf.chooseFrequence = frequence;
+            if (weakSelf.forkManager.isPlay) {
+                [weakSelf.forkManager changeFrequenceWithFrequence:frequence];
+            }
+        }];
+    }
+    return _forkView;
+}
+
+- (TunerForkManager *)forkManager {
+    if (!_forkManager) {
+        _forkManager = [[TunerForkManager alloc] init];
+    }
+    return _forkManager;
+}
+
+/*
+#pragma mark - Navigation
+
+// In a storyboard-based application, you will often want to do a little preparation before navigation
+- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
+    // Get the new view controller using [segue destinationViewController].
+    // Pass the selected object to the new view controller.
+}
+*/
+
+@end

+ 16 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/WidgetViewController.h

@@ -0,0 +1,16 @@
+//
+//  WidgetViewController.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/13.
+//
+
+#import "KSBaseViewController.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface WidgetViewController : KSBaseViewController
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 353 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Controller/WidgetViewController.m

@@ -0,0 +1,353 @@
+//
+//  WidgetViewController.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/13.
+//
+
+#import "WidgetViewController.h"
+#import "WidgetNavView.h"
+#import "WidgetDotView.h"
+#import "WidgetSpeedView.h"
+#import "WidgetFunctionView.h"
+#import "KSChoosePicker.h"
+#import "WidgetBottomButtonView.h"
+#import <AVFoundation/AVFoundation.h>
+#import "KSMetronomePlayer.h"
+
+@interface WidgetViewController ()<MetronomeControlViewDelegate,MetronomeFunctionDelegate,KSMetronomePlayerDelegate>
+
+@property (nonatomic, strong) WidgetNavView *navView;
+
+@property (nonatomic, strong) WidgetDotView *dotView;
+
+@property (nonatomic, strong) WidgetSpeedView *speedView;
+
+@property (nonatomic, strong) WidgetFunctionView *functionView;
+
+@property (nonatomic, assign) int speed;
+
+@property (nonatomic, assign) KSWidgeMetronomeType metronomeType;
+
+@property (nonatomic, strong) KSChoosePicker *pickerView;
+
+@property (nonatomic, strong) WidgetBottomButtonView *bottomView;
+
+/** 定时器 */
+@property (nonatomic, strong) NSTimer *timer;
+
+/** 定时器多少秒循环一次 */
+@property (nonatomic, assign) double timerLength;
+
+@property (nonatomic, assign) int currentNo;
+
+@property (nonatomic, assign) NSInteger totalNoCount;
+
+@property (nonatomic, assign) float playVolume;
+
+@property (nonatomic, strong) KSMetronomePlayer *nodePlayer;
+
+@end
+
+@implementation WidgetViewController
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    // Do any additional setup after loading the view.
+    self.ks_prefersNavigationBarHidden = YES;
+    [self configUI];
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [self setDefaultConfig];
+}
+
+- (void)backAction {
+    [self removeAll];
+    [self.navigationController popViewControllerAnimated:YES];
+}
+
+- (void)setDefaultConfig {
+    self.speed = 90;
+    self.playVolume = 1.0f;
+    [self updateSpeedUI];
+    self.metronomeType = KSWidgeMetronomeType4V4;
+    [self changeBeat];
+    self.functionView.volumeRate = 1.0f;
+}
+
+
+- (void)configUI {
+    [self.scrollView removeFromSuperview];
+    self.view.backgroundColor = HexRGB(0xECF9F7);
+    
+    [self.view addSubview:self.navView];
+    [self.navView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.top.mas_equalTo(self.view);
+        make.height.mas_equalTo(kNaviBarHeight);
+    }];
+    
+    [self.view addSubview:self.dotView];
+    [self.dotView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.mas_equalTo(self.view);
+        make.top.mas_equalTo(self.navView.mas_bottom).offset(10);
+        make.height.mas_equalTo(44);
+    }];
+    
+    [self.view addSubview:self.speedView];
+    [self.speedView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.mas_equalTo(self.view);
+        make.top.mas_equalTo(self.dotView.mas_bottom);
+        make.height.mas_equalTo(300);
+    }];
+    
+    [self.view addSubview:self.functionView];
+    [self.functionView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.mas_equalTo(self.view);
+        make.top.mas_equalTo(self.speedView.mas_bottom);
+        make.height.mas_equalTo(115);
+    }];
+    
+    [self.view addSubview:self.bottomView];
+    [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.mas_equalTo(self.view);
+        make.top.mas_equalTo(self.functionView.mas_bottom).offset(10);
+        make.height.mas_equalTo(60);
+    }];
+}
+
+
+#pragma mark --- delegate
+- (void)volumeChange:(float)volume {
+    self.playVolume = volume;
+    self.nodePlayer.volume = volume;
+}
+
+- (void)clickChangeBeat {
+    // 显示节拍选择弹窗
+    MJWeakSelf;
+    self.pickerView = [[KSChoosePicker alloc] initWithTitle:@"节拍" sourceData:@[@"1/2",@"2/2",@"1/4",@"2/4",@"3/4",@"4/4",@"3/8",@"6/8",@"8/16"] chooseReturnWithBlock:^(NSString * _Nonnull returnValue, NSInteger chooseIndex) {
+        KSWidgeMetronomeType type = chooseIndex;
+        weakSelf.metronomeType = type;
+        [weakSelf changeBeat];
+        
+    } cancel:^{
+        
+    }];
+    [self.pickerView showPicker];
+}
+
+- (void)clickChangeSpeed:(int)speed {
+    self.speed = speed;
+    // 更新UI
+    [self updateSpeedUI];
+}
+
+- (void)changeSpeedWithIsAdd:(BOOL)isAdd speed:(int)speed {
+    if (isAdd) {
+        self.speed = MIN(self.speed +speed, 200);
+    }else{
+        self.speed = MAX(self.speed -speed, 50);
+    }
+    
+    // 更新UI
+    [self updateSpeedUI];
+}
+
+- (void)updateSpeedUI {
+    self.speedView.speed = self.speed;
+    self.functionView.speed = self.speed;
+    [self stopBeat];
+}
+
+#pragma mark --- lazying
+- (WidgetNavView *)navView {
+    if (!_navView) {
+        _navView = [WidgetNavView shareInstance];
+        MJWeakSelf;
+        [_navView navBackAction:^{
+            [weakSelf backAction];
+        }];
+    }
+    return _navView;
+}
+
+- (WidgetDotView *)dotView {
+    if (!_dotView) {
+        _dotView = [WidgetDotView shareInstance];
+    }
+    return _dotView;
+}
+
+- (WidgetSpeedView *)speedView {
+    if (!_speedView) {
+        _speedView = [WidgetSpeedView shareInstance];
+        _speedView.delegate = self;
+    }
+    return _speedView;
+}
+
+- (WidgetFunctionView *)functionView {
+    if (!_functionView) {
+        _functionView = [WidgetFunctionView shareInstance];
+        _functionView.delegate = self;
+    }
+    return _functionView;
+}
+
+- (WidgetBottomButtonView *)bottomView {
+    if (!_bottomView) {
+        _bottomView = [WidgetBottomButtonView shareInstance];
+        MJWeakSelf;
+        [_bottomView metronomePlayCallback:^(BOOL isPlay) {
+            if (isPlay) {
+                [weakSelf playBeat];
+            }
+            else {
+                [weakSelf stopBeat];
+            }
+        }];
+    }
+    return _bottomView;
+}
+
+- (void)playBeat {
+    // 重置
+    self.currentNo = -1;
+    int bpm = self.speed * [self getRate];
+    [self.nodePlayer playAction:bpm metronomeTyle:self.metronomeType];
+  // 开始播放
+    [self.timer setFireDate:[NSDate distantPast]];
+}
+
+- (void)stopBeat {
+    [self.timer setFireDate:[NSDate distantFuture]];//暂停计时器
+    self.currentNo = -1;
+    self.bottomView.isPlay = NO;
+    [self.nodePlayer stopPlay];
+}
+
+
+- (void)changeBeat {
+    [self.dotView updateSpotView:self.metronomeType];
+    self.functionView.currentMetronomeType = self.metronomeType;
+    self.speedView.currentType = self.metronomeType;
+    self.timerLength = 60.0 / (1.0 * self.speed) / ([self getRate]);
+    [self resetTimerPlay];
+    [self stopBeat];
+}
+
+
+- (void)setSpeed:(int)speed {
+    _speed = speed;
+    int rateFloat = speed;
+    self.timerLength = 60.0 / (1.0 * rateFloat) / ([self getRate]);
+    NSLog(@"---- %f",self.self.timerLength);
+    [self stopBeat];
+    [self resetTimerPlay];
+}
+
+- (CGFloat)getRate {
+    switch (self.metronomeType) {
+        case KSWidgeMetronomeType1V2:
+        case KSWidgeMetronomeType2V2:
+            return 0.5f;
+        case KSWidgeMetronomeType1V4:
+        case KSWidgeMetronomeType2V4:
+        case KSWidgeMetronomeType3V4:
+        case KSWidgeMetronomeType4V4:
+            return 1.0f;
+        case KSWidgeMetronomeType3V8:
+        case KSWidgeMetronomeType6V8:
+            return 2.0f;
+        case KSWidgeMetronomeType8V16:
+            return 4.0f;
+        default:
+            return 1.0f;
+    }
+}
+
+
+- (KSMetronomePlayer *)nodePlayer {
+    if (!_nodePlayer) {
+        _nodePlayer = [[KSMetronomePlayer alloc] init];
+        _nodePlayer.delegate = self;
+    }
+    return _nodePlayer;
+}
+
+#pragma mark ---- delegate
+- (void)metronomePlayInterruption {
+    if (self.bottomView.isPlay) {
+        [self stopBeat];
+    }
+}
+
+- (void)resetTimerPlay {
+    if (_timer) {
+        [self resetTimer];
+        MJWeakSelf;
+        _timer = [NSTimer scheduledTimerWithTimeInterval:self.timerLength repeats:YES block:^(NSTimer * _Nonnull timer) {
+            [weakSelf timerAction];
+        }];
+        [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
+        [_timer setFireDate:[NSDate distantFuture]];
+    }
+}
+
+- (NSTimer *)timer{
+    
+    if (!_timer) {
+        MJWeakSelf;
+        _timer = [NSTimer scheduledTimerWithTimeInterval:self.timerLength repeats:YES block:^(NSTimer * _Nonnull timer) {
+            [weakSelf timerAction];
+        }];
+        [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
+        [_timer setFireDate:[NSDate distantFuture]];
+    }
+    return _timer;
+}
+
+- (void)timerAction {
+    [self updateStep];
+    
+}
+
+- (void)updateStep {
+    
+    NSLog(@"---- current no ------ %d ", self.currentNo);
+    self.currentNo++;
+    [self.dotView updateSpotViewHeightState:self.currentNo];
+}
+
+
+#pragma mark -- 重置定时器
+- (void)resetTimer{
+    [self.timer setFireDate:[NSDate distantPast]];
+    [_timer invalidate];
+    _timer = nil;
+}
+
+- (void)removeAll{
+    
+    if (_timer) {
+        [_timer invalidate];
+        _timer = nil;
+    }
+}
+- (void)dealloc {
+    NSLog(@"----dealloc ");
+    [self removeAll];
+}
+/*
+#pragma mark - Navigation
+
+// In a storyboard-based application, you will often want to do a little preparation before navigation
+- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
+    // Get the new view controller using [segue destinationViewController].
+    // Pass the selected object to the new view controller.
+}
+*/
+
+@end

+ 35 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Model/KSMetronomePlayer.h

@@ -0,0 +1,35 @@
+//
+//  KSMetronomePlayer.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/20.
+//
+
+#import <Foundation/Foundation.h>
+#import "WidgetDotView.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol KSMetronomePlayerDelegate <NSObject>
+
+- (void)metronomePlayInterruption;
+
+@optional;
+- (void)resumeAudioSession;
+
+@end
+
+@interface KSMetronomePlayer : NSObject
+
+@property (nonatomic, assign) id <KSMetronomePlayerDelegate> delegate;
+@property(nonatomic, assign) BOOL isPlaying;
+
+@property (nonatomic, assign) float volume;
+
+- (void)playAction:(int)bpm metronomeTyle:(KSWidgeMetronomeType)type;
+
+- (void)stopPlay;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 249 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Model/KSMetronomePlayer.m

@@ -0,0 +1,249 @@
+//
+//  KSMetronomePlayer.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/20.
+//
+
+#import "KSMetronomePlayer.h"
+#import <AVFoundation/AVFoundation.h>
+
+@interface KSMetronomePlayer ()
+
+@property (nonatomic, strong) AVAudioPlayerNode *playerNode;
+
+@property (nonatomic, strong) AVAudioEngine *audioEngine;
+
+@property (nonatomic, strong) AVAudioFile *audioDIFile;
+
+@property (nonatomic, strong) AVAudioFile *audioDongFile;
+
+
+@property (nonatomic, strong) AVAudioPCMBuffer *dingBuffer;
+
+@property (nonatomic, strong) AVAudioPCMBuffer *dongBuffer;
+
+@property (nonatomic, assign) NSInteger playIndex;
+
+@end
+
+@implementation KSMetronomePlayer
+
+- (instancetype)init {
+    self = [super init];
+    if (self) {
+        [self configEngine];
+    }
+    return self;
+}
+
+- (void)setupAudioSession {
+    
+    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
+    NSError *err = nil;
+    AVAudioSessionCategory category = AVAudioSessionCategoryPlayAndRecord;
+    if (@available(iOS 10.0, *)) {
+        [audioSession setCategory:category mode:AVAudioSessionModeDefault options:AVAudioSessionCategoryOptionAllowBluetooth|AVAudioSessionCategoryOptionAllowBluetoothA2DP|AVAudioSessionCategoryOptionDefaultToSpeaker|AVAudioSessionCategoryOptionMixWithOthers error:&err];
+    }
+    else {
+        [audioSession setCategory:category withOptions:AVAudioSessionCategoryOptionAllowBluetooth|AVAudioSessionCategoryOptionDefaultToSpeaker|AVAudioSessionCategoryOptionMixWithOthers error:&err];
+    }
+    [audioSession setActive:YES error:&err];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:audioSession];
+}
+
+// 打断处理
+- (void)handleInterruption:(NSNotification *)notification {
+    
+    NSDictionary *info = notification.userInfo;
+    AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
+    if (type == AVAudioSessionInterruptionTypeBegan) {
+        //Handle InterruptionBegan
+        if (self.delegate && [self.delegate respondsToSelector:@selector(metronomePlayInterruption)]) {
+            [self.delegate metronomePlayInterruption];
+        }
+    }else{
+        AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
+        if (options == AVAudioSessionInterruptionOptionShouldResume) {
+            //Handle Resume
+            if (self.delegate && [self.delegate respondsToSelector:@selector(resumeAudioSession)]) {
+                [self.delegate resumeAudioSession];
+            }
+        }
+    }
+}
+
+- (void)configEngine {
+    [self setupAudioSession];
+    NSString *tickPath = [[NSBundle mainBundle]pathForResource:@"tick" ofType:@"wav"];
+    NSURL *tickUrl = [NSURL fileURLWithPath:tickPath];
+    self.audioDIFile = [[AVAudioFile alloc] initForReading:tickUrl error:nil];
+    
+    NSString *tockPath = [[NSBundle mainBundle]pathForResource:@"tock" ofType:@"wav"];
+    NSURL *tockUrl = [NSURL fileURLWithPath:tockPath];
+    self.audioDongFile = [[AVAudioFile alloc] initForReading:tockUrl error:nil];
+    self.playerNode = [[AVAudioPlayerNode alloc] init];
+    self.audioEngine = [[AVAudioEngine alloc] init];
+
+    [self.audioEngine attachNode:self.playerNode];
+    [self.audioEngine connect:self.playerNode to:self.audioEngine.mainMixerNode format:self.audioDIFile.processingFormat];
+    
+    [self.audioEngine startAndReturnError:nil];
+    self.audioEngine.autoShutdownEnabled = YES;
+}
+
+
+- (CGFloat)getRate:(KSWidgeMetronomeType)metronomeType {
+    switch (metronomeType) {
+        case KSWidgeMetronomeType1V2:
+        case KSWidgeMetronomeType2V2:
+            return 0.5f;
+        case KSWidgeMetronomeType1V4:
+        case KSWidgeMetronomeType2V4:
+        case KSWidgeMetronomeType3V4:
+        case KSWidgeMetronomeType4V4:
+            return 1.0f;
+        case KSWidgeMetronomeType3V8:
+        case KSWidgeMetronomeType6V8:
+            return 2.0f;
+        case KSWidgeMetronomeType8V16:
+            return 4.0f;
+        default:
+            return 1.0f;
+    }
+}
+
+- (AVAudioPCMBuffer *)generateBuffer:(int)bmp metronomeType:(KSWidgeMetronomeType)type {
+    self.audioDIFile.framePosition = 0;
+    self.audioDongFile.framePosition = 0;
+    
+    AVAudioFrameCount beatLength = self.audioDIFile.processingFormat.sampleRate * 60 / bmp;
+    
+    AVAudioPCMBuffer *dingBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:self.audioDIFile.processingFormat frameCapacity:beatLength];
+    [self.audioDIFile readIntoBuffer:dingBuffer error:nil];
+    dingBuffer.frameLength = beatLength;
+    
+    AVAudioPCMBuffer *dongBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:self.audioDIFile.processingFormat frameCapacity:beatLength];
+    [self.audioDongFile readIntoBuffer:dongBuffer error:nil];
+    dongBuffer.frameLength = beatLength;
+    
+    int totalCount = [self getTotalCountFrame:type];
+    AVAudioPCMBuffer *bufferBar = [[AVAudioPCMBuffer alloc] initWithPCMFormat:self.audioDIFile.processingFormat frameCapacity:totalCount * beatLength];
+    bufferBar.frameLength = totalCount * beatLength;
+    
+    int channerCount = (int)self.audioDIFile.processingFormat.channelCount;
+    
+    NSMutableArray *dingClickArray = [NSMutableArray array];
+    NSMutableArray *dongClickArray = [NSMutableArray array];
+    
+    for (int i = 0 ; i < channerCount *(int)beatLength; i++) {
+        double temp = *(dongBuffer.floatChannelData[0]+i);
+        double tempMain = *(dingBuffer.floatChannelData[0]+i);
+        [dingClickArray addObject:@(tempMain)];
+        [dongClickArray addObject:@(temp)];
+    }
+    
+    NSMutableArray *barArray = [NSMutableArray array];
+    
+    [barArray addObjectsFromArray:dingClickArray];
+    NSInteger dongCount = [self getDongCount:type];
+    for (int i = 0; i < dongCount; i++) {
+        [barArray addObjectsFromArray:dongClickArray];
+    }
+    for (NSInteger i = 0; i < channerCount * bufferBar.frameLength; i++) {
+        double temp = 0;
+        if (i < barArray.count) {
+            temp = [barArray[i] doubleValue];
+        }
+        *(bufferBar.floatChannelData[0]+i) = temp;
+    }
+
+    return bufferBar;
+}
+
+- (int)getTotalCountFrame:(KSWidgeMetronomeType)type {
+    switch (type) {
+        case KSWidgeMetronomeType1V2:
+            return 1;
+        case KSWidgeMetronomeType2V2:
+            return 2;
+        case KSWidgeMetronomeType1V4:
+            return 1;
+        case KSWidgeMetronomeType2V4:
+            return 2;
+        case KSWidgeMetronomeType3V4:
+            return 3;
+        case KSWidgeMetronomeType4V4:
+            return 4;
+        case KSWidgeMetronomeType3V8:
+            return 3;
+        case KSWidgeMetronomeType6V8:
+            return 6;
+        case KSWidgeMetronomeType8V16:
+            return 8;
+        default:
+            return 4;
+            break;
+    }
+}
+
+- (int)getDongCount:(KSWidgeMetronomeType)type {
+    if (type == KSWidgeMetronomeType1V2) {
+        return 0;
+    }
+    else if (type == KSWidgeMetronomeType2V2) {
+        return 1;
+    }
+    else if (type == KSWidgeMetronomeType1V4) {
+        return 0;
+    }
+    else if (type == KSWidgeMetronomeType2V4) {
+        return 1;
+    }
+    else if (type == KSWidgeMetronomeType3V4) {
+        return 2;
+    }
+    else if (type == KSWidgeMetronomeType4V4) {
+        return 3;
+    }
+    else if (type == KSWidgeMetronomeType3V8) {
+        return 2;
+    }
+    else if (type == KSWidgeMetronomeType6V8) {
+        return 5;
+    }
+    else {
+        return 7;
+    }
+}
+
+- (void)playAction:(int)bpm metronomeTyle:(KSWidgeMetronomeType)type {
+    if (self.audioEngine.isRunning == NO) {
+        [self.audioEngine startAndReturnError:nil];
+    }
+    AVAudioPCMBuffer *buffer = [self generateBuffer:bpm metronomeType:type];
+    if (self.playerNode.isPlaying) {
+        [self.playerNode stop];
+    }
+    [self.playerNode play];
+    
+    [self.playerNode scheduleBuffer:buffer atTime:nil options:AVAudioPlayerNodeBufferLoops completionHandler:nil];
+}
+
+- (void)stopPlay {
+    [self.playerNode stop];
+}
+
+- (BOOL)isPlaying {
+    return self.playerNode.isPlaying;
+}
+
+- (void)setVolume:(float)volume {
+    self.playerNode.volume = volume;
+}
+
+- (void)dealloc {
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+@end

+ 4 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Model/TuningFunction/KulexiuForTeacher-Bridging-Header.h

@@ -0,0 +1,4 @@
+//
+//  Use this file to import your target's public headers that you would like to expose to Swift.
+//
+

+ 213 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Model/TuningFunction/Tuner.swift

@@ -0,0 +1,213 @@
+//
+//  Tuner.swift
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+import Foundation
+import AudioKit
+
+
+private let flats = ["C", "D♭","D","E♭","E","F","G♭","G","A♭","A","B♭","B"]
+private let sharps = ["C", "C♯","D","D♯","E","F","F♯","G","G♯","A","A♯","B"]
+private let frequencies: [Float] = [
+    16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96, 27.50, 29.14, 30.87, // 0
+    32.70, 34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 49.00, 51.91, 55.00, 58.27, 61.74, // 1
+    65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.8, 110.0, 116.5, 123.5, // 2
+    130.8, 138.6, 146.8, 155.6, 164.8, 174.6, 185.0, 196.0, 207.7, 220.0, 233.1, 246.9, // 3
+    261.6, 277.2, 293.7, 311.1, 329.6, 349.2, 370.0, 392.0, 415.3, 440.0, 466.2, 493.9, // 4
+    523.3, 554.4, 587.3, 622.3, 659.3, 698.5, 740.0, 784.0, 830.6, 880.0, 932.3, 987.8, // 5
+    1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976,             // 6
+    2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,             // 7
+    4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902              // 8
+]
+
+/**
+ Types adopting the TunerDelegate protocol act as callbacks for Tuners and are
+ the mechanism by which you may receive and respond to new information decoded
+ by a Tuner.
+ */
+@objc public protocol TunerDelegate {
+    
+    /**
+     Called by a Tuner on each update.
+     
+     - parameter tuner: Tuner that performed the update.
+     - parameter output: Contains information decoded by the Tuner.
+     */
+    func tunerDidUpdate(_ tuner: Tuner, output: TunerOutput)
+}
+
+// MARK:- TunerOutput
+/**
+ Contains information decoded by a Tuner, such as frequency, octave, pitch, etc.
+ */
+@objc public class TunerOutput: NSObject {
+    
+    /**
+     The octave of the interpreted pitch.
+     */
+    @objc public fileprivate(set) var octave: Int = 0
+    
+    /**
+     The interpreted pitch of the microphone audio.
+     */
+    @objc public fileprivate(set) var pitch: String = ""
+    
+    /**
+     The difference between the frequency of the interpreted pitch and the actual
+     frequency of the microphone audio.
+     
+     For example if the microphone audio has a frequency of 432Hz, the pitch will
+     be interpreted as A4 (440Hz), thus making the distance -8Hz.
+     */
+    @objc public fileprivate(set) var distance: Float = 0.0
+    
+    /**
+     The amplitude of the microphone audio.
+     */
+    @objc public fileprivate(set) var amplitude: Float = 0.0
+    
+    /**
+     The frequency of the microphone audio.
+     */
+    @objc public fileprivate(set) var frequency: Float = 0.0
+    
+    fileprivate override init() {}
+}
+
+
+/**
+ A Tuner uses the devices microphone and interprets the frequency, pitch, etc.
+ */
+@objc public class Tuner: NSObject {
+    
+    fileprivate let smoothingBufferCount = 30
+    
+    fileprivate let threshold: Float
+    fileprivate let smoothing: Float
+    fileprivate var engine: AudioEngine?
+    fileprivate var microphone: AudioEngine.InputNode?
+    fileprivate var pitchTap: PitchTap?
+    fileprivate var silence: Fader?
+    fileprivate var smoothingBuffer: [Float] = []
+    
+    /**
+     Object adopting the TunerDelegate protocol that should receive callbacks
+     from this tuner.
+     */
+    @objc public var delegate: TunerDelegate?
+    
+    /**
+     Initializes a new Tuner.
+     
+     - parameter threshold: The minimum amplitude to recognize, 0 < threshold < 1
+     - parameter smoothing: Exponential smoothing factor, 0 < smoothing < 1
+     
+     */
+    @objc public init(threshold: Float = 0.0, smoothing: Float = 0.25) {
+        self.threshold = Float(min(abs(threshold), 1.0))
+        self.smoothing = Float(min(abs(smoothing), 1.0))
+    }
+    
+    /**
+     Starts the tuner.
+     */
+    @objc public func start() {
+        engine = AudioEngine()
+        microphone = engine!.input
+        silence = Fader(microphone!, gain: 0)
+        /**
+         调大bufferSize ,可以降低采样频率
+         16384 (10次/秒)
+         8192 (20次/秒)
+         4096 (40次/秒)  def
+         */
+        pitchTap = PitchTap(microphone!, bufferSize: 4096, handler: tap_handler)
+        microphone!.start()
+        pitchTap!.start()
+        engine!.output = silence
+        try? engine!.start()
+        
+    }
+    
+    /**
+     Stops the tuner.
+     */
+    @objc public func stop() {
+        microphone!.stop()
+        pitchTap!.stop()
+        engine!.stop()
+    }
+    
+    func tap_handler(freq: [Float], amp: [Float]) -> Void {
+#if DEBUG
+        print("freq -- real_\(freq[0]) -- imag_\(freq[1])" )
+        print("amp -- real_\(amp[0]) -- imag_\(amp[1])")
+#endif
+        if let d = self.delegate {
+            if amp[0] > self.threshold
+            {
+                let amplitude = amp[0]
+                let frequency = freq[0]
+                let output = Tuner.newOutput(frequency, amplitude)
+                DispatchQueue.main.async {
+                    d.tunerDidUpdate(self, output: output)
+                }
+            }
+        }
+    }
+    
+    /**
+     Exponential smoothing:
+     指数平滑算法
+     smoothing 平滑常数
+     https://en.wikipedia.org/wiki/Exponential_smoothing
+     */
+    fileprivate func smooth(_ value: Float) -> Float {
+        var frequency = value
+        if smoothingBuffer.count > 0 {
+            let last = smoothingBuffer.last!
+            frequency = (smoothing * value) + (1.0 - smoothing) * last
+            if smoothingBuffer.count > smoothingBufferCount {
+                smoothingBuffer.removeFirst()
+            }
+        }
+        smoothingBuffer.append(frequency)
+        return frequency
+    }
+    
+    /**
+     Transfer frequency and amplitude to TunerOutput
+    */
+    static func newOutput(_ frequency: Float, _ amplitude: Float) -> TunerOutput {
+        let output = TunerOutput()
+        
+        var norm = frequency
+        while norm > frequencies[frequencies.count - 1] {
+            norm = norm / 2.0
+        }
+        while norm < frequencies[0] {
+            norm = norm * 2.0
+        }
+        
+        var i = -1
+        var min = Float.infinity
+        for n in 0...frequencies.count-1 {
+            let diff = frequencies[n] - norm
+            if abs(diff) < abs(min) {
+                min = diff
+                i = n
+            }
+        }
+        
+        output.octave = i / 12
+        output.frequency = frequency
+        output.amplitude = amplitude
+        output.distance = frequency - frequencies[i]
+        output.pitch = String(format: "%@", sharps[i % sharps.count], flats[i % flats.count])
+        
+        return output
+    }
+}

+ 48 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/Model/TuningFunction/TunerForkManager.swift

@@ -0,0 +1,48 @@
+//
+//  TunerForkManager.swift
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/20.
+//
+
+import Foundation
+import AudioKit
+
+@objc public class TunerForkManager : NSObject {
+    
+    let engine = AudioEngine()
+    let osc = Oscillator();
+    @objc public var isPlay = false
+    
+    @objc public override init() {
+        super.init()
+    }
+    
+    @objc public func setup(frequence: Float) {
+        osc.amplitude = 1.0
+        engine.output = osc
+        do {
+            try engine.start()
+        } catch {
+            print("Couldn't start AudioEngine.")
+        }
+        if osc.isPlaying {
+            osc.stop()
+        }
+        osc.frequency = frequence
+        osc.start()
+        self.isPlay = true
+    }
+    
+    @objc public func changeFrequence (frequence : Float) {
+        osc.frequency = frequence
+    }
+    
+    @objc public func stop() {
+        if osc.isStarted {
+            osc.stop()
+            self.isPlay = false
+        }
+    }
+    
+}

+ 24 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetBottomButtonView.h

@@ -0,0 +1,24 @@
+//
+//  WidgetBottomButtonView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/14.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef void(^WidgetPlayAction)(BOOL isPlay);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface WidgetBottomButtonView : UIView
+
+@property (nonatomic, assign) BOOL isPlay;
+
++ (instancetype)shareInstance;
+
+- (void)metronomePlayCallback:(WidgetPlayAction)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 57 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetBottomButtonView.m

@@ -0,0 +1,57 @@
+//
+//  WidgetBottomButtonView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/14.
+//
+
+#import "WidgetBottomButtonView.h"
+
+@interface WidgetBottomButtonView ()
+
+@property (nonatomic, copy) WidgetPlayAction callback;
+@property (weak, nonatomic) IBOutlet UIButton *actionButton;
+
+@end
+
+@implementation WidgetBottomButtonView
+
+
++ (instancetype)shareInstance {
+    WidgetBottomButtonView *view = [[[NSBundle mainBundle] loadNibNamed:@"WidgetBottomButtonView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)metronomePlayCallback:(WidgetPlayAction)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+
+
+- (IBAction)playAction:(id)sender {
+    self.isPlay = !self.isPlay;
+    if (self.callback) {
+        self.callback(self.isPlay);
+    }
+}
+
+- (void)setIsPlay:(BOOL)isPlay {
+    _isPlay = isPlay;
+    if (isPlay) {
+        [self.actionButton setTitle:@"暂停" forState:UIControlStateNormal];
+    }
+    else {
+        [self.actionButton setTitle:@"播放" forState:UIControlStateNormal];
+    }
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 62 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetBottomButtonView.xib

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="WidgetBottomButtonView">
+            <rect key="frame" x="0.0" y="0.0" width="414" height="75"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dUf-b4-rpL">
+                    <rect key="frame" x="69" y="10" width="276" height="45"/>
+                    <color key="backgroundColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="276" id="IAr-m9-HBE"/>
+                        <constraint firstAttribute="height" constant="45" id="neJ-fh-cFC"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" type="system" pointSize="16"/>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" title="播放"/>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="22.5"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="size" keyPath="shadowOffset">
+                            <size key="value" width="0.0" height="6"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowOpacity">
+                            <real key="value" value="1"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowRadius">
+                            <real key="value" value="12"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="color" keyPath="shadowUIColor">
+                            <color key="value" red="1" green="1" blue="1" alpha="0.71999999999999997" colorSpace="custom" customColorSpace="sRGB"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                    <connections>
+                        <action selector="playAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="VaJ-tx-TKv"/>
+                    </connections>
+                </button>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="dUf-b4-rpL" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="10" id="YIX-HD-67L"/>
+                <constraint firstItem="dUf-b4-rpL" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="kjy-H9-oV0"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="actionButton" destination="dUf-b4-rpL" id="RBy-oS-v2K"/>
+            </connections>
+            <point key="canvasLocation" x="131.8840579710145" y="-159.04017857142856"/>
+        </view>
+    </objects>
+</document>

+ 44 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetDotView.h

@@ -0,0 +1,44 @@
+//
+//  WidgetDotView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/13.
+//
+
+#import <UIKit/UIKit.h>
+
+// 节拍器节拍选择
+typedef NS_ENUM(NSInteger, KSWidgeMetronomeType) {
+    KSWidgeMetronomeType1V2 = 0,    // 1/2
+    KSWidgeMetronomeType2V2,        // 2/2
+    KSWidgeMetronomeType1V4,        // 1/4
+    KSWidgeMetronomeType2V4,        // 2/4
+    KSWidgeMetronomeType3V4,        // 3/4
+    KSWidgeMetronomeType4V4,        // 4/4
+    KSWidgeMetronomeType3V8,        // 3/8
+    KSWidgeMetronomeType6V8,        // 6/8
+    KSWidgeMetronomeType8V16,       // 8/16
+ };
+
+
+
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface WidgetDotView : UIView
+
++ (instancetype)shareInstance;
+
+/** 更新当前点View(显示几个点) */
+- (void)updateSpotView:(KSWidgeMetronomeType)metronomeType;
+
+/**
+ 更新某个点闪烁
+
+ @param currentTotalNo 可以被currentTotalNo余0的闪烁一下
+ */
+- (void)updateSpotViewHeightState:(int)currentTotalNo;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 383 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetDotView.m

@@ -0,0 +1,383 @@
+//
+//  WidgetDotView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/13.
+//
+
+#import "WidgetDotView.h"
+
+#define SPOT_WHDTH (21)
+
+@interface WidgetDotView ()
+
+@property (nonatomic, assign) KSWidgeMetronomeType currentMetronomeType;
+
+@end
+
+@implementation WidgetDotView
+
+
++ (instancetype)shareInstance {
+    WidgetDotView *view = [[[NSBundle mainBundle] loadNibNamed:@"WidgetDotView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+
+- (void)updateSpotViewHeightState:(int)currentTotalNo {
+    //    当前圆个数
+    int spotNo;
+    
+    if (self.currentMetronomeType == KSWidgeMetronomeType1V4 || self.currentMetronomeType == KSWidgeMetronomeType1V2) {
+        spotNo = 1;
+    }else if (self.currentMetronomeType == KSWidgeMetronomeType2V4 || self.currentMetronomeType == KSWidgeMetronomeType2V2){
+        spotNo = 2;
+    }else if (self.currentMetronomeType == KSWidgeMetronomeType3V4 || self.currentMetronomeType == KSWidgeMetronomeType3V8){
+        spotNo = 3;
+    }else if (self.currentMetronomeType == KSWidgeMetronomeType4V4){
+        spotNo = 4;
+    }else if (self.currentMetronomeType == KSWidgeMetronomeType6V8){
+        spotNo = 6;
+    }
+    else {
+        spotNo = 8;
+    }
+    
+    UIImageView *currentImageView = [self viewWithTag:currentTotalNo % spotNo +100];
+    if (currentImageView) {
+        currentImageView.highlighted = YES;
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+            currentImageView.highlighted = NO;
+        });
+    }
+}
+
+- (void)updateSpotView:(KSWidgeMetronomeType)metronomeStat {
+    
+    self.currentMetronomeType = metronomeStat;
+    [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
+
+    [self creatSpotImageView:metronomeStat];
+}
+
+- (void)creatSpotImageView:(KSWidgeMetronomeType)metronomeType {
+    CGFloat dotSpace = 8.0f;
+    if (metronomeType == KSWidgeMetronomeType1V2) {
+        UIImageView *spotView = [self creatSpotImageView];
+        spotView.tag = 100;
+        spotView.highlighted = YES;
+        [self addSubview:spotView];
+        [spotView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.centerX.mas_equalTo(self.mas_centerX);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+    }
+    else if (metronomeType == KSWidgeMetronomeType2V2) {
+        CGFloat xSpace = (KPortraitWidth - SPOT_WHDTH * 2 - dotSpace) / 2;
+        
+        UIImageView *spotView = [self creatSpotImageView];
+        spotView.tag = 100;
+        spotView.highlighted = YES;
+        [self addSubview:spotView];
+        [spotView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(self.mas_left).offset(xSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView1 = [self createWhiteImageView];
+        spotView1.tag = 101;
+        [self addSubview:spotView1];
+        [spotView1 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+    }
+    else if (metronomeType == KSWidgeMetronomeType1V4) {
+        UIImageView *spotView = [self creatSpotImageView];
+        spotView.tag = 100;
+        spotView.highlighted = YES;
+        [self addSubview:spotView];
+        [spotView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.centerX.mas_equalTo(self.mas_centerX);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+    }
+    else if (metronomeType == KSWidgeMetronomeType2V4) {
+        CGFloat xSpace = (KPortraitWidth - SPOT_WHDTH * 2 - dotSpace) / 2;
+        
+        UIImageView *spotView = [self creatSpotImageView];
+        spotView.tag = 100;
+        spotView.highlighted = YES;
+        [self addSubview:spotView];
+        [spotView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(self.mas_left).offset(xSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView1 = [self createWhiteImageView];
+        spotView1.tag = 101;
+        [self addSubview:spotView1];
+        [spotView1 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+    }
+    else if (metronomeType == KSWidgeMetronomeType3V4) {
+        CGFloat xSpace = (KPortraitWidth - SPOT_WHDTH * 3 - dotSpace * 2) / 2;
+        UIImageView *spotView = [self creatSpotImageView];
+        spotView.tag = 100;
+        spotView.highlighted = YES;
+        [self addSubview:spotView];
+        [spotView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(self.mas_left).offset(xSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView1 = [self createWhiteImageView];
+        spotView1.tag = 101;
+        [self addSubview:spotView1];
+        [spotView1 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView2 = [self createWhiteImageView];
+        spotView2.tag = 102;
+        [self addSubview:spotView2];
+        [spotView2 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView1.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+    }
+    else if (metronomeType == KSWidgeMetronomeType4V4) {
+        CGFloat xSpace = (KPortraitWidth - SPOT_WHDTH * 4 -  dotSpace * 3) / 2;
+        UIImageView *spotView = [self creatSpotImageView];
+        spotView.tag = 100;
+        spotView.highlighted = YES;
+        [self addSubview:spotView];
+        [spotView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(self.mas_left).offset(xSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView1 = [self createWhiteImageView];
+        spotView1.tag = 101;
+        [self addSubview:spotView1];
+        [spotView1 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView2 = [self createWhiteImageView];
+        spotView2.tag = 102;
+        [self addSubview:spotView2];
+        [spotView2 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView1.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView3 = [self createWhiteImageView];
+        spotView3.tag = 103;
+        [self addSubview:spotView3];
+        [spotView3 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView2.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+    }
+    else if (metronomeType == KSWidgeMetronomeType3V8) {
+        CGFloat xSpace = (KPortraitWidth - SPOT_WHDTH * 3 - dotSpace * 2) / 2;
+        UIImageView *spotView = [self creatSpotImageView];
+        spotView.highlighted = YES;
+        spotView.tag = 100;
+        [self addSubview:spotView];
+        [spotView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(self.mas_left).offset(xSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView1 = [self createWhiteImageView];
+        spotView1.tag = 101;
+        [self addSubview:spotView1];
+        [spotView1 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView2 = [self createWhiteImageView];
+        spotView2.tag = 102;
+        [self addSubview:spotView2];
+        [spotView2 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView1.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+    }
+    else if (metronomeType == KSWidgeMetronomeType6V8) {
+        CGFloat xSpace = (KPortraitWidth - SPOT_WHDTH * 6 - dotSpace * 5) / 2;
+        UIImageView *spotView = [self creatSpotImageView];
+        spotView.tag = 100;
+        spotView.highlighted = YES;
+        [self addSubview:spotView];
+        [spotView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(self.mas_left).offset(xSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView1 = [self createWhiteImageView];
+        spotView1.tag = 101;
+        [self addSubview:spotView1];
+        [spotView1 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView2 = [self createWhiteImageView];
+        spotView2.tag = 102;
+        [self addSubview:spotView2];
+        [spotView2 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView1.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView3 = [self createWhiteImageView];
+        spotView3.tag = 103;
+        [self addSubview:spotView3];
+        [spotView3 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView2.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView4 = [self createWhiteImageView];
+        spotView4.tag = 104;
+        [self addSubview:spotView4];
+        [spotView4 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView3.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView5 = [self createWhiteImageView];
+        spotView5.tag = 105;
+        [self addSubview:spotView5];
+        [spotView5 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView4.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+    }
+    else if (metronomeType == KSWidgeMetronomeType8V16) {
+        CGFloat xSpace = (KPortraitWidth - SPOT_WHDTH * 8 - dotSpace * 7) / 2;
+        UIImageView *spotView = [self creatSpotImageView];
+        spotView.tag = 100;
+        spotView.highlighted = YES;
+        [self addSubview:spotView];
+        [spotView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(self.mas_left).offset(xSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView1 = [self createWhiteImageView];
+        spotView1.tag = 101;
+        [self addSubview:spotView1];
+        [spotView1 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView2 = [self createWhiteImageView];
+        spotView2.tag = 102;
+        [self addSubview:spotView2];
+        [spotView2 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView1.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView3 = [self createWhiteImageView];
+        spotView3.tag = 103;
+        [self addSubview:spotView3];
+        [spotView3 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView2.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView4 = [self createWhiteImageView];
+        spotView4.tag = 104;
+        [self addSubview:spotView4];
+        [spotView4 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView3.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView5 = [self createWhiteImageView];
+        spotView5.tag = 105;
+        [self addSubview:spotView5];
+        [spotView5 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView4.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView6 = [self createWhiteImageView];
+        spotView6.tag = 106;
+        [self addSubview:spotView6];
+        [spotView6 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView5.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+        
+        UIImageView *spotView7 = [self createWhiteImageView];
+        spotView7.tag = 107;
+        [self addSubview:spotView7];
+        [spotView7 mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.mas_equalTo(spotView6.mas_right).offset(dotSpace);
+            make.centerY.mas_equalTo(self.mas_centerY);
+            make.width.height.mas_equalTo(SPOT_WHDTH);
+        }];
+    }
+}
+
+#pragma mark -- 创建大圆View
+- (UIImageView *)creatSpotImageView {
+    
+    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"metro_bigWhiteSpot"] highlightedImage:[UIImage imageNamed:@"metro_bigHightSpot"]];
+    return imageView;
+}
+
+- (UIImageView *)createWhiteImageView {
+    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"metro_whiteSpot"] highlightedImage:[UIImage imageNamed:@"metro_hightSpot"]];
+    return imageView;
+}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 22 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetDotView.xib

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="WidgetDotView">
+            <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="131.8840579710145" y="-3.0133928571428572"/>
+        </view>
+    </objects>
+</document>

+ 40 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetFunctionView.h

@@ -0,0 +1,40 @@
+//
+//  WidgetFunctionView.h
+//  ;ForStudent
+//
+//  Created by 王智 on 2022/9/13.
+//
+
+#import <UIKit/UIKit.h>
+#import "WidgetDotView.h"
+
+@protocol MetronomeFunctionDelegate <NSObject>
+
+/** 加减号改变频率 */
+- (void)clickChangeSpeed:(int)speed;
+
+- (void)clickChangeBeat;
+// 播放音量调整
+- (void)volumeChange:(float)volume;
+
+@end
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface WidgetFunctionView : UIView
+
+@property (nonatomic, assign) KSWidgeMetronomeType currentMetronomeType;
+
+@property (nonatomic, assign) int speed;
+
+@property (weak, nonatomic) IBOutlet UIButton *signatureButton;
+
+@property (nonatomic, assign) CGFloat volumeRate;
+
++ (instancetype)shareInstance;
+/** 代理 */
+@property (nonatomic, weak) id <MetronomeFunctionDelegate> delegate;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 139 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetFunctionView.m

@@ -0,0 +1,139 @@
+//
+//  WidgetFunctionView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/13.
+//
+
+#import "WidgetFunctionView.h"
+
+@interface WidgetFunctionView ()
+
+@property (weak, nonatomic) IBOutlet UIButton *speedMinusButton;
+
+@property (weak, nonatomic) IBOutlet UIButton *speedAddButton;
+
+
+@property (weak, nonatomic) IBOutlet UISlider *volumeSlider;
+
+
+@end
+
+@implementation WidgetFunctionView
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    [self.volumeSlider setThumbImage:[UIImage imageNamed:@"slider_image"] forState:UIControlStateNormal];
+    [self.volumeSlider setThumbImage:[UIImage imageNamed:@"slider_image"] forState:UIControlStateFocused];
+}
+
++ (instancetype)shareInstance {
+    WidgetFunctionView *view = [[[NSBundle mainBundle] loadNibNamed:@"WidgetFunctionView" owner:nil options:nil] firstObject];
+    return view;
+}
+- (IBAction)changeVolumeAction:(UISlider *)sender {
+    self.volumeRate = sender.value;
+    if (self.delegate && [self.delegate respondsToSelector:@selector(volumeChange:)]) {
+        [self.delegate volumeChange:sender.value];
+    }
+}
+
+- (IBAction)changeBeatAction:(id)sender {
+    if (self.delegate && [self.delegate respondsToSelector:@selector(clickChangeBeat)]) {
+        [self.delegate clickChangeBeat];
+    }
+}
+
+
+- (IBAction)onReduceButtonClick:(id)sender {
+    if (self.speed > 50) {
+        self.speed --;
+    }else{
+        self.speed = 50;
+    }
+    [self updateSpeed];
+}
+
+
+- (IBAction)onAddButtonClick:(id)sender {
+    if (self.speed < 200) {
+        self.speed ++;
+    }else{
+        self.speed = 200;
+    }
+    [self updateSpeed];
+}
+
+- (void)updateSpeed {
+    
+    if (self.delegate && [self.delegate respondsToSelector:@selector(clickChangeSpeed:)]) {
+        [self.delegate clickChangeSpeed:self.speed];
+    }
+}
+
+- (void)setCurrentMetronomeType:(KSWidgeMetronomeType)currentMetronomeType {
+    _currentMetronomeType = currentMetronomeType;
+    switch (currentMetronomeType) {
+        case KSWidgeMetronomeType1V2:
+        {
+            [self.signatureButton setTitle:@"1/2" forState:UIControlStateNormal];
+        }
+            break;
+        case KSWidgeMetronomeType2V2:
+        {
+            [self.signatureButton setTitle:@"2/2" forState:UIControlStateNormal];
+        }
+            break;
+        case KSWidgeMetronomeType1V4:
+        {
+            [self.signatureButton setTitle:@"1/4" forState:UIControlStateNormal];
+        }
+            break;
+        case KSWidgeMetronomeType2V4:
+        {
+            [self.signatureButton setTitle:@"2/4" forState:UIControlStateNormal];
+        }
+            break;
+        case KSWidgeMetronomeType3V4:
+        {
+            [self.signatureButton setTitle:@"3/4" forState:UIControlStateNormal];
+        }
+            break;
+        case KSWidgeMetronomeType4V4:
+        {
+            [self.signatureButton setTitle:@"4/4" forState:UIControlStateNormal];
+        }
+            break;
+        case KSWidgeMetronomeType3V8:
+        {
+            [self.signatureButton setTitle:@"3/8" forState:UIControlStateNormal];
+        }
+            break;
+        case KSWidgeMetronomeType6V8:
+        {
+            [self.signatureButton setTitle:@"6/8" forState:UIControlStateNormal];
+        }
+            break;
+        case KSWidgeMetronomeType8V16:
+        {
+            [self.signatureButton setTitle:@"8/16" forState:UIControlStateNormal];
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+- (void)setVolumeRate:(CGFloat)volumeRate {
+    _volumeRate = volumeRate;
+    self.volumeSlider.value = volumeRate;
+}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 98 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetFunctionView.xib

@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="WidgetFunctionView">
+            <rect key="frame" x="0.0" y="0.0" width="414" height="115"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Vgr-v5-aHg">
+                    <rect key="frame" x="69.5" y="0.0" width="66" height="74"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="74" id="Lsq-An-xLd"/>
+                        <constraint firstAttribute="width" constant="66" id="ZRc-yd-VZu"/>
+                    </constraints>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="speed_minus" backgroundImage="speed_button_bg"/>
+                    <connections>
+                        <action selector="onReduceButtonClick:" destination="iN0-l3-epB" eventType="touchUpInside" id="DTz-pP-VFz"/>
+                    </connections>
+                </button>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8S7-tR-fpt">
+                    <rect key="frame" x="140.5" y="0.0" width="133" height="74"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="74" id="G2q-Lb-C9F"/>
+                        <constraint firstAttribute="width" constant="133" id="GvT-n8-eSh"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" title="4/4" backgroundImage="metronome_bg">
+                        <color key="titleColor" red="0.10196078431372549" green="0.10196078431372549" blue="0.10196078431372549" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    </state>
+                    <connections>
+                        <action selector="changeBeatAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="iQs-c2-I9L"/>
+                    </connections>
+                </button>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xxw-kt-6aa">
+                    <rect key="frame" x="278.5" y="0.0" width="66" height="74"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="74" id="Sxb-x5-Zj0"/>
+                        <constraint firstAttribute="width" constant="66" id="TLd-GR-oiI"/>
+                    </constraints>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="speed_add" backgroundImage="speed_button_bg"/>
+                    <connections>
+                        <action selector="onAddButtonClick:" destination="iN0-l3-epB" eventType="touchUpInside" id="dyG-1d-skC"/>
+                    </connections>
+                </button>
+                <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" minimumValueImage="volume_down" maximumValueImage="volume_up" translatesAutoresizingMaskIntoConstraints="NO" id="n9V-6j-N8n">
+                    <rect key="frame" x="67.5" y="79" width="279" height="31"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <color key="minimumTrackTintColor" red="0.1764705882352941" green="0.7803921568627451" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <color key="maximumTrackTintColor" red="0.87450980392156863" green="0.87450980392156863" blue="0.87450980392156863" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <color key="thumbTintColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <connections>
+                        <action selector="changeVolumeAction:" destination="iN0-l3-epB" eventType="valueChanged" id="bW8-7J-Cqc"/>
+                    </connections>
+                </slider>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="8S7-tR-fpt" firstAttribute="leading" secondItem="Vgr-v5-aHg" secondAttribute="trailing" constant="5" id="8Sy-9T-tOM"/>
+                <constraint firstItem="xxw-kt-6aa" firstAttribute="leading" secondItem="8S7-tR-fpt" secondAttribute="trailing" constant="5" id="Dro-Aa-AjB"/>
+                <constraint firstItem="8S7-tR-fpt" firstAttribute="centerY" secondItem="Vgr-v5-aHg" secondAttribute="centerY" id="KLA-a2-frd"/>
+                <constraint firstItem="xxw-kt-6aa" firstAttribute="centerY" secondItem="Vgr-v5-aHg" secondAttribute="centerY" id="Rbr-as-gF3"/>
+                <constraint firstItem="n9V-6j-N8n" firstAttribute="leading" secondItem="Vgr-v5-aHg" secondAttribute="leading" id="VYP-Bw-aUN"/>
+                <constraint firstItem="8S7-tR-fpt" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="dZt-n0-mcE"/>
+                <constraint firstItem="8S7-tR-fpt" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="fwC-U3-xfC"/>
+                <constraint firstItem="n9V-6j-N8n" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="wUi-jX-bDm"/>
+                <constraint firstItem="n9V-6j-N8n" firstAttribute="top" secondItem="Vgr-v5-aHg" secondAttribute="bottom" constant="5" id="xV7-rF-gtp"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="signatureButton" destination="8S7-tR-fpt" id="Lic-ec-FEc"/>
+                <outlet property="speedAddButton" destination="xxw-kt-6aa" id="WQE-Gf-zIz"/>
+                <outlet property="speedMinusButton" destination="Vgr-v5-aHg" id="zux-FN-7kX"/>
+                <outlet property="volumeSlider" destination="n9V-6j-N8n" id="c0q-59-r5I"/>
+            </connections>
+            <point key="canvasLocation" x="131.8840579710145" y="6.3616071428571423"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="metronome_bg" width="133" height="74"/>
+        <image name="speed_add" width="14" height="14"/>
+        <image name="speed_button_bg" width="66" height="74"/>
+        <image name="speed_minus" width="14" height="14"/>
+        <image name="volume_down" width="12" height="12"/>
+        <image name="volume_up" width="12" height="12"/>
+    </resources>
+</document>

+ 22 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetNavView.h

@@ -0,0 +1,22 @@
+//
+//  WidgetNavView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/13.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef void(^WidgeNavCallback)(void);
+
+@interface WidgetNavView : UIView
+
++ (instancetype)shareInstance;
+
+- (void)navBackAction:(WidgeNavCallback)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 43 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetNavView.m

@@ -0,0 +1,43 @@
+//
+//  WidgetNavView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/13.
+//
+
+#import "WidgetNavView.h"
+
+@interface WidgetNavView ()
+
+@property (nonatomic, copy) WidgeNavCallback callback;
+
+@end
+
+@implementation WidgetNavView
+
+
++ (instancetype)shareInstance {
+    WidgetNavView *view = [[[NSBundle mainBundle] loadNibNamed:@"WidgetNavView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)navBackAction:(WidgeNavCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+- (IBAction)backAction:(id)sender {
+    if (self.callback) {
+        self.callback();
+    }
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 67 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetNavView.xib

@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="WidgetNavView">
+            <rect key="frame" x="0.0" y="0.0" width="414" height="92"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SUk-Ag-K6M">
+                    <rect key="frame" x="0.0" y="48" width="414" height="44"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="back_black" translatesAutoresizingMaskIntoConstraints="NO" id="DDF-Ml-1qT">
+                            <rect key="frame" x="14" y="12" width="12" height="20"/>
+                        </imageView>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yeM-eb-aeH">
+                            <rect key="frame" x="0.0" y="0.0" width="44" height="44"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="44" id="j2f-oU-zP6"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="backAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="SpQ-th-fns"/>
+                            </connections>
+                        </button>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="节拍器" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yk0-dS-WZy">
+                            <rect key="frame" x="179" y="11" width="56" height="22"/>
+                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
+                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="calibratedRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="yk0-dS-WZy" firstAttribute="centerX" secondItem="SUk-Ag-K6M" secondAttribute="centerX" id="0i1-EH-ueL"/>
+                        <constraint firstItem="DDF-Ml-1qT" firstAttribute="centerY" secondItem="SUk-Ag-K6M" secondAttribute="centerY" id="9S9-KI-omf"/>
+                        <constraint firstItem="yeM-eb-aeH" firstAttribute="leading" secondItem="SUk-Ag-K6M" secondAttribute="leading" id="MBb-cm-NxT"/>
+                        <constraint firstItem="yk0-dS-WZy" firstAttribute="centerY" secondItem="SUk-Ag-K6M" secondAttribute="centerY" id="MK3-Qc-twi"/>
+                        <constraint firstItem="DDF-Ml-1qT" firstAttribute="leading" secondItem="SUk-Ag-K6M" secondAttribute="leading" constant="14" id="U6P-oC-62I"/>
+                        <constraint firstAttribute="bottom" secondItem="yeM-eb-aeH" secondAttribute="bottom" id="VHC-dZ-gcV"/>
+                        <constraint firstAttribute="height" constant="44" id="de5-7W-Mcn"/>
+                        <constraint firstItem="yeM-eb-aeH" firstAttribute="top" secondItem="SUk-Ag-K6M" secondAttribute="top" id="gln-eQ-4h0"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="SUk-Ag-K6M" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="4il-Fu-RKX"/>
+                <constraint firstAttribute="trailing" secondItem="SUk-Ag-K6M" secondAttribute="trailing" id="Dcf-Gp-3NN"/>
+                <constraint firstAttribute="bottom" secondItem="SUk-Ag-K6M" secondAttribute="bottom" id="piy-Th-6Ak"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="131.8840579710145" y="-167.41071428571428"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="back_black" width="12" height="20"/>
+    </resources>
+</document>

+ 41 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetSpeedView.h

@@ -0,0 +1,41 @@
+//
+//  WidgetSpeedView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/13.
+//
+
+#import <UIKit/UIKit.h>
+#import "WidgetDotView.h"
+// 代理
+@protocol MetronomeControlViewDelegate <NSObject>
+
+/**
+ 改变频率
+
+ @param isAdd 增加频率还是减小频率
+ @param speed 增加或减小的值
+ */
+- (void)changeSpeedWithIsAdd:(BOOL)isAdd speed:(int)speed;
+
+
+@end
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface WidgetSpeedView : UIView
+
++ (instancetype)shareInstance;
+
+/** 节拍类型 */
+@property (nonatomic, assign) KSWidgeMetronomeType currentType;
+
+/** 代理 */
+@property (nonatomic, weak) id <MetronomeControlViewDelegate> delegate;
+
+/** 播放速率,范围50~200 */
+@property (nonatomic, assign) int speed;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 145 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetSpeedView.m

@@ -0,0 +1,145 @@
+//
+//  WidgetSpeedView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/9/13.
+//
+
+#import "WidgetSpeedView.h"
+
+@interface WidgetSpeedView ()
+{
+    CGFloat _lastPointAngle;//上一个点相对于x轴角度
+    CGPoint _centerPoint;
+}
+
+@property (weak, nonatomic) IBOutlet UIImageView *nodeImage;
+
+@property (weak, nonatomic) IBOutlet UILabel *speedLabel;
+
+@property (weak, nonatomic) IBOutlet UIView *bgView;
+
+/** 控制频率View */
+@property (nonatomic, strong) UIImageView *controlSpeedView;
+
+@end
+
+@implementation WidgetSpeedView
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    [self createUI];
+}
+
++ (instancetype)shareInstance {
+    WidgetSpeedView *view = [[[NSBundle mainBundle] loadNibNamed:@"WidgetSpeedView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)createUI {
+    _centerPoint = CGPointMake(300 / 2, kScreenWidth / 2);//中心点
+    
+    self.controlSpeedView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"speed_dot"]];
+    [self.bgView addSubview:self.controlSpeedView];
+    [self.controlSpeedView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.top.right.bottom.left.mas_equalTo(self.bgView);
+    }];
+    
+}
+
+- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
+    
+    UITouch *touch = [touches anyObject];
+    CGPoint point = [touch locationInView:self];
+    //计算上一个点相对于x轴的角度
+    CGFloat lastPointRadius = sqrt(pow(point.y - _centerPoint.y, 2) + pow(point.x - _centerPoint.x, 2));
+    if (lastPointRadius == 0) {
+        return;
+    }
+    _lastPointAngle = acos((point.x - _centerPoint.x) / lastPointRadius);
+    if (point.y > _centerPoint.y) {
+        _lastPointAngle = 2 * M_PI - _lastPointAngle;
+    }
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
+
+    UITouch *touch = [touches anyObject];
+    CGPoint currentPoint = [touch locationInView:self];
+//    CGPoint preLocation = [touch preciseLocationInView:self];
+
+    
+    //1.计算当前点相对于x轴的角度
+    CGFloat currentPointRadius = sqrt(pow(currentPoint.y - _centerPoint.y, 2) + pow(currentPoint.x - _centerPoint.x, 2));
+    if (currentPointRadius == 0) {//当点在中心点时,被除数不能为0
+        return;
+    }
+    CGFloat curentPointAngle = acos((currentPoint.x - _centerPoint.x) / currentPointRadius);
+    if (currentPoint.y > _centerPoint.y) {
+        curentPointAngle = 2 * M_PI - curentPointAngle;
+    }
+    //2.变化的角度
+    CGFloat angle = _lastPointAngle - curentPointAngle;
+    NSLog(@"angle -----   %f",angle);
+    // 小点点的位置
+    self.controlSpeedView.transform = CGAffineTransformRotate(self.controlSpeedView.transform, angle);
+    _lastPointAngle = curentPointAngle;
+    
+    if (angle > 0 && angle < 1) {
+        if (self.delegate && [self.delegate respondsToSelector:@selector(changeSpeedWithIsAdd:speed:)]) {
+            [self.delegate changeSpeedWithIsAdd:YES speed:MIN(1, MAX(angle *2, 1))];
+        }
+    }
+    else if(angle < 0 && angle > -1) {
+        if (self.delegate && [self.delegate respondsToSelector:@selector(changeSpeedWithIsAdd:speed:)]) {
+            [self.delegate changeSpeedWithIsAdd:NO speed:MIN(1, MAX(angle *2, 1))];
+        }
+    }
+}
+
+- (void)setSpeed:(int)speed {
+    _speed = speed;
+    self.speedLabel.text = [NSString stringWithFormat:@"%d", speed];
+}
+
+
+//- (void)setCurrentType:(KSWidgeMetronomeType)currentType {
+//    _currentType = currentType;
+//    switch (currentType) {
+//        case KSWidgeMetronomeType1V2:
+//        case KSWidgeMetronomeType2V2:
+//        {
+//            [self.nodeImage setImage:[UIImage imageNamed:@"half_node"]];
+//        }
+//            break;
+//        case KSWidgeMetronomeType1V4:
+//        case KSWidgeMetronomeType2V4:
+//        case KSWidgeMetronomeType3V4:
+//        case KSWidgeMetronomeType4V4:
+//        {
+//            [self.nodeImage setImage:[UIImage imageNamed:@"quarter_node"]];
+//        }
+//            break;
+//        case KSWidgeMetronomeType3V8:
+//        case KSWidgeMetronomeType6V8:
+//        {
+//            [self.nodeImage setImage:[UIImage imageNamed:@"eighth_node"]];
+//        }
+//            break;
+//        case KSWidgeMetronomeType8V16:
+//        {
+//            [self.nodeImage setImage:[UIImage imageNamed:@"sixteenth_node"]];
+//        }
+//            break;
+//        default:
+//            break;
+//    }
+//}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 81 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/Metronome/WidgetSpeedView.xib

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="WidgetSpeedView">
+            <rect key="frame" x="0.0" y="0.0" width="373" height="300"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xgb-CP-q8k">
+                    <rect key="frame" x="36.5" y="0.0" width="300" height="300"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="speed_background" translatesAutoresizingMaskIntoConstraints="NO" id="M3y-XL-rfN">
+                            <rect key="frame" x="0.0" y="0.0" width="300" height="300"/>
+                        </imageView>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="90" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="e9W-bt-jeB">
+                            <rect key="frame" x="124.5" y="126" width="51" height="48"/>
+                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="40"/>
+                            <nil key="textColor"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="quarter_node" translatesAutoresizingMaskIntoConstraints="NO" id="7gk-Eb-b8E">
+                            <rect key="frame" x="108" y="100" width="20" height="20"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="20" id="7Ln-ex-jSv"/>
+                                <constraint firstAttribute="width" constant="20" id="jtJ-h3-wy8"/>
+                            </constraints>
+                        </imageView>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="=" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tZK-hU-4GH">
+                            <rect key="frame" x="128" y="101.5" width="9" height="17"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="7gk-Eb-b8E" firstAttribute="leading" secondItem="xgb-CP-q8k" secondAttribute="leading" constant="108" id="9Z5-8o-0mh"/>
+                        <constraint firstItem="e9W-bt-jeB" firstAttribute="centerY" secondItem="xgb-CP-q8k" secondAttribute="centerY" id="Atr-A9-US6"/>
+                        <constraint firstItem="e9W-bt-jeB" firstAttribute="centerX" secondItem="xgb-CP-q8k" secondAttribute="centerX" id="B3Z-Wx-QmF"/>
+                        <constraint firstItem="tZK-hU-4GH" firstAttribute="leading" secondItem="7gk-Eb-b8E" secondAttribute="trailing" id="B9k-F1-kxz"/>
+                        <constraint firstAttribute="trailing" secondItem="M3y-XL-rfN" secondAttribute="trailing" id="Fff-ft-9mF"/>
+                        <constraint firstItem="tZK-hU-4GH" firstAttribute="centerY" secondItem="7gk-Eb-b8E" secondAttribute="centerY" id="FrH-5b-gLZ"/>
+                        <constraint firstAttribute="bottom" secondItem="M3y-XL-rfN" secondAttribute="bottom" id="ONf-RV-cOi"/>
+                        <constraint firstAttribute="height" constant="300" id="Ort-HP-5YW"/>
+                        <constraint firstItem="M3y-XL-rfN" firstAttribute="leading" secondItem="xgb-CP-q8k" secondAttribute="leading" id="bS2-Qj-cqY"/>
+                        <constraint firstItem="7gk-Eb-b8E" firstAttribute="top" secondItem="xgb-CP-q8k" secondAttribute="top" constant="100" id="k83-31-D35"/>
+                        <constraint firstAttribute="width" constant="300" id="liP-q8-rba"/>
+                        <constraint firstItem="M3y-XL-rfN" firstAttribute="top" secondItem="xgb-CP-q8k" secondAttribute="top" id="m8Z-QB-sEg"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="xgb-CP-q8k" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="2Aa-Aa-9Gq"/>
+                <constraint firstItem="xgb-CP-q8k" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="6cd-Mf-ZSX"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="bgView" destination="xgb-CP-q8k" id="991-9V-bEO"/>
+                <outlet property="nodeImage" destination="7gk-Eb-b8E" id="WZ2-0a-Alb"/>
+                <outlet property="speedLabel" destination="e9W-bt-jeB" id="n90-IA-ARD"/>
+            </connections>
+            <point key="canvasLocation" x="161.59420289855075" y="11.383928571428571"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="quarter_node" width="20" height="20"/>
+        <image name="speed_background" width="300" height="300"/>
+    </resources>
+</document>

+ 27 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/SmallToolBodyView.h

@@ -0,0 +1,27 @@
+//
+//  SmallToolBodyView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef NS_ENUM(NSInteger, TOOLTYPE) {
+    TOOLTYPE_METRONOME, // 节拍器
+    TOOLTYPE_TONE,      // 调音器
+};
+
+typedef void(^ToolFunctionCallback)(TOOLTYPE type);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SmallToolBodyView : UIView
+
++ (instancetype)shareInstance;
+
+- (void)chooseToolCallback:(ToolFunctionCallback)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 77 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/SmallToolBodyView.m

@@ -0,0 +1,77 @@
+//
+//  SmallToolBodyView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "SmallToolBodyView.h"
+
+@interface SmallToolBodyView ()
+
+@property (weak, nonatomic) IBOutlet UIView *metrnomeBgView;
+
+@property (weak, nonatomic) IBOutlet UIView *toneBgView;
+
+@property (nonatomic, copy) ToolFunctionCallback callback;
+
+@end
+
+@implementation SmallToolBodyView
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    CAGradientLayer *layer = [self createGradientLayerFromColor:HexRGB(0xACFBEC) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0x7FEED9) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, KPortraitWidth - 32, 114)];
+    layer.cornerRadius = 14.0f;
+    layer.masksToBounds = YES;
+    [self.metrnomeBgView.layer addSublayer:layer];
+    
+    CAGradientLayer *colorLayer = [self createGradientLayerFromColor:HexRGB(0xE2D5FF) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0xCAB2FF) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, KPortraitWidth - 32, 114)];
+    colorLayer.cornerRadius = 14.0f;
+    colorLayer.masksToBounds = YES;
+    [self.toneBgView.layer addSublayer:colorLayer];
+}
+
++ (instancetype)shareInstance {
+    SmallToolBodyView *view = [[[NSBundle mainBundle] loadNibNamed:@"SmallToolBodyView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)chooseToolCallback:(ToolFunctionCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+
+- (IBAction)chooseMetronome:(id)sender {
+    if (self.callback) {
+        self.callback(TOOLTYPE_METRONOME);
+    }
+}
+
+- (IBAction)chooseTone:(id)sender {
+    if (self.callback) {
+        self.callback(TOOLTYPE_TONE);
+    }
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+- (CAGradientLayer *)createGradientLayerFromColor:(UIColor *)fromColor startPoint:(CGPoint)startPoint endColor:(UIColor *)endColor endPoint:(CGPoint)endPoint bounds:(CGRect)bounds {
+    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
+    gradientLayer.colors = @[(__bridge id)fromColor.CGColor, (__bridge id)endColor.CGColor];
+    gradientLayer.startPoint = startPoint;
+    gradientLayer.endPoint = endPoint;
+    gradientLayer.frame = bounds;
+    gradientLayer.locations = @[@(0),@(1.0f)];
+    return gradientLayer;
+}
+
+
+@end

+ 168 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/SmallToolBodyView.xib

@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_0" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="SmallToolBodyView">
+            <rect key="frame" x="0.0" y="0.0" width="390" height="536"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7pE-lf-Zsa">
+                    <rect key="frame" x="16" y="18" width="358" height="114"/>
+                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                    <gestureRecognizers/>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KbL-Cl-bwO">
+                    <rect key="frame" x="16" y="18" width="358" height="114"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="节拍器" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mOb-C9-ek9">
+                            <rect key="frame" x="24" y="24" width="62" height="28"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="28" id="4cy-JE-o1v"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
+                            <color key="textColor" red="0.0" green="0.42745098039215684" blue="0.34509803921568627" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="节拍器,练习好节奏" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fYi-C4-Nd8">
+                            <rect key="frame" x="24" y="58" width="128.66666666666666" height="20"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="20" id="d06-5s-M0F"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" red="0.0" green="0.42745098040000001" blue="0.34509803919999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="metronome_image" translatesAutoresizingMaskIntoConstraints="NO" id="Bog-1X-TMf">
+                            <rect key="frame" x="226" y="0.0" width="132" height="114"/>
+                            <constraints>
+                                <constraint firstAttribute="width" secondItem="Bog-1X-TMf" secondAttribute="height" multiplier="22:19" id="geJ-Gg-GV4"/>
+                            </constraints>
+                        </imageView>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="bottom" secondItem="Bog-1X-TMf" secondAttribute="bottom" id="2U1-Ni-Bfu"/>
+                        <constraint firstAttribute="height" constant="114" id="3WE-5U-XPW"/>
+                        <constraint firstItem="mOb-C9-ek9" firstAttribute="top" secondItem="KbL-Cl-bwO" secondAttribute="top" constant="24" id="5IG-oq-ltv"/>
+                        <constraint firstAttribute="trailing" secondItem="Bog-1X-TMf" secondAttribute="trailing" id="CNH-J8-5wM"/>
+                        <constraint firstItem="mOb-C9-ek9" firstAttribute="leading" secondItem="KbL-Cl-bwO" secondAttribute="leading" constant="24" id="WUi-Xk-CB6"/>
+                        <constraint firstItem="fYi-C4-Nd8" firstAttribute="leading" secondItem="mOb-C9-ek9" secondAttribute="leading" id="phj-Lx-O0p"/>
+                        <constraint firstItem="Bog-1X-TMf" firstAttribute="top" secondItem="KbL-Cl-bwO" secondAttribute="top" id="qwC-c0-eGn"/>
+                        <constraint firstItem="fYi-C4-Nd8" firstAttribute="top" secondItem="mOb-C9-ek9" secondAttribute="bottom" constant="6" id="vaN-vB-Gwk"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="14"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                    <connections>
+                        <outletCollection property="gestureRecognizers" destination="9vs-gn-bTj" appends="YES" id="wzz-JZ-nwz"/>
+                    </connections>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6Es-PT-RKh">
+                    <rect key="frame" x="16" y="148" width="358" height="114"/>
+                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                    <gestureRecognizers/>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Usm-eK-CbE">
+                    <rect key="frame" x="16" y="148" width="358" height="114"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="调音器" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="a3D-R7-hIK">
+                            <rect key="frame" x="24" y="24" width="62" height="28"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="28" id="H7M-N6-557"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
+                            <color key="textColor" red="0.42352941176470588" green="0.0" blue="0.76470588235294112" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="精准调音,一劳永逸" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="n9K-wq-FY7">
+                            <rect key="frame" x="24" y="58" width="128.66666666666666" height="20"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="20" id="laX-31-CM7"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" red="0.42352941176470588" green="0.0" blue="0.76470588235294112" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tone_tuning" translatesAutoresizingMaskIntoConstraints="NO" id="tri-rT-zBB">
+                            <rect key="frame" x="226" y="0.0" width="132" height="114"/>
+                            <constraints>
+                                <constraint firstAttribute="width" secondItem="tri-rT-zBB" secondAttribute="height" multiplier="22:19" id="SY7-Or-Rqy"/>
+                            </constraints>
+                        </imageView>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="n9K-wq-FY7" firstAttribute="leading" secondItem="a3D-R7-hIK" secondAttribute="leading" id="3vQ-bM-igo"/>
+                        <constraint firstItem="tri-rT-zBB" firstAttribute="top" secondItem="Usm-eK-CbE" secondAttribute="top" id="KCv-Cp-h9E"/>
+                        <constraint firstItem="a3D-R7-hIK" firstAttribute="top" secondItem="Usm-eK-CbE" secondAttribute="top" constant="24" id="QGE-gc-JwY"/>
+                        <constraint firstItem="n9K-wq-FY7" firstAttribute="top" secondItem="a3D-R7-hIK" secondAttribute="bottom" constant="6" id="Rwh-yq-iaI"/>
+                        <constraint firstAttribute="bottom" secondItem="tri-rT-zBB" secondAttribute="bottom" id="hFh-0A-ete"/>
+                        <constraint firstAttribute="height" constant="114" id="jJn-LN-WQj"/>
+                        <constraint firstItem="a3D-R7-hIK" firstAttribute="leading" secondItem="Usm-eK-CbE" secondAttribute="leading" constant="24" id="t1c-0J-O2x"/>
+                        <constraint firstAttribute="trailing" secondItem="tri-rT-zBB" secondAttribute="trailing" id="y7r-SW-qnQ"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="14"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                    <connections>
+                        <outletCollection property="gestureRecognizers" destination="g8h-7k-sbT" appends="YES" id="5CR-UR-wvj"/>
+                    </connections>
+                </view>
+            </subviews>
+            <color key="backgroundColor" red="0.97254901960784312" green="0.97647058823529409" blue="0.9882352941176471" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+            <constraints>
+                <constraint firstItem="6Es-PT-RKh" firstAttribute="top" secondItem="Usm-eK-CbE" secondAttribute="top" id="6dg-GT-Ksh"/>
+                <constraint firstItem="7pE-lf-Zsa" firstAttribute="bottom" secondItem="KbL-Cl-bwO" secondAttribute="bottom" id="GP7-dl-p1G"/>
+                <constraint firstItem="6Es-PT-RKh" firstAttribute="bottom" secondItem="Usm-eK-CbE" secondAttribute="bottom" id="HsQ-Jp-7lQ"/>
+                <constraint firstItem="7pE-lf-Zsa" firstAttribute="trailing" secondItem="KbL-Cl-bwO" secondAttribute="trailing" id="KLk-JI-yy5"/>
+                <constraint firstItem="Usm-eK-CbE" firstAttribute="top" secondItem="KbL-Cl-bwO" secondAttribute="bottom" constant="16" id="NNX-2g-AL1"/>
+                <constraint firstItem="KbL-Cl-bwO" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="18" id="Q2d-d9-jkt"/>
+                <constraint firstItem="7pE-lf-Zsa" firstAttribute="leading" secondItem="KbL-Cl-bwO" secondAttribute="leading" id="SX4-zW-u40"/>
+                <constraint firstItem="6Es-PT-RKh" firstAttribute="leading" secondItem="Usm-eK-CbE" secondAttribute="leading" id="UVc-oT-i8l"/>
+                <constraint firstItem="7pE-lf-Zsa" firstAttribute="top" secondItem="KbL-Cl-bwO" secondAttribute="top" id="eIY-He-vXn"/>
+                <constraint firstItem="KbL-Cl-bwO" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="kZn-jQ-9OT"/>
+                <constraint firstItem="Usm-eK-CbE" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="nNg-ul-d1k"/>
+                <constraint firstAttribute="trailing" secondItem="KbL-Cl-bwO" secondAttribute="trailing" constant="16" id="r6T-3q-ibo"/>
+                <constraint firstAttribute="trailing" secondItem="Usm-eK-CbE" secondAttribute="trailing" constant="16" id="vZr-Ku-Vfw"/>
+                <constraint firstItem="6Es-PT-RKh" firstAttribute="trailing" secondItem="Usm-eK-CbE" secondAttribute="trailing" id="xtt-yt-JZo"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="metrnomeBgView" destination="7pE-lf-Zsa" id="6s3-XW-LSJ"/>
+                <outlet property="toneBgView" destination="6Es-PT-RKh" id="TbE-VR-gNn"/>
+            </connections>
+            <point key="canvasLocation" x="87.692307692307693" y="68.957345971563981"/>
+        </view>
+        <tapGestureRecognizer id="9vs-gn-bTj">
+            <connections>
+                <action selector="chooseMetronome:" destination="iN0-l3-epB" id="rdo-OF-fwD"/>
+            </connections>
+        </tapGestureRecognizer>
+        <tapGestureRecognizer id="g8h-7k-sbT">
+            <connections>
+                <action selector="chooseTone:" destination="iN0-l3-epB" id="ueq-cr-tj7"/>
+            </connections>
+        </tapGestureRecognizer>
+    </objects>
+    <resources>
+        <image name="metronome_image" width="132" height="114"/>
+        <image name="tone_tuning" width="132" height="114"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 77 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeView.h

@@ -0,0 +1,77 @@
+/*
+ * WMGaugeView.h
+ *
+ * Copyright (C) 2014 William Markezana <william.markezana@me.com>
+ *
+ */
+
+#import <UIKit/UIKit.h>
+#import "WMGaugeViewStyle.h"
+#import "WMGaugeViewStyleFlatThin.h"
+#import "WMGaugeViewStyle3D.h"
+
+/**
+ * Styling enumerations
+ */
+typedef NS_ENUM(NSUInteger, WMGaugeViewSubdivisionsAlignment) {
+    WMGaugeViewSubdivisionsAlignmentTop,
+    WMGaugeViewSubdivisionsAlignmentCenter,
+    WMGaugeViewSubdivisionsAlignmentBottom
+};
+
+/**
+ * WMGaugeView class
+ */
+@interface WMGaugeView : UIView
+
+/**
+ * WMGaugeView properties
+ */
+@property (nonatomic, readwrite, assign) bool showInnerBackground;
+@property (nonatomic, readwrite, assign) bool showInnerRim;
+@property (nonatomic, readwrite, assign) CGFloat innerRimWidth;
+@property (nonatomic, readwrite, assign) CGFloat innerRimBorderWidth;
+@property (nonatomic, readwrite, assign) CGFloat scalePosition;
+@property (nonatomic, readwrite, assign) CGFloat scaleStartAngle;
+@property (nonatomic, readwrite, assign) CGFloat scaleEndAngle;
+@property (nonatomic, readwrite, assign) CGFloat scaleDivisions;
+@property (nonatomic, readwrite, assign) CGFloat scaleSubdivisions;
+@property (nonatomic, readwrite, assign) bool showScaleShadow;
+@property (nonatomic, readwrite, assign) bool showScale;
+@property (nonatomic, readwrite, assign) bool showScaleValues;
+@property (nonatomic, readwrite, assign) WMGaugeViewSubdivisionsAlignment scalesubdivisionsAligment;
+@property (nonatomic, readwrite, assign) CGFloat scaleDivisionsLength;
+@property (nonatomic, readwrite, assign) CGFloat scaleDivisionsWidth;
+@property (nonatomic, readwrite, assign) CGFloat scaleSubdivisionsLength;
+@property (nonatomic, readwrite, assign) CGFloat scaleSubdivisionsWidth;
+@property (nonatomic, readwrite, strong) UIColor *scaleDivisionColor;
+@property (nonatomic, readwrite, strong) UIColor *scaleSubDivisionColor;
+@property (nonatomic, readwrite, strong) UIFont *scaleFont;
+@property (nonatomic, readwrite, assign) float value;
+@property (nonatomic, readwrite, assign) float minValue;
+@property (nonatomic, readwrite, assign) float maxValue;
+@property (nonatomic, readwrite, assign) bool showRangeLabels;
+@property (nonatomic, readwrite, assign) CGFloat rangeLabelsWidth;
+@property (nonatomic, readwrite, strong) UIFont *rangeLabelsFont;
+@property (nonatomic, readwrite, assign) BOOL adjustRangeLabelSizeToFitWidth;
+@property (nonatomic, readwrite, strong) UIColor *rangeLabelsFontColor;
+@property (nonatomic, readwrite, assign) CGFloat rangeLabelsFontKerning;
+@property (nonatomic, readwrite, strong) NSArray<NSNumber*> *rangeValues;
+@property (nonatomic, readwrite, strong) NSArray<UIColor*> *rangeColors;
+@property (nonatomic, readwrite, strong) NSArray<NSString*> *rangeLabels;
+@property (nonatomic, readwrite, strong) UIColor *unitOfMeasurementColor;
+@property (nonatomic, readwrite, assign) CGFloat unitOfMeasurementVerticalOffset;
+@property (nonatomic, readwrite, strong) UIFont *unitOfMeasurementFont;
+@property (nonatomic, readwrite, strong) NSString *unitOfMeasurement;
+@property (nonatomic, readwrite, assign) bool showUnitOfMeasurement;
+@property (nonatomic, readwrite, strong) id<WMGaugeViewStyle> style;
+
+/**
+ * WMGaugeView public functions
+ */
+- (void)setValue:(float)value animated:(BOOL)animated;
+- (void)setValue:(float)value animated:(BOOL)animated completion:(void (^)(BOOL finished))completion;
+- (void)setValue:(float)value animated:(BOOL)animated duration:(NSTimeInterval)duration;
+- (void)setValue:(float)value animated:(BOOL)animated duration:(NSTimeInterval)duration completion:(void (^)(BOOL finished))completion;
+
+@end

+ 820 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeView.m

@@ -0,0 +1,820 @@
+/*
+ * WMGaugeView.h
+ *
+ * Copyright (C) 2014 William Markezana <william.markezana@me.com>
+ *
+ */
+
+#import "WMGaugeView.h"
+
+/* Scale conversion macro from [0-1] range to view  real size range */
+#define FULL_SCALE(x,y)    (x)*self.bounds.size.width, (y)*self.bounds.size.height
+
+@implementation WMGaugeView
+{
+    /* Drawing rects */
+    CGRect fullRect;
+    CGRect innerRimRect;
+    CGRect innerRimBorderRect;
+    CGRect faceRect;
+    CGRect rangeLabelsRect;
+    CGRect scaleRect;
+
+    /* View center */
+    CGPoint center;
+
+    /* Scale variables */
+    CGFloat scaleRotation;    
+    CGFloat divisionValue;
+    CGFloat subdivisionValue;
+    CGFloat subdivisionAngle;
+    
+    /* Background image */
+    UIImage *background;
+    
+    /* Needle layer */
+    CALayer *rootNeedleLayer;
+
+    /* Annimation completion */
+    void (^animationCompletion)(BOOL);
+}
+
+#pragma mark - Initialization
+
+- (id)initWithFrame:(CGRect)frame
+{
+    self = [super initWithFrame:frame];
+    if (self)
+    {
+        [self initialize];
+    }
+    return self;
+}
+
+- (void)awakeFromNib
+{
+    [super awakeFromNib];
+    [self initialize];
+}
+
+/**
+ *  Set all properties to default values
+ *  Set all private variables to default values
+ */
+- (void)initialize;
+{
+    _style = nil;
+    _showInnerRim = NO;
+    _showInnerBackground = YES;
+    _innerRimWidth = 0.05;
+    _innerRimBorderWidth = 0.005;
+    
+    _scalePosition = 0.025;
+    _scaleStartAngle = 30.0;
+    _scaleEndAngle = 330.0;
+    _scaleDivisions = 12.0;
+    _scaleSubdivisions = 10.0;
+    _showScale = YES;
+    _showScaleShadow = YES;
+    _showScaleValues = YES;
+    _scalesubdivisionsAligment = WMGaugeViewSubdivisionsAlignmentTop;
+    _scaleDivisionsLength = 0.045;
+    _scaleDivisionsWidth = 0.01;
+    _scaleSubdivisionsLength = 0.015;
+    _scaleSubdivisionsWidth = 0.01;
+    
+    _value = 0.0;
+    _minValue = 0.0;
+    _maxValue = 240.0;
+
+    background = nil;
+    
+    _showRangeLabels = NO;
+    _rangeLabelsWidth = 0.05;
+    _rangeLabelsFont = [UIFont fontWithName:@"Helvetica" size:0.05];
+    _adjustRangeLabelSizeToFitWidth = NO;
+    _rangeLabelsFontColor = [UIColor whiteColor];
+    _rangeLabelsFontKerning = 1.0;
+    _rangeValues = nil;
+    _rangeColors = nil;
+    _rangeLabels = nil;
+    
+    _scaleDivisionColor = RGB(68, 84, 105);
+    _scaleSubDivisionColor = RGB(217, 217, 217);
+    
+    _scaleFont = nil;
+    
+    _unitOfMeasurementVerticalOffset = 0.6;
+    _unitOfMeasurementColor = [UIColor whiteColor];
+    _unitOfMeasurementFont = [UIFont fontWithName:@"Helvetica" size:0.04];
+    _unitOfMeasurement = @"";
+    _showUnitOfMeasurement = NO;
+    
+    animationCompletion = nil;
+
+    [self initDrawingRects];
+    [self initScale];
+}
+
+/**
+ *  Initialize all drawing rects and center point
+ */
+- (void)initDrawingRects
+{
+    center = CGPointMake(0.5, 0.5);
+    fullRect = CGRectMake(0.0, 0.0, 1.0, 1.0);
+    
+    _innerRimBorderWidth = _showInnerRim ? _innerRimBorderWidth : 0.0;
+    _innerRimWidth = _showInnerRim ? _innerRimWidth : 0.0;
+    
+    innerRimRect = fullRect;
+    innerRimBorderRect = CGRectMake(innerRimRect.origin.x + _innerRimBorderWidth,
+                                    innerRimRect.origin.y + _innerRimBorderWidth,
+                                    innerRimRect.size.width - 2 * _innerRimBorderWidth,
+                                    innerRimRect.size.height - 2 * _innerRimBorderWidth);
+    faceRect = CGRectMake(innerRimRect.origin.x + _innerRimWidth,
+                          innerRimRect.origin.y + _innerRimWidth,
+                          innerRimRect.size.width - 2 * _innerRimWidth,
+                          innerRimRect.size.height - 2 * _innerRimWidth);
+    rangeLabelsRect = CGRectMake(faceRect.origin.x + (_showRangeLabels ? _rangeLabelsWidth : 0.0),
+                                 faceRect.origin.y + (_showRangeLabels ? _rangeLabelsWidth : 0.0),
+                                 faceRect.size.width - 2 * (_showRangeLabels ? _rangeLabelsWidth : 0.0),
+                                 faceRect.size.height - 2 * (_showRangeLabels ? _rangeLabelsWidth : 0.0));
+    scaleRect = CGRectMake(rangeLabelsRect.origin.x + _scalePosition,
+                           rangeLabelsRect.origin.y + _scalePosition,
+                           rangeLabelsRect.size.width - 2 * _scalePosition,
+                           rangeLabelsRect.size.height - 2 * _scalePosition);
+}
+
+#pragma mark - Drawing
+
+/**
+ * Main drawing entry point 
+ */
+- (void)drawRect:(CGRect)rect
+{
+    if (background == nil)
+    {
+        // Create image context
+        UIGraphicsBeginImageContextWithOptions(rect.size, NO, [UIScreen mainScreen].scale);
+        CGContextRef context = UIGraphicsGetCurrentContext();
+        
+        // Scale context for [0-1] drawings
+        CGContextScaleCTM(context, rect.size.width , rect.size.height);
+
+        // Draw gauge background in image context
+        [self drawGauge:context];
+        
+        // Save background
+        background = UIGraphicsGetImageFromCurrentImageContext();
+        UIGraphicsEndImageContext();
+    }
+    
+    // Drawing background in view
+    [background drawInRect:rect];
+    
+    if (rootNeedleLayer == nil)
+    {
+        // Initialize needle layer
+        rootNeedleLayer = [CALayer new];
+
+        // For performance puporse, the needle layer is not scaled to [0-1] range
+        rootNeedleLayer.frame = self.bounds;
+        [self.layer addSublayer:rootNeedleLayer];
+        
+        // Draw needle
+        [self drawNeedle];
+        
+        // Set needle current value
+        [self setValue:_value animated:NO];
+    }    
+}
+
+/**
+ *  Gauge background drawing
+ */
+- (void)drawGauge:(CGContextRef)context
+{
+    [self drawRim:context];
+
+    if (_showInnerBackground)
+        [self drawFace:context];
+
+    if (_showUnitOfMeasurement)
+        [self drawText:context];
+
+    if (_showScale)
+        [self drawScale:context];
+
+    if (_showRangeLabels)
+        [self drawRangeLabels:context];
+}
+
+/**
+ *  Gauge external rim drawing
+ */
+- (void)drawRim:(CGContextRef)context
+{
+    // TODO
+}
+
+/**
+ *  Gauge inner background drawing
+ */
+- (void)drawFace:(CGContextRef)context
+{
+    if ([_style conformsToProtocol:@protocol(WMGaugeViewStyle)]) {
+        [_style drawFaceWithContext:context inRect:faceRect];
+    }
+}
+
+/**
+ *  Unit of measurement drawing
+ */
+- (void)drawText:(CGContextRef)context
+{
+    CGContextSetShadow(context, CGSizeMake(0.05, 0.05), 2.0);
+    UIFont* font = _unitOfMeasurementFont ? _unitOfMeasurementFont : [UIFont fontWithName:@"Helvetica" size:0.04];
+    UIColor* color = _unitOfMeasurementColor ? _unitOfMeasurementColor : [UIColor whiteColor];
+    NSDictionary* stringAttrs = @{ NSFontAttributeName : font, NSForegroundColorAttributeName : color };
+    NSAttributedString* attrStr = [[NSAttributedString alloc] initWithString:_unitOfMeasurement attributes:stringAttrs];
+    CGSize fontWidth;
+    fontWidth = [_unitOfMeasurement sizeWithAttributes:stringAttrs];
+
+    [attrStr drawAtPoint:CGPointMake(0.5 - fontWidth.width / 2.0, _unitOfMeasurementVerticalOffset)];
+}
+
+/**
+ * Scale drawing 
+ */
+- (void)drawScale:(CGContextRef)context
+{
+    CGContextSaveGState(context);
+    [self rotateContext:context fromCenter:center withAngle:DEGREES_TO_RADIANS(180 + _scaleStartAngle)];
+    
+    int totalTicks = _scaleDivisions * _scaleSubdivisions + 1;
+    for (int i = 0; i < totalTicks; i++)
+    {
+        CGFloat offset = 0.0;
+        if (_scalesubdivisionsAligment == WMGaugeViewSubdivisionsAlignmentCenter) offset = (_scaleDivisionsLength - _scaleSubdivisionsLength) / 2.0;
+        if (_scalesubdivisionsAligment == WMGaugeViewSubdivisionsAlignmentBottom) offset = _scaleDivisionsLength - _scaleSubdivisionsLength;
+        
+        CGFloat y1 = scaleRect.origin.y;
+        CGFloat y2 = y1 + _scaleSubdivisionsLength;
+        CGFloat y3 = y1 + _scaleDivisionsLength;
+        
+        float value = [self valueForTick:i];
+        float div = (_maxValue - _minValue) / _scaleDivisions;
+        float mod = (int)value % (int)div;
+        
+        // Division
+        if ((fabsf(mod - 0) < 0.000001) || (fabsf(mod - div) < 0.000001))
+        {
+            // Initialize Core Graphics settings
+            UIColor *color = (_rangeValues && _rangeColors) ? [self rangeColorForValue:value] : _scaleDivisionColor;
+            CGContextSetStrokeColorWithColor(context, color.CGColor);
+            CGContextSetLineWidth(context, _scaleDivisionsWidth);
+            CGContextSetShadow(context, CGSizeMake(0.05, 0.05), _showScaleShadow ? 2.0 : 0.0);
+            
+            // Draw tick
+            CGContextMoveToPoint(context, 0.5, y1);
+            CGContextAddLineToPoint(context, 0.5, y3);
+            CGContextStrokePath(context);
+            
+            // Draw label
+            if(_showScaleValues) {
+                NSString *valueString = [NSString stringWithFormat:@"%0.0f",value];
+                UIFont* font = _scaleFont ? _scaleFont : [UIFont fontWithName:@"Helvetica-Bold" size:0.05];
+                NSDictionary* stringAttrs = @{ NSFontAttributeName : font, NSForegroundColorAttributeName : color };
+                NSAttributedString* attrStr = [[NSAttributedString alloc] initWithString:valueString attributes:stringAttrs];
+                CGSize fontWidth;
+                fontWidth = [valueString sizeWithAttributes:stringAttrs];
+                
+                [attrStr drawAtPoint:CGPointMake(0.5 - fontWidth.width / 2.0, y3 + 0.005)];
+            }
+        }
+        // Subdivision
+        else
+        {
+            // Initialize Core Graphics settings
+            UIColor *color = (_rangeValues && _rangeColors) ? [self rangeColorForValue:value] : _scaleSubDivisionColor;
+            CGContextSetStrokeColorWithColor(context, color.CGColor);
+            CGContextSetLineWidth(context, _scaleSubdivisionsWidth);
+            CGContextMoveToPoint(context, 0.5, y1);
+            if (_showScaleShadow) CGContextSetShadow(context, CGSizeMake(0.05, 0.05), 2.0);
+            
+            // Draw tick
+            CGContextMoveToPoint(context, 0.5, y1 + offset);
+            CGContextAddLineToPoint(context, 0.5, y2 + offset);
+            CGContextStrokePath(context);
+        }
+        
+        // Rotate to next tick
+        [self rotateContext:context fromCenter:center withAngle:DEGREES_TO_RADIANS(subdivisionAngle)];
+    }
+    CGContextRestoreGState(context);
+}
+
+/**
+ * scale range labels drawing 
+ */
+- (void)drawRangeLabels:(CGContextRef)context
+{
+    CGContextSaveGState(context);
+    [self rotateContext:context fromCenter:center withAngle:DEGREES_TO_RADIANS(90 + _scaleStartAngle)];
+    CGContextSetShadow(context, CGSizeMake(0.0, 0.0), 0.0);
+    
+    CGFloat maxAngle = _scaleEndAngle - _scaleStartAngle;
+    CGFloat lastStartAngle = 0.0f;
+
+    for (int i = 0; i < _rangeValues.count; i ++)
+    {
+        // Range value
+        float value = ((NSNumber*)[_rangeValues objectAtIndex:i]).floatValue;
+        float valueAngle = (value - _minValue) / (_maxValue - _minValue) * maxAngle;
+        
+        // Range curved shape
+        UIBezierPath *path = [UIBezierPath bezierPath];
+        [path addArcWithCenter:center radius:rangeLabelsRect.size.width / 2.0 startAngle:DEGREES_TO_RADIANS(lastStartAngle) endAngle:DEGREES_TO_RADIANS(valueAngle) - 0.01 clockwise:YES];
+        
+        UIColor *color = _rangeColors[i];
+        [color setStroke];
+        path.lineWidth = _rangeLabelsWidth;
+        [path stroke];
+        
+        // Range curved label
+        [self drawStringAtContext:context string:(NSString*)_rangeLabels[i] withCenter:center radius:rangeLabelsRect.size.width / 2.0 startAngle:DEGREES_TO_RADIANS(lastStartAngle) endAngle:DEGREES_TO_RADIANS(valueAngle)];
+        
+        lastStartAngle = valueAngle;
+    }
+    
+    CGContextRestoreGState(context);
+}
+
+/**
+ * Needle drawing 
+ */
+- (void)drawNeedle
+{
+    if ([_style conformsToProtocol:@protocol(WMGaugeViewStyle)]) {
+        [_style drawNeedleOnLayer:rootNeedleLayer inRect:self.bounds];
+    }
+}
+
+#pragma mark - Tools
+
+/**
+ * Core Graphics rotation in context
+ */
+- (void)rotateContext:(CGContextRef)context fromCenter:(CGPoint)center_ withAngle:(CGFloat)angle
+{
+    CGContextTranslateCTM(context, center_.x, center_.y);
+    CGContextRotateCTM(context, angle);
+    CGContextTranslateCTM(context, -center_.x, -center_.y);
+}
+
+/**
+ * Needle angle computation
+ */
+- (CGFloat)needleAngleForValue:(double)value
+{
+    return DEGREES_TO_RADIANS(_scaleStartAngle + (value - _minValue) / (_maxValue - _minValue) * (_scaleEndAngle - _scaleStartAngle)) + M_PI;
+}
+
+/**
+ * Initialize scale helper values
+ */
+- (void)initScale
+{
+    scaleRotation = (int)(_scaleStartAngle + 180) % 360;
+    divisionValue = (_maxValue - _minValue) / _scaleDivisions;
+    subdivisionValue = divisionValue / _scaleSubdivisions;
+    subdivisionAngle = (_scaleEndAngle - _scaleStartAngle) / (_scaleDivisions * _scaleSubdivisions);
+}
+
+/**
+ * Scale tick value computation
+ */
+- (float)valueForTick:(int)tick
+{
+    return tick * (divisionValue / _scaleSubdivisions) + _minValue;
+}
+
+/**
+ * Scale range label color for value
+ */
+- (UIColor*)rangeColorForValue:(float)value
+{
+    NSInteger length = _rangeValues.count;
+    for (int i = 0; i < length - 1; i++)
+    {
+        if (value < [_rangeValues[i] floatValue])
+            return _rangeColors[i];
+    }
+    if (value <= [_rangeValues[length - 1] floatValue])
+        return _rangeColors[length - 1];
+    return nil;
+}
+
+/**
+ * Draw curved NSSring in context
+ */
+- (void)drawStringAtContext:(CGContextRef) context string:(NSString*)text withCenter:(CGPoint)center_ radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle
+{
+    CGContextSaveGState(context);
+    
+    UIFont* font = _rangeLabelsFont ? _rangeLabelsFont : [UIFont fontWithName:@"Helvetica" size:0.05];
+    UIColor* color = _rangeLabelsFontColor ? _rangeLabelsFontColor : [UIColor whiteColor];
+
+    //add extra character to ensure there is some padding
+    NSString* testText = [text stringByAppendingString:@"$"];
+    while(_adjustRangeLabelSizeToFitWidth) {
+    
+        CGFloat maxWidth = radius * (endAngle - startAngle);
+        CGSize textSize = [testText boundingRectWithSize:CGSizeMake(maxWidth, CGFLOAT_MAX)
+                                      options:NSStringDrawingUsesLineFragmentOrigin
+                                   attributes:@{NSFontAttributeName: font}
+                                       context:nil].size;
+
+        if(textSize.height < _rangeLabelsWidth) {
+            break;
+        }
+        
+        font = [font fontWithSize:font.pointSize * 0.9];
+    }
+    
+    NSDictionary* stringAttrs = @{ NSFontAttributeName : font, NSForegroundColorAttributeName : color };
+    CGSize textSize = [text sizeWithAttributes:stringAttrs];
+    
+    float perimeter = 2 * M_PI * radius;
+    float textAngle = textSize.width / perimeter * 2 * M_PI * _rangeLabelsFontKerning;
+    float offset = ((endAngle - startAngle) - textAngle) / 2.0;
+
+    float letterPosition = 0;
+    NSString *lastLetter = @"";
+    
+    [self rotateContext:context fromCenter:center withAngle:startAngle + offset];
+    for (int index = 0; index < [text length]; index++)
+    {
+        NSRange range = {index, 1};
+        NSString* letter = [text substringWithRange:range];
+        NSAttributedString* attrStr = [[NSAttributedString alloc] initWithString:letter attributes:stringAttrs];
+        CGSize charSize = [letter sizeWithAttributes:stringAttrs];
+        float totalWidth = [[NSString stringWithFormat:@"%@%@",lastLetter, letter] sizeWithAttributes:stringAttrs].width;
+        float currentLetterWidth = [letter sizeWithAttributes:stringAttrs].width;
+        float lastLetterWidth = [lastLetter sizeWithAttributes:stringAttrs].width;
+        float kerning = (lastLetterWidth) ? 0.0 : ((currentLetterWidth + lastLetterWidth) - totalWidth);
+        
+        letterPosition += (charSize.width / 2) - kerning;
+        float angle = (letterPosition / perimeter * 2 * M_PI) * _rangeLabelsFontKerning;
+        CGPoint letterPoint = CGPointMake((radius - charSize.height / 2.0) * cos(angle) + center_.x, (radius - charSize.height / 2.0) * sin(angle) + center_.y);
+        
+        CGContextSaveGState(context);
+        CGContextTranslateCTM(context, letterPoint.x, letterPoint.y);
+        CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(angle + M_PI_2);
+        CGContextConcatCTM(context, rotationTransform);
+        CGContextTranslateCTM(context, -letterPoint.x, -letterPoint.y);
+
+        [attrStr drawAtPoint:CGPointMake(letterPoint.x - charSize.width/2 , letterPoint.y - charSize.height)];
+        
+        CGContextRestoreGState(context);
+        
+        letterPosition += charSize.width / 2;
+        lastLetter = letter;
+    }
+    CGContextRestoreGState(context);
+}
+
+/**
+ * Invalidate background
+ * Background will be regenerated during next draw rect
+ */
+- (void)invalidateBackground
+{
+    background = nil;
+    [self initDrawingRects];
+    [self initScale];
+    [self setNeedsDisplay];
+}
+
+
+/**
+ * Invalidate Needle
+ * Needle will be regenerated during next draw rect
+ */
+- (void)invalidateNeedle
+{
+    [rootNeedleLayer removeAllAnimations];
+    rootNeedleLayer.sublayers = nil;
+    rootNeedleLayer = nil;
+    
+    [self setNeedsDisplay];
+}
+
+#pragma mark - Value update
+
+/**
+ * Update gauge value
+ */
+- (void)updateValue:(float)value
+{
+    // Clamp value if out of range
+    if (value > _maxValue)
+        value = _maxValue;
+    else if (value < _minValue)
+        value = _minValue;
+    else
+        value = value;
+    
+    // Set value
+    _value = value;
+}
+
+/**
+ * Update gauge value with animation
+ */
+- (void)setValue:(float)value animated:(BOOL)animated
+{
+    [self setValue:value animated:animated duration:0.8];
+}
+
+/**
+ * Update gauge value with animation and fire a completion block
+ */
+- (void)setValue:(float)value animated:(BOOL)animated completion:(void (^)(BOOL finished))completion
+{
+    [self setValue:value animated:animated duration:0.8 completion:completion];
+}
+
+/**
+ * Update gauge value with animation and duration
+ */
+- (void)setValue:(float)value animated:(BOOL)animated duration:(NSTimeInterval)duration
+{
+    [self setValue:value animated:animated duration:duration completion:nil];
+}
+
+/**
+ * Update gauge value with animation, duration and fire a completion block
+ */
+- (void)setValue:(float)value animated:(BOOL)animated duration:(NSTimeInterval)duration completion:(void (^)(BOOL finished))completion
+{
+    animationCompletion = completion;
+    
+    double lastValue = _value;
+    
+    [self updateValue:value];
+    double middleValue = lastValue + (((lastValue + (_value - lastValue) / 2.0) >= 0) ? (_value - lastValue) / 2.0 : (lastValue - _value) / 2.0);
+    
+    // Needle animation to target value
+    // An intermediate "middle" value is used to make sure the needle will follow the right rotation direction
+    
+    CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
+    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+    animation.removedOnCompletion = YES;
+    animation.duration = animated ? duration : 0.0;
+    animation.delegate = (id<CAAnimationDelegate>)self;
+    animation.values = @[[NSValue valueWithCATransform3D:CATransform3DMakeRotation([self needleAngleForValue:lastValue]  , 0, 0, 1.0)],
+                         [NSValue valueWithCATransform3D:CATransform3DMakeRotation([self needleAngleForValue:middleValue], 0, 0, 1.0)],
+                         [NSValue valueWithCATransform3D:CATransform3DMakeRotation([self needleAngleForValue:_value]     , 0, 0, 1.0)]];
+    
+    if ([_style conformsToProtocol:@protocol(WMGaugeViewStyle)] == NO || [_style needleLayer:rootNeedleLayer willMoveAnimated:animated duration:duration animation:animation] == NO)
+    {
+        rootNeedleLayer.transform = [[animation.values lastObject] CATransform3DValue];
+        [rootNeedleLayer addAnimation:animation forKey:kCATransition];
+    }
+}
+
+#pragma mark - CAAnimation delegate
+
+- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
+{
+    if (animationCompletion)
+        animationCompletion(flag);
+    
+    animationCompletion = nil;
+}
+
+#pragma mark - Properties
+
+- (void)setValue:(float)value
+{
+    [self setValue:value animated:YES];
+}
+
+- (void)setShowInnerBackground:(bool)showInnerBackground
+{
+    _showInnerBackground = showInnerBackground;
+    [self invalidateBackground];
+}
+
+- (void)setShowInnerRim:(bool)showInnerRim
+{
+    _showInnerRim = showInnerRim;
+    [self invalidateBackground];
+}
+
+- (void)setInnerRimWidth:(CGFloat)innerRimWidth
+{
+    _innerRimWidth = innerRimWidth;
+    [self invalidateBackground];
+}
+
+- (void)setInnerRimBordeWidth:(CGFloat)innerRimBorderWidth
+{
+    _innerRimBorderWidth = innerRimBorderWidth;
+    [self invalidateBackground];
+}
+
+- (void)setScalePosition:(CGFloat)scalePosition
+{
+    _scalePosition = scalePosition;
+    [self invalidateBackground];
+}
+
+- (void)setScaleStartAngle:(CGFloat)scaleStartAngle
+{
+    _scaleStartAngle = scaleStartAngle;
+    [self invalidateBackground];
+}
+
+- (void)setScaleEndAngle:(CGFloat)scaleEndAngle
+{
+    _scaleEndAngle = scaleEndAngle;
+    [self invalidateBackground];
+}
+
+- (void)setScaleDivisions:(CGFloat)scaleDivisions
+{
+    _scaleDivisions = scaleDivisions;
+    [self invalidateBackground];
+}
+
+- (void)setScaleSubdivisions:(CGFloat)scaleSubdivisions
+{
+    _scaleSubdivisions = scaleSubdivisions;
+    [self invalidateBackground];
+}
+
+- (void)setShowScaleShadow:(bool)showScaleShadow
+{
+    _showScaleShadow = showScaleShadow;
+    [self invalidateBackground];
+}
+
+- (void)setShowScale:(bool)showScale
+{
+    _showScale = showScale;
+    [self invalidateBackground];
+}
+
+-(void)setShowScaleValues:(bool)showScaleValues {
+    _showScaleValues = showScaleValues;
+    [self invalidateBackground];
+}
+
+- (void)setScalesubdivisionsAligment:(WMGaugeViewSubdivisionsAlignment)scalesubdivisionsAligment
+{
+    _scalesubdivisionsAligment = scalesubdivisionsAligment;
+    [self invalidateBackground];
+}
+
+- (void)setScaleDivisionsLength:(CGFloat)scaleDivisionsLength
+{
+    _scaleDivisionsLength = scaleDivisionsLength;
+    [self invalidateBackground];
+}
+
+- (void)setScaleDivisionsWidth:(CGFloat)scaleDivisionsWidth
+{
+    _scaleDivisionsWidth = scaleDivisionsWidth;
+    [self invalidateBackground];
+}
+
+- (void)setScaleSubdivisionsLength:(CGFloat)scaleSubdivisionsLength
+{
+    _scaleSubdivisionsLength = scaleSubdivisionsLength;
+    [self invalidateBackground];
+}
+
+- (void)setScaleSubdivisionsWidth:(CGFloat)scaleSubdivisionsWidth
+{
+    _scaleSubdivisionsWidth = scaleSubdivisionsWidth;
+    [self invalidateBackground];
+}
+
+- (void)setScaleDivisionColor:(UIColor *)scaleDivisionColor
+{
+    _scaleDivisionColor = scaleDivisionColor;
+    [self invalidateBackground];
+}
+
+- (void)setScaleSubDivisionColor:(UIColor *)scaleSubDivisionColor
+{
+    _scaleSubDivisionColor = scaleSubDivisionColor;
+    [self invalidateBackground];
+}
+
+- (void)setScaleFont:(UIFont *)scaleFont
+{
+    _scaleFont = scaleFont;
+    [self invalidateBackground];
+}
+
+- (void)setRangeLabelsWidth:(CGFloat)rangeLabelsWidth
+{
+    _rangeLabelsWidth = rangeLabelsWidth;
+    [self invalidateBackground];
+}
+
+- (void)setMinValue:(float)minValue
+{
+    _minValue = minValue;
+    [self invalidateBackground];
+}
+
+- (void)setMaxValue:(float)maxValue
+{
+    _maxValue = maxValue;
+    [self invalidateBackground];
+}
+
+- (void)setRangeValues:(NSArray *)rangeValues
+{
+    _rangeValues = rangeValues;
+    [self invalidateBackground];
+}
+
+- (void)setRangeColors:(NSArray *)rangeColors
+{
+    _rangeColors = rangeColors;
+    [self invalidateBackground];
+}
+
+- (void)setRangeLabels:(NSArray *)rangeLabels
+{
+    _rangeLabels = rangeLabels;
+    [self invalidateBackground];
+}
+
+- (void)setUnitOfMeasurement:(NSString *)unitOfMeasurement
+{
+    _unitOfMeasurement = unitOfMeasurement;
+    [self invalidateBackground];
+}
+
+- (void)setShowUnitOfMeasurement:(bool)showUnitOfMeasurement
+{
+    _showUnitOfMeasurement = showUnitOfMeasurement;
+    [self invalidateBackground];
+}
+
+- (void)setShowRangeLabels:(bool)showRangeLabels
+{
+    _showRangeLabels = showRangeLabels;
+    [self invalidateBackground];
+}
+
+- (void)setRangeLabelsFontKerning:(CGFloat)rangeLabelsFontKerning
+{
+    _rangeLabelsFontKerning = rangeLabelsFontKerning;
+    [self invalidateBackground];
+}
+
+- (void)setUnitOfMeasurementFont:(UIFont *)unitOfMeasurementFont
+{
+    _unitOfMeasurementFont = unitOfMeasurementFont;
+    [self invalidateBackground];
+}
+
+- (void)setRangeLabelsFont:(UIFont *)rangeLabelsFont
+{
+    _rangeLabelsFont = rangeLabelsFont;
+    [self invalidateBackground];
+}
+
+- (void)setUnitOfMeasurementVerticalOffset:(CGFloat)unitOfMeasurementVerticalOffset
+{
+    _unitOfMeasurementVerticalOffset = unitOfMeasurementVerticalOffset;
+    [self invalidateBackground];
+}
+
+- (void)setUnitOfMeasurementColor:(UIColor *)unitOfMeasurementColor
+{
+    _unitOfMeasurementColor = unitOfMeasurementColor;
+    [self invalidateBackground];
+}
+
+- (void)setRangeLabelsFontColor:(UIColor *)rangeLabelsFontColor
+{
+    _rangeLabelsFontColor = rangeLabelsFontColor;
+    [self invalidateBackground];
+}
+
+- (void)setStyle:(id<WMGaugeViewStyle>)style {
+    _style = style;
+    [self invalidateBackground];
+    [self invalidateNeedle];
+}
+
+@end

+ 58 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeViewStyle.h

@@ -0,0 +1,58 @@
+/*
+ * WMGaugeViewStyle.h
+ *
+ * Copyright (C) 2015 William Markezana <william.markezana@me.com>
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+/* Degrees to radians conversion macro */
+#define DEGREES_TO_RADIANS(degrees) (degrees) / 180.0 * M_PI
+
+/* Colors creation macros */
+#define RGB(r,g,b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0]
+#define RGBA(r,g,b,a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a/255.0]
+#define CGRGB(r,g,b) RGB(r,g,b).CGColor
+#define iCGRGB(r,g,b) (id)CGRGB(r,g,b)
+#define CGRGBA(r,g,b,a) RGBA(r,g,b,a).CGColor
+#define iCGRGBA(r,g,b,a) (id)CGRGBA(r,g,b,a)
+
+static inline
+NSArray* CGColorArray(NSArray<UIColor*>*c) {
+    NSMutableArray* _c = [NSMutableArray array];
+    for(UIColor* col in c) {
+        [_c addObject: (id)col.CGColor];
+    }
+    return _c.copy;
+}
+
+static inline
+CGFloat* CGFloatCArray(NSArray<NSNumber*>*a) {
+    CGFloat* ar = malloc(sizeof(*ar) * a.count);
+    [a enumerateObjectsUsingBlock:^(NSNumber* obj, NSUInteger idx, BOOL * _Nonnull stop) {
+        ar[idx] = (CGFloat)obj.doubleValue;
+    }];
+    return ar;
+}
+
+/* Position macros */
+#define FULL_RECT           CGRectMake(0.0, 0.0, 1.0, 1.0)
+#define kCenterX            0.5
+#define kCenterY            0.5
+#define kCenterPoint        CGPointMake(kCenterX, kCenterY)
+
+/* Scale conversion macro from [0-1] range to view  real size range */
+#define FULLSCALE(x,y)    (x)*rect.size.width, (y)*rect.size.height
+
+/**
+ * WMGaugeView style protocol
+ */
+@protocol WMGaugeViewStyle <NSObject>
+@required
+- (void)drawNeedleOnLayer:(CALayer*)layer inRect:(CGRect)rect;
+- (void)drawFaceWithContext:(CGContextRef)context inRect:(CGRect)rect;
+- (BOOL)needleLayer:(CALayer*)layer willMoveAnimated:(BOOL)animated duration:(NSTimeInterval)duration animation:(CAKeyframeAnimation*)animation;
+
+@end

+ 32 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeViewStyle3D.h

@@ -0,0 +1,32 @@
+/*
+ * WMGaugeViewStyle3D.h
+ *
+ * Copyright (C) 2015 William Markezana <william.markezana@me.com>
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "WMGaugeViewStyle.h"
+
+@interface WMGaugeViewStyle3D : NSObject<WMGaugeViewStyle>
+
+@property (nonatomic) CGFloat needleWidth;
+@property (nonatomic) CGFloat needleHeight;
+@property (nonatomic) CGFloat needleScrewRadius;
+
+@property (nonatomic) UIColor* leftNeedleColor;
+@property (nonatomic) UIColor* rightNeedleColor;
+
+@property (nonatomic) UIColor* screwFillColor;
+@property (nonatomic) UIColor* screwStrokeColor;
+
+@property (nonatomic) UIColor* borderColor;
+@property (nonatomic) CGFloat borderWidth;
+
+@property (nonatomic) NSArray<UIColor*>* faceGradientColors;
+@property (nonatomic) NSArray<NSNumber*>* faceGradientPositions;
+@property (nonatomic) NSArray<UIColor*>* faceShadowColors;
+@property (nonatomic) NSArray<NSNumber*>* faceShadowPositions;
+
+@end
+

+ 131 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeViewStyle3D.m

@@ -0,0 +1,131 @@
+//
+//  WMGaugeViewStyleFlat3D.m
+//  WMGaugeView
+//
+//  Created by Markezana, William on 25/10/15.
+//  Copyright © 2015 Will™. All rights reserved.
+//
+
+#import "WMGaugeViewStyle3D.h"
+
+@implementation WMGaugeViewStyle3D
+
+- (instancetype)init
+{
+    if (self = [super init]) {
+        _needleWidth = 0.035;
+        _needleHeight = 0.34;
+        _needleScrewRadius = 0.04;
+        
+        _leftNeedleColor = RGB(176, 10, 19);
+        _rightNeedleColor = RGB(252, 18, 30);
+        
+        _screwFillColor = RGB(171, 171, 171);
+        _screwStrokeColor = RGBA(81, 84, 89, 100);
+        
+        _borderColor = RGBA(81, 84, 89, 160);
+        _borderWidth = 0.005;
+        
+        _faceGradientColors = @[RGB(96, 96, 96), RGB(68, 68, 68), RGB(32, 32, 32)];
+        _faceGradientPositions = @[@0.35, @0.96, @0.99];
+        _faceShadowColors = @[RGBA(40, 96, 170, 60), RGBA(15, 34, 98, 80), RGBA(0, 0, 0, 120), RGBA(0, 0, 0, 140)];
+        _faceShadowPositions = @[@0.60, @0.85, @0.96, @0.99];
+    }
+    return self;
+}
+
+- (void)drawNeedleOnLayer:(CALayer*)layer inRect:(CGRect)rect
+{
+    // Left Needle
+    CAShapeLayer *leftNeedleLayer = [CAShapeLayer layer];
+    UIBezierPath *leftNeedlePath = [UIBezierPath bezierPath];
+    [leftNeedlePath moveToPoint:CGPointMake(FULLSCALE(kCenterX, kCenterY))];
+    [leftNeedlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX - _needleWidth, kCenterY))];
+    [leftNeedlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX, kCenterY - _needleHeight))];
+    [leftNeedlePath closePath];
+    
+    leftNeedleLayer.path = leftNeedlePath.CGPath;
+    leftNeedleLayer.backgroundColor = [[UIColor clearColor] CGColor];
+    leftNeedleLayer.fillColor = _leftNeedleColor.CGColor;
+    
+    [layer addSublayer:leftNeedleLayer];
+    
+    // Right Needle
+    CAShapeLayer *rightNeedleLayer = [CAShapeLayer layer];
+    UIBezierPath *rightNeedlePath = [UIBezierPath bezierPath];
+    [rightNeedlePath moveToPoint:CGPointMake(FULLSCALE(kCenterX, kCenterY))];
+    [rightNeedlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX + _needleWidth, kCenterY))];
+    [rightNeedlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX, kCenterY - _needleHeight))];
+    [rightNeedlePath closePath];
+    
+    rightNeedleLayer.path = rightNeedlePath.CGPath;
+    rightNeedleLayer.backgroundColor = [[UIColor clearColor] CGColor];
+    rightNeedleLayer.fillColor = _rightNeedleColor.CGColor;
+    
+    [layer addSublayer:rightNeedleLayer];
+    
+    // Needle shadow
+    [layer setShadowColor:[[UIColor blackColor] CGColor]];
+    [layer setShadowOffset:CGSizeMake(0, 0)];
+    [layer setShadowOpacity:0.5];
+    [layer setShadowRadius:2.0];
+    
+    // Screw drawing
+    CAShapeLayer *screwLayer = [CAShapeLayer layer];
+    screwLayer.bounds = CGRectMake(FULLSCALE(kCenterX - _needleScrewRadius, kCenterY - _needleScrewRadius), FULLSCALE(_needleScrewRadius * 2.0, _needleScrewRadius * 2.0));
+    screwLayer.position = CGPointMake(FULLSCALE(kCenterX, kCenterY));
+    screwLayer.path = [UIBezierPath bezierPathWithOvalInRect:screwLayer.bounds].CGPath;
+    screwLayer.fillColor = _screwFillColor.CGColor;
+    screwLayer.strokeColor = _screwStrokeColor.CGColor;
+    screwLayer.lineWidth = 1.5;
+    
+    // Screw shadow
+    screwLayer.shadowColor = [[UIColor blackColor] CGColor];
+    screwLayer.shadowOffset = CGSizeMake(0.0, 0.0);
+    screwLayer.shadowOpacity = 0.1;
+    screwLayer.shadowRadius = 2.0;
+    
+    [layer addSublayer:screwLayer];
+}
+
+- (void)drawFaceWithContext:(CGContextRef)context inRect:(CGRect)rect
+{
+    // Default Face
+    NSAssert(_faceGradientPositions.count == _faceGradientColors.count, @"Must have same amout of colors as positions");
+    CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
+    CGFloat* positions = CGFloatCArray(_faceGradientPositions);
+    CGGradientRef gradient = CGGradientCreateWithColors(baseSpace, (CFArrayRef)CGColorArray(_faceGradientColors), positions);
+    CGColorSpaceRelease(baseSpace);
+    baseSpace = NULL;
+    CGContextAddEllipseInRect(context, rect);
+    CGContextClip(context);
+    CGContextDrawRadialGradient(context, gradient, kCenterPoint, 0, kCenterPoint, rect.size.width / 2.0, kCGGradientDrawsAfterEndLocation);
+    CGGradientRelease(gradient);
+    gradient = NULL;
+    free(positions); positions = NULL;
+    
+    // Shadow
+    NSAssert(_faceShadowPositions.count == _faceShadowColors.count, @"Must have same amount of colors as positions");
+    baseSpace = CGColorSpaceCreateDeviceRGB();
+    positions = CGFloatCArray(_faceShadowPositions);
+    gradient = CGGradientCreateWithColors(baseSpace, (CFArrayRef)CGColorArray(_faceShadowColors), positions);
+    CGColorSpaceRelease(baseSpace); baseSpace = NULL;
+    CGContextAddEllipseInRect(context, rect);
+    CGContextClip(context);
+    CGContextDrawRadialGradient(context, gradient, kCenterPoint, 0, kCenterPoint, rect.size.width / 2.0, kCGGradientDrawsAfterEndLocation);
+    CGGradientRelease(gradient); gradient = NULL;
+    free(positions); positions = NULL;
+    
+    // Border
+    CGContextSetLineWidth(context, _borderWidth);
+    CGContextSetStrokeColorWithColor(context, _borderColor.CGColor);
+    CGContextAddEllipseInRect(context, rect);
+    CGContextStrokePath(context);
+}
+
+- (BOOL)needleLayer:(CALayer*)layer willMoveAnimated:(BOOL)animated duration:(NSTimeInterval)duration animation:(CAKeyframeAnimation*)animation
+{
+    return NO;
+}
+
+@end

+ 29 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeViewStyleFlatThin.h

@@ -0,0 +1,29 @@
+/*
+ * WMGaugeViewStyleFlatThin.h
+ *
+ * Copyright (C) 2015 William Markezana <william.markezana@me.com>
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "WMGaugeViewStyle.h"
+
+@interface WMGaugeViewStyleFlatThin : NSObject<WMGaugeViewStyle>
+
+@property (nonatomic) CGFloat needleWidth;
+@property (nonatomic) CGFloat needleHeight;
+@property (nonatomic) CGFloat needleScrewRadius;
+
+@property (nonatomic) UIColor* needleColor;
+@property (nonatomic) UIColor* needleScrewColor;
+
+@property (nonatomic) CGFloat externalRingRadius;
+@property (nonatomic) CGFloat internalRingRadius;
+
+@property (nonatomic) UIColor* externalFaceColor;
+@property (nonatomic) UIColor* internalFaceColor;
+
+@property (nonatomic) UIColor* borderColor;
+@property (nonatomic) CGFloat borderWidth;
+
+@end

+ 128 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/WMGaugeView/WMGaugeViewStyleFlatThin.m

@@ -0,0 +1,128 @@
+//
+//  WMGaugeViewStyleFlatThin.m
+//  WMGaugeView
+//
+//  Created by Markezana, William on 25/10/15.
+//  Copyright © 2015 Will™. All rights reserved.
+//
+
+#import "WMGaugeViewStyleFlatThin.h"
+
+@interface WMGaugeViewStyleFlatThin ()
+
+@property (nonatomic) CAShapeLayer *needleLayer;
+
+@end
+
+@implementation WMGaugeViewStyleFlatThin
+
+- (instancetype)init
+{
+    if (self = [super init]) {
+        _needleWidth = 0.012;
+        _needleHeight = 0.4;
+        _needleScrewRadius = 0.05;
+//        _needleColor = RGB(255, 104, 97);//橘红色
+        _needleColor = [[UIColor redColor] colorWithAlphaComponent:0.2];
+//        _needleScrewColor = RGB(68, 84, 105);//蓝紫灰色
+        _needleScrewColor = [UIColor systemTealColor];
+        
+        _externalRingRadius = 0.24;
+//        _externalFaceColor = RGB(255, 104, 97);
+        _externalFaceColor = [[UIColor redColor] colorWithAlphaComponent:0.2];
+
+        _internalRingRadius = 0.1;
+//        _internalFaceColor = RGB(242, 99, 92);//亮橘红色
+        _internalFaceColor = [[UIColor yellowColor] colorWithAlphaComponent:0.2];
+        
+//        _borderColor = RGBA(81, 84, 89, 160);//火山灰色
+        _borderColor = [UIColor blackColor];
+        _borderWidth = 0;
+    }
+    return self;
+}
+
+- (void)drawNeedleOnLayer:(CALayer*)layer inRect:(CGRect)rect
+{
+    _needleLayer = [CAShapeLayer layer];
+    UIBezierPath *needlePath = [UIBezierPath bezierPath];
+    [needlePath moveToPoint:CGPointMake(FULLSCALE(kCenterX - _needleWidth, kCenterY))];
+    [needlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX + _needleWidth, kCenterY))];
+    [needlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX, kCenterY - _needleHeight))];
+    [needlePath closePath];
+    
+    _needleLayer.path = needlePath.CGPath;
+    _needleLayer.backgroundColor = [[UIColor clearColor] CGColor];
+    _needleLayer.fillColor = _needleColor.CGColor;
+    _needleLayer.strokeColor = _needleColor.CGColor;
+    _needleLayer.lineWidth = 1.2;
+    
+    // Needle shadow
+    _needleLayer.shadowColor = [[UIColor blackColor] CGColor];
+    _needleLayer.shadowOffset = CGSizeMake(-2.0, -2.0);
+    _needleLayer.shadowOpacity = 0.2;
+    _needleLayer.shadowRadius = 1.2;
+    
+    [layer addSublayer:_needleLayer];
+    
+    // Screw drawing
+    CAShapeLayer *screwLayer = [CAShapeLayer layer];
+    screwLayer.bounds = CGRectMake(FULLSCALE(kCenterX - _needleScrewRadius, kCenterY - _needleScrewRadius), FULLSCALE(_needleScrewRadius * 2.0, _needleScrewRadius * 2.0));
+    screwLayer.position = CGPointMake(FULLSCALE(kCenterX, kCenterY));
+    screwLayer.path = [UIBezierPath bezierPathWithOvalInRect:screwLayer.bounds].CGPath;
+    screwLayer.fillColor = _needleScrewColor.CGColor;
+    
+    // Screw shadow
+    screwLayer.shadowColor = [[UIColor blackColor] CGColor];
+    screwLayer.shadowOffset = CGSizeMake(0.0, 0.0);
+    screwLayer.shadowOpacity = 0.2;
+    screwLayer.shadowRadius = 2.0;
+    
+    [layer addSublayer:screwLayer];
+}
+
+- (void)drawFaceWithContext:(CGContextRef)context inRect:(CGRect)rect
+{
+    // External circle
+    CGRect externalRect = CGRectMake(kCenterX - _externalRingRadius, kCenterY - _externalRingRadius, _externalRingRadius * 2.0, _externalRingRadius * 2.0);
+    CGContextSetFillColorWithColor(context, _externalFaceColor.CGColor);
+    CGContextFillEllipseInRect(context, externalRect);
+
+    // Inner circle
+    CGRect internalRect = CGRectMake(kCenterX - _internalRingRadius, kCenterY - _internalRingRadius, _internalRingRadius * 2.0, _internalRingRadius * 2.0);
+    CGContextSetFillColorWithColor(context, _internalFaceColor.CGColor);
+    CGContextFillEllipseInRect(context, internalRect);
+    
+    // Border
+    CGRect borderRect = CGRectInset(rect, _borderWidth * 0.5, _borderWidth * 0.5);
+    CGContextSetLineWidth(context, _borderWidth);
+    CGContextSetStrokeColorWithColor(context, _borderColor.CGColor);
+    CGContextStrokeEllipseInRect(context, borderRect);
+}
+
+- (BOOL)needleLayer:(CALayer*)layer willMoveAnimated:(BOOL)animated duration:(NSTimeInterval)duration animation:(CAKeyframeAnimation*)animation
+{
+    layer.transform = [[animation.values objectAtIndex:0] CATransform3DValue];
+    CGAffineTransform affineTransform1 = [layer affineTransform];
+    layer.transform = [[animation.values objectAtIndex:1] CATransform3DValue];
+    CGAffineTransform affineTransform2 = [layer affineTransform];
+    layer.transform = [[animation.values lastObject] CATransform3DValue];
+    CGAffineTransform affineTransform3 = [layer affineTransform];
+    
+    _needleLayer.shadowOffset = CGSizeApplyAffineTransform(CGSizeMake(-2.0, -2.0), affineTransform3);
+    
+    [layer addAnimation:animation forKey:kCATransition];
+    
+    CAKeyframeAnimation * animationShadowOffset = [CAKeyframeAnimation animationWithKeyPath:@"shadowOffset"];
+    animationShadowOffset.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+    animationShadowOffset.removedOnCompletion = YES;
+    animationShadowOffset.duration = animated ? duration : 0.0;
+    animationShadowOffset.values = @[[NSValue valueWithCGSize:CGSizeApplyAffineTransform(CGSizeMake(-2.0, -2.0), affineTransform1)],
+                                     [NSValue valueWithCGSize:CGSizeApplyAffineTransform(CGSizeMake(-2.0, -2.0), affineTransform2)],
+                                     [NSValue valueWithCGSize:CGSizeApplyAffineTransform(CGSizeMake(-2.0, -2.0), affineTransform3)]];
+    [_needleLayer addAnimation:animationShadowOffset forKey:kCATransition];
+    
+    return YES;
+}
+
+@end

+ 19 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/DialPlateView.h

@@ -0,0 +1,19 @@
+//
+//  DialPlateView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/14.
+//
+
+#import <UIKit/UIKit.h>
+#import "WMGaugeView.h"
+NS_ASSUME_NONNULL_BEGIN
+
+/// 表盘View
+@interface DialPlateView : UIView
+
+@property (nonatomic, strong) WMGaugeView *gaugeView;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 65 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/DialPlateView.m

@@ -0,0 +1,65 @@
+//
+//  DialPlateView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/14.
+//
+
+#import "DialPlateView.h"
+#import "WMGaugeViewStyleFlatThin.h"
+
+@interface DialPlateView ()
+
+
+@end
+
+@implementation DialPlateView
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    if (self) {
+        [self configUI];
+    }
+    return self;
+}
+
+- (void)configUI {
+    self.backgroundColor = [UIColor clearColor];
+    [self addSubview:self.gaugeView];
+    [self.gaugeView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.top.bottom.right.mas_equalTo(self);
+    }];
+    
+}
+
+- (WMGaugeView *)gaugeView {
+    if (!_gaugeView) {
+        _gaugeView = [[WMGaugeView alloc] init];
+        _gaugeView.style = [[WMGaugeViewStyleFlatThin alloc] init];
+        _gaugeView.maxValue = 50.0f;
+        _gaugeView.minValue = -50.0f;
+        _gaugeView.scaleDivisions = 10;
+        _gaugeView.scaleEndAngle = 270;
+        _gaugeView.scaleStartAngle = 90;
+        _gaugeView.scaleSubdivisions = 5;
+        _gaugeView.showScaleShadow = NO;
+        _gaugeView.scaleDivisionsLength = 0.05;
+        _gaugeView.scaleDivisionsWidth = 0.007;
+        _gaugeView.scaleSubdivisionsLength = 0.02;
+        _gaugeView.scaleSubdivisionsWidth = 0.002;
+        _gaugeView.backgroundColor = [UIColor clearColor];
+        _gaugeView.scaleFont = [UIFont systemFontOfSize:0.05 weight:UIFontWeightUltraLight];
+    }
+    return _gaugeView;
+}
+
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 55 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/ToneTuningBodyView.h

@@ -0,0 +1,55 @@
+//
+//  ToneTuningBodyView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/14.
+//
+
+#import <UIKit/UIKit.h>
+
+#define MAXFREQUENCE (445)
+#define MINFREQUENCE (415)
+
+typedef NS_ENUM(NSInteger, TUNINGACTION) {
+    TUNINGACTION_SETTING,     // 设置
+    TUNINGACTION_FREQUENCE,   // 调节频率
+    TUNINGACTION_FORKSETTING, // 音叉设置
+    TUNINGACTION_PLAY,        // 播放音叉
+    TUNINGACTION_STOP,        // 停止播放
+};
+
+typedef void(^TuningActionCallback)(TUNINGACTION action, NSInteger frequence);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ToneTuningBodyView : UIView
+
+@property (weak, nonatomic) IBOutlet UIView *plateView;
+
+@property (weak, nonatomic) IBOutlet UIView *pitchBgView;
+
+@property (weak, nonatomic) IBOutlet UILabel *nomalPitch;
+
+@property (weak, nonatomic) IBOutlet UIView *transferView;
+
+@property (weak, nonatomic) IBOutlet UILabel *transferNomalPitch;
+
+@property (weak, nonatomic) IBOutlet UILabel *transferPitch;
+
+@property (weak, nonatomic) IBOutlet UILabel *transferDesc;
+
+@property (weak, nonatomic) IBOutlet UILabel *A4FrequenceLabel;
+
+@property (weak, nonatomic) IBOutlet UILabel *pitchFrequenceLabel;
+
+@property (nonatomic, assign) NSInteger A4Frequence;
+
+@property (nonatomic, assign) BOOL isPlaying;
+
++ (instancetype)shareInstance;
+
+- (void)tuningViewAction:(TuningActionCallback)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 127 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/ToneTuningBodyView.m

@@ -0,0 +1,127 @@
+//
+//  ToneTuningBodyView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/14.
+//
+
+#import "ToneTuningBodyView.h"
+
+@interface ToneTuningBodyView ()
+
+@property (weak, nonatomic) IBOutlet UIView *minusView;
+
+@property (weak, nonatomic) IBOutlet UIButton *minusButton;
+
+@property (weak, nonatomic) IBOutlet UIView *addView;
+
+@property (weak, nonatomic) IBOutlet UIButton *addButton;
+
+@property (weak, nonatomic) IBOutlet UIButton *playButton;
+
+@property (nonatomic, copy) TuningActionCallback callback;
+
+@end
+
+@implementation ToneTuningBodyView
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    
+    CAGradientLayer *layer = [self createGradientLayerFromColor:HexRGB(0x323D50) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0x1C2331) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, KPortraitWidth - 28, 170)];
+    layer.cornerRadius = 11.0f;
+    layer.masksToBounds = YES;
+    [self.pitchBgView.layer addSublayer:layer];
+    
+    CAGradientLayer *minusLayer = [self createGradientLayerFromColor:HexRGB(0x3C495F) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0x1C2331) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, 50, 31)];
+    minusLayer.cornerRadius = 15.5f;
+    minusLayer.masksToBounds = YES;
+    [self.minusView.layer addSublayer:minusLayer];
+    
+    CAGradientLayer *addViewLayer = [self createGradientLayerFromColor:HexRGB(0x3C495F) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0x1C2331) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, 50, 31)];
+    addViewLayer.cornerRadius = 15.5f;
+    addViewLayer.masksToBounds = YES;
+    [self.addView.layer addSublayer:addViewLayer];
+    self.transferView.hidden = YES;
+    
+}
+
++ (instancetype)shareInstance {
+    ToneTuningBodyView *view = [[[NSBundle mainBundle] loadNibNamed:@"ToneTuningBodyView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (CAGradientLayer *)createGradientLayerFromColor:(UIColor *)fromColor startPoint:(CGPoint)startPoint endColor:(UIColor *)endColor endPoint:(CGPoint)endPoint bounds:(CGRect)bounds {
+    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
+    gradientLayer.colors = @[(__bridge id)fromColor.CGColor, (__bridge id)endColor.CGColor];
+    gradientLayer.startPoint = startPoint;
+    gradientLayer.endPoint = endPoint;
+    gradientLayer.frame = bounds;
+    gradientLayer.locations = @[@(0),@(1.0f)];
+    return gradientLayer;
+}
+
+
+- (void)tuningViewAction:(TuningActionCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+
+- (IBAction)frequenceMinus:(id)sender {
+    NSInteger frequence = self.A4Frequence;
+    if (self.A4Frequence <= MINFREQUENCE) {
+        return;
+    }
+    self.A4Frequence = frequence - 1;
+    if (self.callback) {
+        self.callback(TUNINGACTION_FREQUENCE, self.A4Frequence);
+    }
+}
+
+- (IBAction)frequenceAdd:(id)sender {
+    NSInteger frequence = self.A4Frequence;
+    if (self.A4Frequence >= MAXFREQUENCE) {
+        return;
+    }
+    self.A4Frequence = frequence + 1;
+    if (self.callback) {
+        self.callback(TUNINGACTION_FREQUENCE, self.A4Frequence);
+    }
+}
+
+
+- (void)setA4Frequence:(NSInteger)A4Frequence {
+    _A4Frequence = A4Frequence;
+    self.A4FrequenceLabel.text = [NSString stringWithFormat:@"A4=%zdHz",A4Frequence];
+}
+
+- (IBAction)forkSetting:(id)sender {
+    if (self.callback) {
+        self.callback(TUNINGACTION_FORKSETTING, self.A4Frequence);
+    }
+}
+
+- (IBAction)playButtonAction:(id)sender {
+    self.isPlaying = !self.isPlaying;
+    TUNINGACTION action = self.isPlaying ? TUNINGACTION_PLAY : TUNINGACTION_STOP;
+    if (self.callback) {
+        self.callback(action, self.A4Frequence);
+    }
+}
+
+- (void)setIsPlaying:(BOOL)isPlaying {
+    _isPlaying = isPlaying;
+    NSString *playImage = isPlaying ? @"fork_play" : @"fork_unPlay";
+    [self.playButton setImage:[UIImage imageNamed:playImage] forState:UIControlStateNormal];
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 390 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/ToneTuningBodyView.xib

@@ -0,0 +1,390 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_0" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="ToneTuningBodyView">
+            <rect key="frame" x="0.0" y="0.0" width="390" height="844"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A4=440Hz" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ux8-2g-RqK">
+                    <rect key="frame" x="22" y="10" width="80" height="22"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="22" id="0p2-aR-QPJ"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" type="system" pointSize="16"/>
+                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tuning_warning" translatesAutoresizingMaskIntoConstraints="NO" id="Sbe-L2-liH">
+                    <rect key="frame" x="107" y="15" width="13" height="12"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="13" id="A7f-bT-aBO"/>
+                        <constraint firstAttribute="height" constant="12" id="gve-gP-2q7"/>
+                    </constraints>
+                </imageView>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="plate_bg" translatesAutoresizingMaskIntoConstraints="NO" id="SXB-cz-Qdh">
+                    <rect key="frame" x="8" y="42" width="374" height="254"/>
+                    <constraints>
+                        <constraint firstAttribute="width" secondItem="SXB-cz-Qdh" secondAttribute="height" multiplier="187:127" id="ixC-MD-R46"/>
+                    </constraints>
+                </imageView>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Zrx-Pe-2NT">
+                    <rect key="frame" x="43.666666666666657" y="88" width="303" height="303"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="303" id="93S-gk-24h"/>
+                        <constraint firstAttribute="height" constant="303" id="rD4-eg-11F"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="g4h-vE-BMO">
+                    <rect key="frame" x="107" y="322.66666666666669" width="50" height="31"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                </view>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Cai-ev-v5E">
+                    <rect key="frame" x="107" y="322.66666666666669" width="50" height="31"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="31" id="Uqc-Hy-CFM"/>
+                        <constraint firstAttribute="width" constant="50" id="aIN-Pe-lUG"/>
+                    </constraints>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="frequence_minus"/>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="size" keyPath="shadowOffset">
+                            <size key="value" width="0.0" height="2"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowOpacity">
+                            <real key="value" value="1"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="color" keyPath="shadowUIColor">
+                            <color key="value" red="0.0" green="0.0" blue="0.0" alpha="0.27000000000000002" colorSpace="custom" customColorSpace="calibratedRGB"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="15.5"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                    <connections>
+                        <action selector="frequenceMinus:" destination="iN0-l3-epB" eventType="touchUpInside" id="BeR-xr-RYR"/>
+                    </connections>
+                </button>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uwR-ra-cDH">
+                    <rect key="frame" x="233" y="322.66666666666669" width="50" height="31"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                </view>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VJU-LE-Pdq">
+                    <rect key="frame" x="233" y="322.66666666666669" width="50" height="31"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="31" id="30u-8l-H8Y"/>
+                        <constraint firstAttribute="width" constant="50" id="nH5-lU-4GN"/>
+                    </constraints>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="freguqnce_add"/>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="size" keyPath="shadowOffset">
+                            <size key="value" width="0.0" height="2"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowOpacity">
+                            <real key="value" value="1"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="15.5"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                    <connections>
+                        <action selector="frequenceAdd:" destination="iN0-l3-epB" eventType="touchUpInside" id="k9j-Rr-TnO"/>
+                    </connections>
+                </button>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="CALIB" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sZj-ZO-VHR">
+                    <rect key="frame" x="174" y="328" width="42" height="20"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="20" id="NtT-NM-EEj"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" name="DINAlternate-Bold" family="DIN Alternate" pointSize="16"/>
+                    <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="calibratedRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7Em-FC-3EL">
+                    <rect key="frame" x="14" y="380" width="362" height="170"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="170" id="z4J-oh-ydh"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="11"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="size" keyPath="shadowOffset">
+                            <size key="value" width="0.0" height="2"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowOpacity">
+                            <real key="value" value="1"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="boolean" keyPath="maskToBounces" value="YES"/>
+                        <userDefinedRuntimeAttribute type="color" keyPath="shadowUIColor">
+                            <color key="value" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="calibratedRGB"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ZeX-qM-LJD">
+                    <rect key="frame" x="14" y="380" width="362" height="170"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A#" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uIc-iL-EXg">
+                            <rect key="frame" x="155" y="56" width="52" height="46"/>
+                            <fontDescription key="fontDescription" type="boldSystem" pointSize="38"/>
+                            <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6FP-hm-dfl">
+                            <rect key="frame" x="11" y="154" width="340" height="5"/>
+                            <subviews>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="i3w-7a-0hL">
+                                    <rect key="frame" x="0.0" y="0.0" width="0.0" height="5"/>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstAttribute="width" id="Ndf-lc-KO2"/>
+                                    </constraints>
+                                </view>
+                            </subviews>
+                            <color key="backgroundColor" red="0.1764705882352941" green="0.21176470588235294" blue="0.28235294117647058" alpha="1" colorSpace="calibratedRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="5" id="3Xi-hl-yD4"/>
+                                <constraint firstAttribute="bottom" secondItem="i3w-7a-0hL" secondAttribute="bottom" id="54u-rc-9uK"/>
+                                <constraint firstItem="i3w-7a-0hL" firstAttribute="top" secondItem="6FP-hm-dfl" secondAttribute="top" id="KB8-r8-QVf"/>
+                                <constraint firstItem="i3w-7a-0hL" firstAttribute="leading" secondItem="6FP-hm-dfl" secondAttribute="leading" id="qxd-Xa-3KK"/>
+                            </constraints>
+                        </view>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ya5-kp-sTW">
+                            <rect key="frame" x="0.0" y="0.0" width="362" height="143"/>
+                            <subviews>
+                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tuning_transfer" translatesAutoresizingMaskIntoConstraints="NO" id="guW-zj-dkh">
+                                    <rect key="frame" x="171" y="80" width="20" height="23"/>
+                                    <constraints>
+                                        <constraint firstAttribute="width" constant="20" id="3Ic-Ab-Zdu"/>
+                                        <constraint firstAttribute="height" constant="23" id="Jad-yu-6YO"/>
+                                    </constraints>
+                                </imageView>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A#" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MNR-s0-re6">
+                                    <rect key="frame" x="68" y="65" width="52" height="53"/>
+                                    <constraints>
+                                        <constraint firstAttribute="height" constant="53" id="rPI-W9-m7n"/>
+                                    </constraints>
+                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="38"/>
+                                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="C#" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vku-Yz-AEN">
+                                    <rect key="frame" x="242" y="65" width="53" height="53"/>
+                                    <constraints>
+                                        <constraint firstAttribute="height" constant="53" id="vCR-NZ-KzZ"/>
+                                    </constraints>
+                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="38"/>
+                                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Concert" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FLn-wI-hyg">
+                                    <rect key="frame" x="71.666666666666671" y="38" width="45" height="17"/>
+                                    <fontDescription key="fontDescription" name="DINAlternate-Bold" family="DIN Alternate" pointSize="14"/>
+                                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="F# ins." textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tD3-d1-rfd">
+                                    <rect key="frame" x="249" y="38" width="39" height="17"/>
+                                    <fontDescription key="fontDescription" name="DINAlternate-Bold" family="DIN Alternate" pointSize="14"/>
+                                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                            </subviews>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <constraints>
+                                <constraint firstItem="MNR-s0-re6" firstAttribute="top" secondItem="FLn-wI-hyg" secondAttribute="bottom" constant="10" id="CiQ-l5-VOE"/>
+                                <constraint firstItem="guW-zj-dkh" firstAttribute="top" secondItem="ya5-kp-sTW" secondAttribute="top" constant="80" id="KZl-F4-IKz"/>
+                                <constraint firstItem="vku-Yz-AEN" firstAttribute="leading" secondItem="guW-zj-dkh" secondAttribute="trailing" constant="51" id="Lab-An-ath"/>
+                                <constraint firstItem="guW-zj-dkh" firstAttribute="centerY" secondItem="MNR-s0-re6" secondAttribute="centerY" id="N1i-3X-n6R"/>
+                                <constraint firstItem="MNR-s0-re6" firstAttribute="centerX" secondItem="FLn-wI-hyg" secondAttribute="centerX" id="Ozw-yh-mZ2"/>
+                                <constraint firstItem="vku-Yz-AEN" firstAttribute="centerY" secondItem="MNR-s0-re6" secondAttribute="centerY" id="Sxq-0v-HVY"/>
+                                <constraint firstItem="guW-zj-dkh" firstAttribute="centerX" secondItem="ya5-kp-sTW" secondAttribute="centerX" id="U6o-GX-a5D"/>
+                                <constraint firstItem="vku-Yz-AEN" firstAttribute="top" secondItem="tD3-d1-rfd" secondAttribute="bottom" constant="10" id="tg6-Gb-dMJ"/>
+                                <constraint firstItem="vku-Yz-AEN" firstAttribute="centerX" secondItem="tD3-d1-rfd" secondAttribute="centerX" id="wTA-Ek-KF6"/>
+                                <constraint firstItem="guW-zj-dkh" firstAttribute="leading" secondItem="MNR-s0-re6" secondAttribute="trailing" constant="51" id="x7v-5G-Dlt"/>
+                            </constraints>
+                        </view>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="uIc-iL-EXg" firstAttribute="centerX" secondItem="ZeX-qM-LJD" secondAttribute="centerX" id="2Bp-Ty-UhN"/>
+                        <constraint firstAttribute="bottom" secondItem="6FP-hm-dfl" secondAttribute="bottom" constant="11" id="31N-v4-lk3"/>
+                        <constraint firstItem="ya5-kp-sTW" firstAttribute="top" secondItem="ZeX-qM-LJD" secondAttribute="top" id="8zF-cX-C0k"/>
+                        <constraint firstAttribute="trailing" secondItem="ya5-kp-sTW" secondAttribute="trailing" id="T8f-BR-fRF"/>
+                        <constraint firstItem="ya5-kp-sTW" firstAttribute="leading" secondItem="ZeX-qM-LJD" secondAttribute="leading" id="beh-Nw-l30"/>
+                        <constraint firstItem="6FP-hm-dfl" firstAttribute="leading" secondItem="ZeX-qM-LJD" secondAttribute="leading" constant="11" id="dcC-0h-BqT"/>
+                        <constraint firstItem="6FP-hm-dfl" firstAttribute="top" secondItem="ya5-kp-sTW" secondAttribute="bottom" constant="11" id="ioo-lM-ERL"/>
+                        <constraint firstItem="uIc-iL-EXg" firstAttribute="top" secondItem="ZeX-qM-LJD" secondAttribute="top" constant="56" id="kgc-uR-hoE"/>
+                        <constraint firstAttribute="trailing" secondItem="6FP-hm-dfl" secondAttribute="trailing" constant="11" id="lZt-Ec-22l"/>
+                    </constraints>
+                </view>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Tkl-Cw-Mdz">
+                    <rect key="frame" x="6" y="562" width="189" height="90"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="90" id="t6I-nJ-9Qd"/>
+                    </constraints>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="fork_unchoose" backgroundImage="forkButton_bg"/>
+                    <connections>
+                        <action selector="forkSetting:" destination="iN0-l3-epB" eventType="touchUpInside" id="wcc-Y0-dc2"/>
+                    </connections>
+                </button>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="p0z-qL-bMm">
+                    <rect key="frame" x="195" y="562" width="189" height="90"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="90" id="D2z-en-N9g"/>
+                    </constraints>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="fork_unPlay" backgroundImage="playButton_bg"/>
+                    <connections>
+                        <action selector="playButtonAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="xnp-kV-r78"/>
+                    </connections>
+                </button>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="toning_bottom" translatesAutoresizingMaskIntoConstraints="NO" id="IXi-hL-in1">
+                    <rect key="frame" x="26" y="659" width="338" height="29"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="338" id="Ohx-Sa-5XQ"/>
+                        <constraint firstAttribute="height" constant="29" id="tsc-a8-bbh"/>
+                    </constraints>
+                </imageView>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5sT-Eb-2rJ">
+                    <rect key="frame" x="56" y="264" width="42" height="21"/>
+                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                    <nil key="textColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ke2-CX-K7G">
+                    <rect key="frame" x="293" y="264" width="42" height="21"/>
+                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                    <nil key="textColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tuning_decorate" translatesAutoresizingMaskIntoConstraints="NO" id="v4Z-dA-iqI">
+                    <rect key="frame" x="37" y="322" width="48" height="32"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="32" id="7Od-xm-q3j"/>
+                        <constraint firstAttribute="width" constant="48" id="9hP-Ux-x02"/>
+                    </constraints>
+                </imageView>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tuning_decorate" translatesAutoresizingMaskIntoConstraints="NO" id="V2v-I8-jzm">
+                    <rect key="frame" x="305" y="322" width="48" height="32"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="48" id="e8K-AZ-0N3"/>
+                        <constraint firstAttribute="height" constant="32" id="qMW-a2-T7G"/>
+                    </constraints>
+                </imageView>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PnS-Ku-vsa">
+                    <rect key="frame" x="195" y="76" width="0.0" height="0.0"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <nil key="highlightedColor"/>
+                </label>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="sZj-ZO-VHR" firstAttribute="leading" secondItem="Cai-ev-v5E" secondAttribute="trailing" constant="17" id="0D9-VV-0Lx"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="leading" secondItem="ZeX-qM-LJD" secondAttribute="leading" id="1nv-ne-dbI"/>
+                <constraint firstItem="g4h-vE-BMO" firstAttribute="bottom" secondItem="Cai-ev-v5E" secondAttribute="bottom" id="3KO-qD-HZ2"/>
+                <constraint firstItem="p0z-qL-bMm" firstAttribute="top" secondItem="7Em-FC-3EL" secondAttribute="bottom" constant="12" id="4SR-6A-ipm"/>
+                <constraint firstItem="p0z-qL-bMm" firstAttribute="leading" secondItem="Tkl-Cw-Mdz" secondAttribute="trailing" id="5EB-Vw-dj2"/>
+                <constraint firstItem="Cai-ev-v5E" firstAttribute="leading" secondItem="v4Z-dA-iqI" secondAttribute="trailing" constant="22" id="6b8-ki-CY9"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="14" id="7ZV-OX-B1b"/>
+                <constraint firstItem="Cai-ev-v5E" firstAttribute="centerY" secondItem="v4Z-dA-iqI" secondAttribute="centerY" id="8Eb-SN-txF"/>
+                <constraint firstItem="IXi-hL-in1" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="Au1-eF-R25"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="leading" secondItem="sZj-ZO-VHR" secondAttribute="trailing" constant="17" id="C3a-5i-dtf"/>
+                <constraint firstItem="p0z-qL-bMm" firstAttribute="width" secondItem="Tkl-Cw-Mdz" secondAttribute="width" id="Dk9-Wv-4mD"/>
+                <constraint firstItem="PnS-Ku-vsa" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="FLp-jq-kpX"/>
+                <constraint firstItem="SXB-cz-Qdh" firstAttribute="top" secondItem="Ux8-2g-RqK" secondAttribute="bottom" constant="10" id="FYQ-Rq-QtT"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="top" secondItem="ZeX-qM-LJD" secondAttribute="top" id="FlV-cS-OgW"/>
+                <constraint firstItem="g4h-vE-BMO" firstAttribute="trailing" secondItem="Cai-ev-v5E" secondAttribute="trailing" id="N2Y-VV-UPi"/>
+                <constraint firstItem="SXB-cz-Qdh" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="8" id="O3Y-u0-MV3"/>
+                <constraint firstItem="sZj-ZO-VHR" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="PEf-sX-DzU"/>
+                <constraint firstItem="Tkl-Cw-Mdz" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="6" id="Q9g-JT-4o6"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="top" secondItem="uwR-ra-cDH" secondAttribute="top" id="RZq-Hd-B0P"/>
+                <constraint firstAttribute="trailing" secondItem="p0z-qL-bMm" secondAttribute="trailing" constant="6" id="RoG-8u-1ap"/>
+                <constraint firstItem="V2v-I8-jzm" firstAttribute="centerY" secondItem="VJU-LE-Pdq" secondAttribute="centerY" id="T1g-e0-zDn"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="trailing" secondItem="uwR-ra-cDH" secondAttribute="trailing" id="Tew-m4-zeK"/>
+                <constraint firstItem="Zrx-Pe-2NT" firstAttribute="top" secondItem="SXB-cz-Qdh" secondAttribute="top" constant="46" id="Xd5-nj-nbj"/>
+                <constraint firstItem="V2v-I8-jzm" firstAttribute="leading" secondItem="VJU-LE-Pdq" secondAttribute="trailing" constant="22" id="YYc-Q5-aGD"/>
+                <constraint firstItem="IXi-hL-in1" firstAttribute="top" secondItem="Tkl-Cw-Mdz" secondAttribute="bottom" constant="7" id="YgN-1n-xha"/>
+                <constraint firstAttribute="trailing" secondItem="7Em-FC-3EL" secondAttribute="trailing" constant="14" id="a3n-fN-euB"/>
+                <constraint firstItem="Zrx-Pe-2NT" firstAttribute="top" secondItem="PnS-Ku-vsa" secondAttribute="bottom" constant="12" id="aSM-PX-vLj"/>
+                <constraint firstItem="g4h-vE-BMO" firstAttribute="leading" secondItem="Cai-ev-v5E" secondAttribute="leading" id="btp-Oe-y1i"/>
+                <constraint firstItem="sZj-ZO-VHR" firstAttribute="centerY" secondItem="Cai-ev-v5E" secondAttribute="centerY" id="cIh-IQ-xRr"/>
+                <constraint firstItem="Tkl-Cw-Mdz" firstAttribute="top" secondItem="7Em-FC-3EL" secondAttribute="bottom" constant="12" id="cJp-sC-nM0"/>
+                <constraint firstItem="Sbe-L2-liH" firstAttribute="centerY" secondItem="Ux8-2g-RqK" secondAttribute="centerY" id="cmi-yM-djK"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="bottom" secondItem="ZeX-qM-LJD" secondAttribute="bottom" id="coV-Qi-RkQ"/>
+                <constraint firstItem="Ux8-2g-RqK" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="22" id="cwN-DO-wkV"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="bottom" secondItem="uwR-ra-cDH" secondAttribute="bottom" id="e0m-sL-feH"/>
+                <constraint firstItem="Ux8-2g-RqK" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="10" id="ev8-FX-ve4"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="leading" secondItem="uwR-ra-cDH" secondAttribute="leading" id="hAr-OA-5ce"/>
+                <constraint firstItem="Sbe-L2-liH" firstAttribute="leading" secondItem="Ux8-2g-RqK" secondAttribute="trailing" constant="5" id="meC-LK-DKS"/>
+                <constraint firstItem="g4h-vE-BMO" firstAttribute="top" secondItem="Cai-ev-v5E" secondAttribute="top" id="nxA-Si-aiG"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="trailing" secondItem="ZeX-qM-LJD" secondAttribute="trailing" id="oKn-54-J78"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="top" secondItem="sZj-ZO-VHR" secondAttribute="bottom" constant="32" id="pZl-CI-zNY"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="centerY" secondItem="Cai-ev-v5E" secondAttribute="centerY" id="usx-pc-WFX"/>
+                <constraint firstItem="sZj-ZO-VHR" firstAttribute="top" secondItem="SXB-cz-Qdh" secondAttribute="bottom" constant="32" id="v2V-ca-D8A"/>
+                <constraint firstAttribute="trailing" secondItem="SXB-cz-Qdh" secondAttribute="trailing" constant="8" id="vQ0-wP-c03"/>
+                <constraint firstItem="Zrx-Pe-2NT" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="wef-Xs-dNx"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="A4FrequenceLabel" destination="Ux8-2g-RqK" id="Jra-7M-iTB"/>
+                <outlet property="addButton" destination="VJU-LE-Pdq" id="Ap2-3f-er6"/>
+                <outlet property="addView" destination="uwR-ra-cDH" id="aTz-zS-V4w"/>
+                <outlet property="minusButton" destination="Cai-ev-v5E" id="7T1-RS-zM3"/>
+                <outlet property="minusView" destination="g4h-vE-BMO" id="SOP-VD-G0z"/>
+                <outlet property="nomalPitch" destination="uIc-iL-EXg" id="i9k-OK-L2i"/>
+                <outlet property="pitchBgView" destination="7Em-FC-3EL" id="0rG-SE-app"/>
+                <outlet property="pitchFrequenceLabel" destination="PnS-Ku-vsa" id="LlJ-w5-az7"/>
+                <outlet property="plateView" destination="Zrx-Pe-2NT" id="ppk-X8-oX5"/>
+                <outlet property="playButton" destination="p0z-qL-bMm" id="DsB-sp-PJ7"/>
+                <outlet property="transferDesc" destination="tD3-d1-rfd" id="vBI-n9-F5P"/>
+                <outlet property="transferNomalPitch" destination="MNR-s0-re6" id="sVk-tj-gtm"/>
+                <outlet property="transferPitch" destination="vku-Yz-AEN" id="ca0-HH-hao"/>
+                <outlet property="transferView" destination="ya5-kp-sTW" id="VPm-KO-4q0"/>
+            </connections>
+            <point key="canvasLocation" x="93.84615384615384" y="-12.085308056872037"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="forkButton_bg" width="182" height="90"/>
+        <image name="fork_unPlay" width="42" height="28"/>
+        <image name="fork_unchoose" width="42" height="28"/>
+        <image name="freguqnce_add" width="14" height="14"/>
+        <image name="frequence_minus" width="14" height="14"/>
+        <image name="plate_bg" width="359" height="254"/>
+        <image name="playButton_bg" width="182" height="90"/>
+        <image name="toning_bottom" width="338" height="29"/>
+        <image name="tuning_decorate" width="48" height="32"/>
+        <image name="tuning_transfer" width="20" height="23"/>
+        <image name="tuning_warning" width="13" height="12"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 16 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TunerSettingView.h

@@ -0,0 +1,16 @@
+//
+//  TunerSettingView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/20.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TunerSettingView : UIView
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 20 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TunerSettingView.m

@@ -0,0 +1,20 @@
+//
+//  TunerSettingView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/20.
+//
+
+#import "TunerSettingView.h"
+
+@implementation TunerSettingView
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 18 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TunerSettingView.xib

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
+        </view>
+    </objects>
+</document>

+ 29 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningForkSettingView.h

@@ -0,0 +1,29 @@
+//
+//  TuningForkSettingView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/20.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef void(^ForkSettingCallback)(NSInteger frequence);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TuningForkSettingView : UIView
+
+@property (nonatomic, assign) BOOL isFallingTone; // 是否降调
+
+@property (nonatomic, assign) NSInteger A4Frequence; // A4频率
+
++ (instancetype)shareInstance;
+
+- (void)forkSettingAction:(ForkSettingCallback)callback;
+
+- (void)showView;
+
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 202 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningForkSettingView.m

@@ -0,0 +1,202 @@
+//
+//  TuningForkSettingView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/20.
+//
+
+#import "TuningForkSettingView.h"
+#import "UIView+Animation.h"
+
+
+@interface TuningForkSettingView ()
+
+@property (nonatomic, assign) NSInteger musicalScale;
+
+@property (weak, nonatomic) IBOutlet UILabel *frequenceLabel;
+@property (weak, nonatomic) IBOutlet UILabel *musicalScaleLabel;
+
+@property (weak, nonatomic) IBOutlet UIImageView *pitchCImage;
+@property (weak, nonatomic) IBOutlet UIImageView *pitchDImage;
+@property (weak, nonatomic) IBOutlet UIImageView *pitchEImage;
+@property (weak, nonatomic) IBOutlet UIImageView *pitchFImage;
+@property (weak, nonatomic) IBOutlet UIImageView *pitchGImage;
+@property (weak, nonatomic) IBOutlet UIImageView *pitchAImage;
+@property (weak, nonatomic) IBOutlet UIImageView *pitchBImage;
+
+@property (weak, nonatomic) IBOutlet UIImageView *pitchDhalfImage;
+@property (weak, nonatomic) IBOutlet UILabel *pitchDhalfLabel;
+@property (weak, nonatomic) IBOutlet UILabel *pitchDhalfDesc;
+
+@property (weak, nonatomic) IBOutlet UIImageView *pitchEhalfImage;
+@property (weak, nonatomic) IBOutlet UILabel *pitchEhalfLabel;
+@property (weak, nonatomic) IBOutlet UILabel *pitchEhalfDesc;
+
+@property (weak, nonatomic) IBOutlet UIImageView *pitchGhalfImage;
+@property (weak, nonatomic) IBOutlet UILabel *pitchGhalfLabel;
+@property (weak, nonatomic) IBOutlet UILabel *pitchGhalfDesc;
+
+
+@property (weak, nonatomic) IBOutlet UIImageView *pitchAhalfImage;
+@property (weak, nonatomic) IBOutlet UILabel *pitchAhalfLabel;
+@property (weak, nonatomic) IBOutlet UILabel *pitchAhalfDesc;
+
+@property (weak, nonatomic) IBOutlet UIImageView *pitchBhalfImage;
+@property (weak, nonatomic) IBOutlet UILabel *pitchBhalfLabel;
+@property (weak, nonatomic) IBOutlet UILabel *pitchBhalfDesc;
+
+@property (nonatomic, assign) NSInteger chooseIndex;
+
+
+@property (nonatomic, strong) NSMutableArray *imageArray;
+
+@property (nonatomic, strong) NSMutableArray *frequenceArray;
+
+@property (nonatomic, copy) ForkSettingCallback callback;
+
+@end
+
+@implementation TuningForkSettingView
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    [self configDefaultSource];
+}
+
+- (void)forkSettingAction:(ForkSettingCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+
+- (void)showView {
+    [[NSObject getKeyWindow] addSubview:self];
+    [self setPopAnimation];
+}
+
+- (void)removeView {
+    [self removeFromSuperview];
+}
+
+- (void)configDefaultSource {
+    self.A4Frequence = 440;
+    self.musicalScale = 4;
+    self.isFallingTone = NO;
+    self.imageArray = [NSMutableArray arrayWithArray:@[self.pitchCImage,self.pitchDhalfImage,self.pitchDImage,self.pitchEhalfImage,self.pitchEImage,self.pitchFImage,self.pitchGhalfImage,self.pitchGImage,self.pitchAhalfImage,self.pitchAImage,self.pitchBhalfImage,self.pitchBImage]];
+    self.chooseIndex = 0;
+    self.frequenceArray = [NSMutableArray arrayWithArray:@[
+        @16.35, @17.32, @18.35, @19.45, @20.60, @21.83, @23.12, @24.50, @25.96, @27.50, @29.14, @30.87, // 0
+        @32.70, @34.65, @36.71, @38.89, @41.20, @43.65, @46.25, @49.00, @51.91, @55.00, @58.27, @61.74, // 1
+        @65.41, @69.30, @73.42, @77.78, @82.41, @87.31, @92.50, @98.00, @103.8, @110.0, @116.5, @123.5, // 2
+        @130.8, @138.6, @146.8, @155.6, @164.8, @174.6, @185.0, @196.0, @207.7, @220.0, @233.1, @246.9, // 3
+        @261.6, @277.2, @293.7, @311.1, @329.6, @349.2, @370.0, @392.0, @415.3, @440.0, @466.2, @493.9, // 4
+        @523.3, @554.4, @587.3, @622.3, @659.3, @698.5, @740.0, @784.0, @830.6, @880.0, @932.3, @987.8, // 5
+        @1047, @1109, @1175, @1245, @1319, @1397, @1480, @1568, @1661, @1760, @1865, @1976,             // 6
+        @2093, @2217, @2349, @2489, @2637, @2794, @2960, @3136, @3322, @3520, @3729, @3951,             // 7
+        @4186, @4435, @4699, @4978, @5274, @5588, @5920, @6272, @6645, @7040, @7459, @7902              // 8
+    ]];
+}
+
++ (instancetype)shareInstance {
+    TuningForkSettingView *view = [[[NSBundle mainBundle] loadNibNamed:@"TuningForkSettingView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+
+- (void)setIsFallingTone:(BOOL)isFallingTone {
+    _isFallingTone = isFallingTone;
+    if (isFallingTone) {
+        self.pitchDhalfLabel.text = @"D";
+        self.pitchEhalfLabel.text = @"E";
+        self.pitchGhalfLabel.text = @"G";
+        self.pitchAhalfLabel.text = @"A";
+        self.pitchBhalfLabel.text = @"B";
+        
+        self.pitchDhalfDesc.text = @"♭";
+        self.pitchEhalfDesc.text = @"♭";
+        self.pitchGhalfDesc.text = @"♭";
+        self.pitchAhalfDesc.text = @"♭";
+        self.pitchBhalfDesc.text = @"♭";
+    }
+    else {
+        
+        self.pitchDhalfLabel.text = @"C";
+        self.pitchEhalfLabel.text = @"D";
+        self.pitchGhalfLabel.text = @"F";
+        self.pitchAhalfLabel.text = @"G";
+        self.pitchBhalfLabel.text = @"A";
+        
+        self.pitchDhalfDesc.text = @"#";
+        self.pitchEhalfDesc.text = @"#";
+        self.pitchGhalfDesc.text = @"#";
+        self.pitchAhalfDesc.text = @"#";
+        self.pitchBhalfDesc.text = @"#";
+        
+    }
+}
+
+- (void)setA4Frequence:(NSInteger)A4Frequence {
+    _A4Frequence = A4Frequence;
+    self.frequenceLabel.text = [NSString stringWithFormat:@"A4=%zdHz",A4Frequence];
+}
+
+- (IBAction)pitchChooseAction:(UIButton *)sender {
+    NSInteger index = sender.tag - 1000;
+    NSLog(@"----- %zd",index);
+    if (self.chooseIndex == index) {
+        return;
+    }
+    // 修改
+    UIImageView *preChooseImage = self.imageArray[self.chooseIndex];
+    [preChooseImage setImage:[UIImage imageNamed:@"fork_pitchUnChoosed"]];
+    
+    UIImageView *chooseImageView = self.imageArray[index];
+    [chooseImageView setImage:[UIImage imageNamed:@"fork_pitchChoosed"]];
+    self.chooseIndex = index;
+}
+
+
+
+// 音阶+
+- (IBAction)musicalScaleAdd:(id)sender {
+    if (self.musicalScale >= 6) {
+        return;
+    }
+    self.musicalScale++;
+}
+// 音阶-
+- (IBAction)musicalScaleMinus:(id)sender {
+    if (self.musicalScale <= 4) {
+        return;
+    }
+    self.musicalScale--;
+}
+
+// 设置保存
+- (IBAction)forkSettingSureAction:(id)sender {
+    if (self.callback) {
+        // 获取频率
+        NSInteger index = self.musicalScale * 12 + self.chooseIndex;
+        if (self.frequenceArray.count > index) {
+            NSInteger frequence = [self.frequenceArray[index] integerValue];
+            self.callback(frequence);
+        }
+    }
+    [self removeView];
+}
+
+// 设置音阶
+- (void)setMusicalScale:(NSInteger)musicalScale {
+    _musicalScale = musicalScale;
+    self.musicalScaleLabel.text = [NSString stringWithFormat:@"%zd",musicalScale];
+}
+
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 768 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningForkSettingView.xib

@@ -0,0 +1,768 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_0" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="TuningForkSettingView">
+            <rect key="frame" x="0.0" y="0.0" width="390" height="844"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ip2-Qv-Yar">
+                    <rect key="frame" x="59.666666666666657" y="207" width="271" height="430"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="音叉" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="M5Y-mG-3VG">
+                            <rect key="frame" x="118" y="14" width="35" height="24"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="24" id="Ogo-h9-fgv"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                            <color key="textColor" red="0.10196078431372549" green="0.10196078431372549" blue="0.10196078431372549" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A4=440Hz" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Bhh-r7-n9J">
+                            <rect key="frame" x="18.000000000000007" y="46" width="71" height="20"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="20" id="dRA-mi-FA2"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_warning" translatesAutoresizingMaskIntoConstraints="NO" id="a4p-9E-C7P">
+                            <rect key="frame" x="94" y="50" width="13" height="12"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="12" id="0si-Jb-XVE"/>
+                                <constraint firstAttribute="width" constant="13" id="tQO-gq-KGl"/>
+                            </constraints>
+                        </imageView>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="AlX-o1-xtB">
+                            <rect key="frame" x="21" y="71" width="229" height="236"/>
+                            <subviews>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QaI-So-iYb">
+                                    <rect key="frame" x="0.0" y="0.0" width="114.33333333333333" height="33.666666666666664"/>
+                                    <subviews>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="7tD-sj-An5">
+                                            <rect key="frame" x="0.0" y="8" width="18" height="18"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="18" id="Qe6-0z-7Dy"/>
+                                                <constraint firstAttribute="height" constant="18" id="jGw-6f-xYc"/>
+                                            </constraints>
+                                        </imageView>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="C" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="q0D-qH-qHW">
+                                            <rect key="frame" x="28" y="7" width="15" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="J3x-5e-Weo"/>
+                                                <constraint firstAttribute="width" constant="15" id="v13-0K-Jrj"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <button opaque="NO" tag="1000" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SFN-Jd-vwS">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="33.666666666666664"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="4fL-Sk-1lq"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="HIx-Lv-I3Q"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstAttribute="bottom" secondItem="SFN-Jd-vwS" secondAttribute="bottom" id="9Tl-bg-PHr"/>
+                                        <constraint firstItem="q0D-qH-qHW" firstAttribute="centerY" secondItem="QaI-So-iYb" secondAttribute="centerY" id="K9H-jH-FSL"/>
+                                        <constraint firstItem="7tD-sj-An5" firstAttribute="centerY" secondItem="QaI-So-iYb" secondAttribute="centerY" id="Qzv-b7-cIQ"/>
+                                        <constraint firstItem="SFN-Jd-vwS" firstAttribute="top" secondItem="QaI-So-iYb" secondAttribute="top" id="U3y-Xn-eNr"/>
+                                        <constraint firstItem="7tD-sj-An5" firstAttribute="leading" secondItem="QaI-So-iYb" secondAttribute="leading" id="Vag-Gd-27Z"/>
+                                        <constraint firstItem="SFN-Jd-vwS" firstAttribute="leading" secondItem="QaI-So-iYb" secondAttribute="leading" id="dr2-EP-PpS"/>
+                                        <constraint firstItem="q0D-qH-qHW" firstAttribute="leading" secondItem="7tD-sj-An5" secondAttribute="trailing" constant="10" id="sBP-d8-p2R"/>
+                                    </constraints>
+                                </view>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jwK-E6-xjZ">
+                                    <rect key="frame" x="0.0" y="33.666666666666686" width="114.33333333333333" height="33.666666666666657"/>
+                                    <subviews>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchUnChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="hKC-PS-LdR">
+                                            <rect key="frame" x="0.0" y="8" width="18" height="18"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="18" id="0bk-Gl-XWB"/>
+                                                <constraint firstAttribute="height" constant="18" id="Nt9-u1-tpO"/>
+                                            </constraints>
+                                        </imageView>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="D" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RST-7S-DcE">
+                                            <rect key="frame" x="28" y="7" width="11" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="JZT-Hv-Emb"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <button opaque="NO" tag="1002" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="d4u-1b-vP7">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="33.666666666666664"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="a2C-4k-prK"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="7pz-pX-Tvc"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstItem="d4u-1b-vP7" firstAttribute="leading" secondItem="jwK-E6-xjZ" secondAttribute="leading" id="2Mc-WQ-oyF"/>
+                                        <constraint firstAttribute="bottom" secondItem="d4u-1b-vP7" secondAttribute="bottom" id="Amd-L6-NN1"/>
+                                        <constraint firstItem="RST-7S-DcE" firstAttribute="leading" secondItem="hKC-PS-LdR" secondAttribute="trailing" constant="10" id="GRj-iX-Z55"/>
+                                        <constraint firstItem="hKC-PS-LdR" firstAttribute="leading" secondItem="jwK-E6-xjZ" secondAttribute="leading" id="N8d-MX-ZOj"/>
+                                        <constraint firstItem="RST-7S-DcE" firstAttribute="centerY" secondItem="jwK-E6-xjZ" secondAttribute="centerY" id="hv9-aM-693"/>
+                                        <constraint firstItem="hKC-PS-LdR" firstAttribute="centerY" secondItem="jwK-E6-xjZ" secondAttribute="centerY" id="ntA-ih-w1a"/>
+                                        <constraint firstItem="d4u-1b-vP7" firstAttribute="top" secondItem="jwK-E6-xjZ" secondAttribute="top" id="uEC-JW-fj0"/>
+                                    </constraints>
+                                </view>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WNU-wF-T23">
+                                    <rect key="frame" x="0.0" y="67.333333333333314" width="114.33333333333333" height="33.666666666666657"/>
+                                    <subviews>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchUnChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="33L-TD-Ua1">
+                                            <rect key="frame" x="0.0" y="8" width="18" height="18"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="18" id="BwL-MR-3cV"/>
+                                                <constraint firstAttribute="height" constant="18" id="VwG-ON-pFW"/>
+                                            </constraints>
+                                        </imageView>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="E" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="F44-Np-VSD">
+                                            <rect key="frame" x="28" y="7" width="9" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="p9U-Lm-V19"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <button opaque="NO" tag="1004" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4rZ-Fm-jWg">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="33.666666666666664"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="Sr6-eQ-Dbu"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="tEV-w3-t1W"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstAttribute="bottom" secondItem="4rZ-Fm-jWg" secondAttribute="bottom" id="Uoz-c0-hMs"/>
+                                        <constraint firstItem="F44-Np-VSD" firstAttribute="leading" secondItem="33L-TD-Ua1" secondAttribute="trailing" constant="10" id="X4J-Un-mUn"/>
+                                        <constraint firstItem="33L-TD-Ua1" firstAttribute="leading" secondItem="WNU-wF-T23" secondAttribute="leading" id="im4-SY-tp7"/>
+                                        <constraint firstItem="4rZ-Fm-jWg" firstAttribute="leading" secondItem="WNU-wF-T23" secondAttribute="leading" id="uQj-Ek-VjV"/>
+                                        <constraint firstItem="33L-TD-Ua1" firstAttribute="centerY" secondItem="WNU-wF-T23" secondAttribute="centerY" id="wbV-z8-t91"/>
+                                        <constraint firstItem="4rZ-Fm-jWg" firstAttribute="top" secondItem="WNU-wF-T23" secondAttribute="top" id="xHc-qL-PpH"/>
+                                        <constraint firstItem="F44-Np-VSD" firstAttribute="centerY" secondItem="WNU-wF-T23" secondAttribute="centerY" id="xM7-cb-oa8"/>
+                                    </constraints>
+                                </view>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vHa-XQ-6ny">
+                                    <rect key="frame" x="0.0" y="101" width="114.33333333333333" height="34"/>
+                                    <subviews>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchUnChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="DUO-ro-JJn">
+                                            <rect key="frame" x="0.0" y="8" width="18" height="18"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="18" id="6IK-nG-GNi"/>
+                                                <constraint firstAttribute="width" constant="18" id="pYl-hk-Ryc"/>
+                                            </constraints>
+                                        </imageView>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="F" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qhy-ZJ-VDs">
+                                            <rect key="frame" x="28" y="7" width="8" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="TYL-32-VIw"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <button opaque="NO" tag="1005" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8G5-fB-XwN">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="34"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="Q9N-P1-rIC"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="Rps-r1-8M6"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstItem="8G5-fB-XwN" firstAttribute="leading" secondItem="vHa-XQ-6ny" secondAttribute="leading" id="2WW-9i-vQA"/>
+                                        <constraint firstItem="DUO-ro-JJn" firstAttribute="centerY" secondItem="vHa-XQ-6ny" secondAttribute="centerY" id="8Ig-Iy-ioV"/>
+                                        <constraint firstItem="qhy-ZJ-VDs" firstAttribute="leading" secondItem="DUO-ro-JJn" secondAttribute="trailing" constant="10" id="Tcn-Nf-ez1"/>
+                                        <constraint firstItem="qhy-ZJ-VDs" firstAttribute="centerY" secondItem="vHa-XQ-6ny" secondAttribute="centerY" id="V4i-pe-i30"/>
+                                        <constraint firstAttribute="bottom" secondItem="8G5-fB-XwN" secondAttribute="bottom" id="euO-7K-fiR"/>
+                                        <constraint firstItem="DUO-ro-JJn" firstAttribute="leading" secondItem="vHa-XQ-6ny" secondAttribute="leading" id="gmN-12-926"/>
+                                        <constraint firstItem="8G5-fB-XwN" firstAttribute="top" secondItem="vHa-XQ-6ny" secondAttribute="top" id="wWZ-BJ-9oH"/>
+                                    </constraints>
+                                </view>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="u4x-Ff-czP">
+                                    <rect key="frame" x="0.0" y="135" width="114.33333333333333" height="33.666666666666657"/>
+                                    <subviews>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="G" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="G6r-gZ-B47">
+                                            <rect key="frame" x="28" y="6.6666666666666856" width="11" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="n7U-Vx-xgz"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchUnChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="XKV-Rp-Jta">
+                                            <rect key="frame" x="0.0" y="7.6666666666666856" width="18" height="18"/>
+                                        </imageView>
+                                        <button opaque="NO" tag="1007" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="BRS-TJ-pWC">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="33.666666666666664"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="Jue-kh-Ft3"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="INX-d0-e7L"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstItem="XKV-Rp-Jta" firstAttribute="leading" secondItem="u4x-Ff-czP" secondAttribute="leading" id="7Yf-05-act"/>
+                                        <constraint firstAttribute="bottom" secondItem="BRS-TJ-pWC" secondAttribute="bottom" id="Fjs-To-qCh"/>
+                                        <constraint firstItem="G6r-gZ-B47" firstAttribute="centerY" secondItem="XKV-Rp-Jta" secondAttribute="centerY" id="SVc-jH-VxW"/>
+                                        <constraint firstItem="BRS-TJ-pWC" firstAttribute="top" secondItem="u4x-Ff-czP" secondAttribute="top" id="cvx-OE-0AM"/>
+                                        <constraint firstItem="BRS-TJ-pWC" firstAttribute="leading" secondItem="u4x-Ff-czP" secondAttribute="leading" id="jx0-RL-gWF"/>
+                                        <constraint firstItem="G6r-gZ-B47" firstAttribute="leading" secondItem="XKV-Rp-Jta" secondAttribute="trailing" constant="10" id="rYg-Ho-QaA"/>
+                                        <constraint firstItem="XKV-Rp-Jta" firstAttribute="centerY" secondItem="u4x-Ff-czP" secondAttribute="centerY" id="v6S-og-SBA"/>
+                                    </constraints>
+                                </view>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="udJ-Lp-A7n">
+                                    <rect key="frame" x="0.0" y="168.66666666666669" width="114.33333333333333" height="33.666666666666657"/>
+                                    <subviews>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchUnChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="NVg-Ou-bSm">
+                                            <rect key="frame" x="0.0" y="7.6666666666666288" width="18" height="18"/>
+                                        </imageView>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8xs-rP-O2t">
+                                            <rect key="frame" x="28" y="6.6666666666666288" width="10" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="6cf-CB-kXe"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <button opaque="NO" tag="1009" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qlH-wo-fVt">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="33.666666666666664"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="5u0-cd-H82"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="eil-Qt-Bif"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstItem="qlH-wo-fVt" firstAttribute="leading" secondItem="udJ-Lp-A7n" secondAttribute="leading" id="MAi-oC-SHA"/>
+                                        <constraint firstItem="8xs-rP-O2t" firstAttribute="leading" secondItem="NVg-Ou-bSm" secondAttribute="trailing" constant="10" id="O2p-h9-Dvw"/>
+                                        <constraint firstItem="NVg-Ou-bSm" firstAttribute="centerY" secondItem="udJ-Lp-A7n" secondAttribute="centerY" id="Vxt-J6-7gs"/>
+                                        <constraint firstItem="NVg-Ou-bSm" firstAttribute="leading" secondItem="udJ-Lp-A7n" secondAttribute="leading" id="a3e-me-pyX"/>
+                                        <constraint firstAttribute="bottom" secondItem="qlH-wo-fVt" secondAttribute="bottom" id="gfx-iM-aFz"/>
+                                        <constraint firstItem="qlH-wo-fVt" firstAttribute="top" secondItem="udJ-Lp-A7n" secondAttribute="top" id="guc-ib-WAW"/>
+                                        <constraint firstItem="8xs-rP-O2t" firstAttribute="centerY" secondItem="NVg-Ou-bSm" secondAttribute="centerY" id="wws-Ut-fEQ"/>
+                                    </constraints>
+                                </view>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="p4w-Gf-bLk">
+                                    <rect key="frame" x="0.0" y="202.33333333333331" width="114.33333333333333" height="33.666666666666657"/>
+                                    <subviews>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchUnChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="4Lk-07-pwR">
+                                            <rect key="frame" x="0.0" y="7.6666666666666856" width="18" height="18"/>
+                                        </imageView>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="B" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="w2x-kg-EP3">
+                                            <rect key="frame" x="28" y="6.6666666666666856" width="10" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="jMT-Le-vI2"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <button opaque="NO" tag="1011" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Mdw-nL-CxU">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="33.666666666666664"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="vtL-S9-0eJ"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="5Jg-tN-wQu"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstAttribute="bottom" secondItem="Mdw-nL-CxU" secondAttribute="bottom" id="DfS-hF-dyP"/>
+                                        <constraint firstItem="Mdw-nL-CxU" firstAttribute="top" secondItem="p4w-Gf-bLk" secondAttribute="top" id="Ggs-ma-lX4"/>
+                                        <constraint firstItem="4Lk-07-pwR" firstAttribute="centerY" secondItem="p4w-Gf-bLk" secondAttribute="centerY" id="Nxs-gI-Nt4"/>
+                                        <constraint firstItem="w2x-kg-EP3" firstAttribute="centerY" secondItem="4Lk-07-pwR" secondAttribute="centerY" id="PvM-pR-45d"/>
+                                        <constraint firstItem="w2x-kg-EP3" firstAttribute="leading" secondItem="4Lk-07-pwR" secondAttribute="trailing" constant="10" id="ZiK-Zx-hpp"/>
+                                        <constraint firstItem="4Lk-07-pwR" firstAttribute="leading" secondItem="p4w-Gf-bLk" secondAttribute="leading" id="am8-h6-Voc"/>
+                                        <constraint firstItem="Mdw-nL-CxU" firstAttribute="leading" secondItem="p4w-Gf-bLk" secondAttribute="leading" id="tY4-62-Z2g"/>
+                                    </constraints>
+                                </view>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="W4E-Jy-6SS">
+                                    <rect key="frame" x="144.33333333333331" y="0.0" width="84.666666666666686" height="33.666666666666664"/>
+                                    <subviews>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchUnChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="kNP-fB-Sgb">
+                                            <rect key="frame" x="0.0" y="8" width="18" height="18"/>
+                                        </imageView>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="D" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sEP-7X-ajC">
+                                            <rect key="frame" x="28" y="7" width="10" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="1Vd-rK-UkG"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="♭" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CI4-Gl-Mnk">
+                                            <rect key="frame" x="38" y="12" width="10" height="10"/>
+                                            <fontDescription key="fontDescription" name="HiraginoSans-W3" family="Hiragino Sans" pointSize="10"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <button opaque="NO" tag="1001" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4km-0o-jtK">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="33.666666666666664"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="YFW-Mz-Owq"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="zni-tU-9hb"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstAttribute="bottom" secondItem="4km-0o-jtK" secondAttribute="bottom" id="BJY-4P-rY3"/>
+                                        <constraint firstItem="4km-0o-jtK" firstAttribute="leading" secondItem="W4E-Jy-6SS" secondAttribute="leading" id="OdR-s3-Jss"/>
+                                        <constraint firstItem="CI4-Gl-Mnk" firstAttribute="centerY" secondItem="sEP-7X-ajC" secondAttribute="centerY" id="SuH-1J-dYC"/>
+                                        <constraint firstItem="sEP-7X-ajC" firstAttribute="centerY" secondItem="kNP-fB-Sgb" secondAttribute="centerY" id="ajy-sb-rOU"/>
+                                        <constraint firstItem="sEP-7X-ajC" firstAttribute="leading" secondItem="kNP-fB-Sgb" secondAttribute="trailing" constant="10" id="c2W-U3-bUE"/>
+                                        <constraint firstItem="4km-0o-jtK" firstAttribute="top" secondItem="W4E-Jy-6SS" secondAttribute="top" id="hpk-kF-EKL"/>
+                                        <constraint firstItem="kNP-fB-Sgb" firstAttribute="centerY" secondItem="W4E-Jy-6SS" secondAttribute="centerY" id="jpM-oj-R2h"/>
+                                        <constraint firstItem="kNP-fB-Sgb" firstAttribute="leading" secondItem="W4E-Jy-6SS" secondAttribute="leading" id="nhj-UK-MA8"/>
+                                        <constraint firstItem="CI4-Gl-Mnk" firstAttribute="leading" secondItem="sEP-7X-ajC" secondAttribute="trailing" id="r4q-Ww-tAf"/>
+                                    </constraints>
+                                </view>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4RW-LV-gXm">
+                                    <rect key="frame" x="144.33333333333331" y="33.666666666666686" width="84.666666666666686" height="33.666666666666657"/>
+                                    <subviews>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchUnChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="TD9-gX-SSD">
+                                            <rect key="frame" x="0.0" y="8" width="18" height="18"/>
+                                        </imageView>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="E" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nYG-4s-3Me">
+                                            <rect key="frame" x="27.999999999999996" y="7" width="8.3333333333333321" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="QCQ-jA-eH8"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="♭" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0Au-70-poU">
+                                            <rect key="frame" x="36.333333333333314" y="12" width="10" height="10"/>
+                                            <fontDescription key="fontDescription" name="HiraginoSans-W3" family="Hiragino Sans" pointSize="10"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <button opaque="NO" tag="1003" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ym0-Ph-9xJ">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="33.666666666666664"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="iwC-Pn-Pl3"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="sfw-lD-73v"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstItem="nYG-4s-3Me" firstAttribute="leading" secondItem="TD9-gX-SSD" secondAttribute="trailing" constant="10" id="I9G-eM-Bpk"/>
+                                        <constraint firstItem="ym0-Ph-9xJ" firstAttribute="leading" secondItem="4RW-LV-gXm" secondAttribute="leading" id="IaA-BH-Naw"/>
+                                        <constraint firstItem="0Au-70-poU" firstAttribute="leading" secondItem="nYG-4s-3Me" secondAttribute="trailing" id="K7i-PY-qeC"/>
+                                        <constraint firstItem="nYG-4s-3Me" firstAttribute="centerY" secondItem="TD9-gX-SSD" secondAttribute="centerY" id="KXN-oq-f01"/>
+                                        <constraint firstItem="TD9-gX-SSD" firstAttribute="leading" secondItem="4RW-LV-gXm" secondAttribute="leading" id="Sba-x6-Vv7"/>
+                                        <constraint firstItem="ym0-Ph-9xJ" firstAttribute="top" secondItem="4RW-LV-gXm" secondAttribute="top" id="Sft-Yd-3Tu"/>
+                                        <constraint firstItem="TD9-gX-SSD" firstAttribute="centerY" secondItem="4RW-LV-gXm" secondAttribute="centerY" id="gHI-EL-cH9"/>
+                                        <constraint firstItem="0Au-70-poU" firstAttribute="centerY" secondItem="nYG-4s-3Me" secondAttribute="centerY" id="hht-jR-RiV"/>
+                                        <constraint firstAttribute="bottom" secondItem="ym0-Ph-9xJ" secondAttribute="bottom" id="kRZ-7s-l0x"/>
+                                    </constraints>
+                                </view>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="X4H-Fh-dFX">
+                                    <rect key="frame" x="144.33333333333331" y="101" width="84.666666666666686" height="34"/>
+                                    <subviews>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchUnChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="ejK-gv-bn7">
+                                            <rect key="frame" x="0.0" y="8" width="18" height="18"/>
+                                        </imageView>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="G" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gUd-Pa-98x">
+                                            <rect key="frame" x="27.999999999999996" y="7" width="10.333333333333332" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="FJQ-n0-rIS"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="♭" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5a7-xm-jfY">
+                                            <rect key="frame" x="38.333333333333314" y="12" width="10" height="10"/>
+                                            <fontDescription key="fontDescription" name="HiraginoSans-W3" family="Hiragino Sans" pointSize="10"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <button opaque="NO" tag="1006" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LsY-5b-xv1">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="34"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="scl-dU-BWw"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="mQt-Pd-nIa"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstItem="gUd-Pa-98x" firstAttribute="centerY" secondItem="ejK-gv-bn7" secondAttribute="centerY" id="4yg-jN-nrU"/>
+                                        <constraint firstItem="LsY-5b-xv1" firstAttribute="leading" secondItem="X4H-Fh-dFX" secondAttribute="leading" id="7Q2-Dy-IEE"/>
+                                        <constraint firstItem="LsY-5b-xv1" firstAttribute="top" secondItem="X4H-Fh-dFX" secondAttribute="top" id="Dsd-ff-wTD"/>
+                                        <constraint firstItem="gUd-Pa-98x" firstAttribute="leading" secondItem="ejK-gv-bn7" secondAttribute="trailing" constant="10" id="GjT-FC-gc9"/>
+                                        <constraint firstItem="ejK-gv-bn7" firstAttribute="centerY" secondItem="X4H-Fh-dFX" secondAttribute="centerY" id="MrA-JC-u3H"/>
+                                        <constraint firstItem="5a7-xm-jfY" firstAttribute="leading" secondItem="gUd-Pa-98x" secondAttribute="trailing" id="Rk2-T2-2XE"/>
+                                        <constraint firstAttribute="bottom" secondItem="LsY-5b-xv1" secondAttribute="bottom" id="ieJ-IX-DBc"/>
+                                        <constraint firstItem="5a7-xm-jfY" firstAttribute="centerY" secondItem="gUd-Pa-98x" secondAttribute="centerY" id="pgg-65-Syf"/>
+                                        <constraint firstItem="ejK-gv-bn7" firstAttribute="leading" secondItem="X4H-Fh-dFX" secondAttribute="leading" id="sCB-MC-pdb"/>
+                                    </constraints>
+                                </view>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Swe-Ry-qdg">
+                                    <rect key="frame" x="144.33333333333331" y="135" width="84.666666666666686" height="33.666666666666657"/>
+                                    <subviews>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchUnChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="F6U-1L-QNJ">
+                                            <rect key="frame" x="0.0" y="7.6666666666666856" width="18" height="18"/>
+                                        </imageView>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Pqy-zd-oA9">
+                                            <rect key="frame" x="27.999999999999996" y="6.6666666666666856" width="9.3333333333333321" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="PKU-Nt-dD1"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="♭" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="t9g-qn-BnE">
+                                            <rect key="frame" x="37.333333333333314" y="11.666666666666686" width="10" height="10"/>
+                                            <fontDescription key="fontDescription" name="HiraginoSans-W3" family="Hiragino Sans" pointSize="10"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <button opaque="NO" tag="1008" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="W5f-GN-alG">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="33.666666666666664"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="Pqc-pD-DWx"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="xQw-y1-U8Q"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstItem="t9g-qn-BnE" firstAttribute="leading" secondItem="Pqy-zd-oA9" secondAttribute="trailing" id="6Am-Of-9DK"/>
+                                        <constraint firstItem="W5f-GN-alG" firstAttribute="leading" secondItem="Swe-Ry-qdg" secondAttribute="leading" id="DXF-73-evP"/>
+                                        <constraint firstItem="F6U-1L-QNJ" firstAttribute="leading" secondItem="Swe-Ry-qdg" secondAttribute="leading" id="Fnk-m5-aeV"/>
+                                        <constraint firstAttribute="bottom" secondItem="W5f-GN-alG" secondAttribute="bottom" id="LAw-i3-7qt"/>
+                                        <constraint firstItem="Pqy-zd-oA9" firstAttribute="leading" secondItem="F6U-1L-QNJ" secondAttribute="trailing" constant="10" id="UyP-fl-n41"/>
+                                        <constraint firstItem="t9g-qn-BnE" firstAttribute="centerY" secondItem="Pqy-zd-oA9" secondAttribute="centerY" id="c3d-KF-3DS"/>
+                                        <constraint firstItem="Pqy-zd-oA9" firstAttribute="centerY" secondItem="F6U-1L-QNJ" secondAttribute="centerY" id="hXE-BY-rnB"/>
+                                        <constraint firstItem="W5f-GN-alG" firstAttribute="top" secondItem="Swe-Ry-qdg" secondAttribute="top" id="l5l-25-MKS"/>
+                                        <constraint firstItem="F6U-1L-QNJ" firstAttribute="centerY" secondItem="Swe-Ry-qdg" secondAttribute="centerY" id="pFY-qO-S7S"/>
+                                    </constraints>
+                                </view>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8fc-ui-TRO">
+                                    <rect key="frame" x="144.33333333333331" y="168.66666666666669" width="84.666666666666686" height="33.666666666666657"/>
+                                    <subviews>
+                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="fork_pitchUnChoosed" translatesAutoresizingMaskIntoConstraints="NO" id="Csb-9e-UUh">
+                                            <rect key="frame" x="0.0" y="7.6666666666666288" width="18" height="18"/>
+                                        </imageView>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="B" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iBu-od-lwB">
+                                            <rect key="frame" x="27.999999999999996" y="6.6666666666666288" width="9.3333333333333321" height="20"/>
+                                            <constraints>
+                                                <constraint firstAttribute="height" constant="20" id="VNf-5n-GF1"/>
+                                            </constraints>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="♭" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cSB-cy-6zq">
+                                            <rect key="frame" x="37.333333333333314" y="11.666666666666629" width="10" height="10"/>
+                                            <fontDescription key="fontDescription" name="HiraginoSans-W3" family="Hiragino Sans" pointSize="10"/>
+                                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                            <nil key="highlightedColor"/>
+                                        </label>
+                                        <button opaque="NO" tag="1010" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0Sz-yn-Kz6">
+                                            <rect key="frame" x="0.0" y="0.0" width="60" height="33.666666666666664"/>
+                                            <constraints>
+                                                <constraint firstAttribute="width" constant="60" id="Jx3-uf-8AG"/>
+                                            </constraints>
+                                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                            <connections>
+                                                <action selector="pitchChooseAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="kkV-OC-KRe"/>
+                                            </connections>
+                                        </button>
+                                    </subviews>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstItem="0Sz-yn-Kz6" firstAttribute="top" secondItem="8fc-ui-TRO" secondAttribute="top" id="9rm-aC-dvf"/>
+                                        <constraint firstItem="Csb-9e-UUh" firstAttribute="centerY" secondItem="8fc-ui-TRO" secondAttribute="centerY" id="AVb-Xx-jd6"/>
+                                        <constraint firstItem="cSB-cy-6zq" firstAttribute="centerY" secondItem="iBu-od-lwB" secondAttribute="centerY" id="EI1-2d-JFa"/>
+                                        <constraint firstItem="iBu-od-lwB" firstAttribute="leading" secondItem="Csb-9e-UUh" secondAttribute="trailing" constant="10" id="H83-7v-j0m"/>
+                                        <constraint firstItem="Csb-9e-UUh" firstAttribute="leading" secondItem="8fc-ui-TRO" secondAttribute="leading" id="L8C-bd-Zrb"/>
+                                        <constraint firstAttribute="bottom" secondItem="0Sz-yn-Kz6" secondAttribute="bottom" id="OY2-vz-SDh"/>
+                                        <constraint firstItem="0Sz-yn-Kz6" firstAttribute="leading" secondItem="8fc-ui-TRO" secondAttribute="leading" id="RfG-83-RYA"/>
+                                        <constraint firstItem="iBu-od-lwB" firstAttribute="centerY" secondItem="Csb-9e-UUh" secondAttribute="centerY" id="tLg-Io-vUd"/>
+                                        <constraint firstItem="cSB-cy-6zq" firstAttribute="leading" secondItem="iBu-od-lwB" secondAttribute="trailing" id="yPO-dA-zKb"/>
+                                    </constraints>
+                                </view>
+                            </subviews>
+                            <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                            <constraints>
+                                <constraint firstItem="p4w-Gf-bLk" firstAttribute="top" secondItem="udJ-Lp-A7n" secondAttribute="bottom" id="0F2-9S-1MV"/>
+                                <constraint firstItem="Swe-Ry-qdg" firstAttribute="height" secondItem="4RW-LV-gXm" secondAttribute="height" id="298-1R-vhl"/>
+                                <constraint firstItem="8fc-ui-TRO" firstAttribute="trailing" secondItem="4RW-LV-gXm" secondAttribute="trailing" id="4Oz-wR-Yti"/>
+                                <constraint firstItem="udJ-Lp-A7n" firstAttribute="leading" secondItem="QaI-So-iYb" secondAttribute="leading" id="4wb-0w-ky8"/>
+                                <constraint firstItem="8fc-ui-TRO" firstAttribute="height" secondItem="4RW-LV-gXm" secondAttribute="height" id="8M9-Tf-bRP"/>
+                                <constraint firstItem="8fc-ui-TRO" firstAttribute="leading" secondItem="4RW-LV-gXm" secondAttribute="leading" id="Axf-hG-ZST"/>
+                                <constraint firstItem="WNU-wF-T23" firstAttribute="top" secondItem="jwK-E6-xjZ" secondAttribute="bottom" id="BB3-8i-nFo"/>
+                                <constraint firstItem="jwK-E6-xjZ" firstAttribute="top" secondItem="QaI-So-iYb" secondAttribute="bottom" id="DbK-67-90q"/>
+                                <constraint firstItem="WNU-wF-T23" firstAttribute="height" secondItem="QaI-So-iYb" secondAttribute="height" id="EeN-Qx-Fp1"/>
+                                <constraint firstItem="4RW-LV-gXm" firstAttribute="top" secondItem="W4E-Jy-6SS" secondAttribute="bottom" id="G8h-Mx-gZF"/>
+                                <constraint firstItem="X4H-Fh-dFX" firstAttribute="leading" secondItem="4RW-LV-gXm" secondAttribute="leading" id="HfH-4X-Scd"/>
+                                <constraint firstItem="jwK-E6-xjZ" firstAttribute="width" secondItem="QaI-So-iYb" secondAttribute="width" id="Ju2-Xx-VP2"/>
+                                <constraint firstItem="p4w-Gf-bLk" firstAttribute="width" secondItem="QaI-So-iYb" secondAttribute="width" id="NJE-Vq-6U4"/>
+                                <constraint firstItem="vHa-XQ-6ny" firstAttribute="leading" secondItem="QaI-So-iYb" secondAttribute="leading" id="NOr-gE-kAI"/>
+                                <constraint firstItem="u4x-Ff-czP" firstAttribute="top" secondItem="vHa-XQ-6ny" secondAttribute="bottom" id="Nfw-fA-Rtb"/>
+                                <constraint firstItem="Swe-Ry-qdg" firstAttribute="leading" secondItem="4RW-LV-gXm" secondAttribute="leading" id="OxX-AZ-Hem"/>
+                                <constraint firstItem="p4w-Gf-bLk" firstAttribute="leading" secondItem="QaI-So-iYb" secondAttribute="leading" id="R6G-T7-Rve"/>
+                                <constraint firstItem="4RW-LV-gXm" firstAttribute="height" secondItem="W4E-Jy-6SS" secondAttribute="height" id="S0j-6R-nvt"/>
+                                <constraint firstItem="jwK-E6-xjZ" firstAttribute="height" secondItem="QaI-So-iYb" secondAttribute="height" id="T9x-CT-SkB"/>
+                                <constraint firstItem="W4E-Jy-6SS" firstAttribute="height" secondItem="QaI-So-iYb" secondAttribute="height" id="UaF-Jh-3NP"/>
+                                <constraint firstItem="QaI-So-iYb" firstAttribute="leading" secondItem="AlX-o1-xtB" secondAttribute="leading" id="Vsi-uc-sJI"/>
+                                <constraint firstItem="W4E-Jy-6SS" firstAttribute="top" secondItem="AlX-o1-xtB" secondAttribute="top" id="XDH-4n-vKb"/>
+                                <constraint firstItem="vHa-XQ-6ny" firstAttribute="width" secondItem="QaI-So-iYb" secondAttribute="width" id="XHq-Ps-MHk"/>
+                                <constraint firstItem="X4H-Fh-dFX" firstAttribute="height" secondItem="4RW-LV-gXm" secondAttribute="height" id="YqO-QZ-x7m"/>
+                                <constraint firstItem="Swe-Ry-qdg" firstAttribute="top" secondItem="X4H-Fh-dFX" secondAttribute="bottom" id="aTh-hI-sBG"/>
+                                <constraint firstItem="8fc-ui-TRO" firstAttribute="top" secondItem="Swe-Ry-qdg" secondAttribute="bottom" id="b5p-Yr-eme"/>
+                                <constraint firstItem="vHa-XQ-6ny" firstAttribute="height" secondItem="QaI-So-iYb" secondAttribute="height" id="baD-Il-kHJ"/>
+                                <constraint firstAttribute="trailing" secondItem="W4E-Jy-6SS" secondAttribute="trailing" id="cNC-SE-Ffg"/>
+                                <constraint firstItem="p4w-Gf-bLk" firstAttribute="height" secondItem="QaI-So-iYb" secondAttribute="height" id="dXh-F6-p3e"/>
+                                <constraint firstItem="WNU-wF-T23" firstAttribute="leading" secondItem="QaI-So-iYb" secondAttribute="leading" id="dfN-iX-k9b"/>
+                                <constraint firstItem="QaI-So-iYb" firstAttribute="trailing" secondItem="AlX-o1-xtB" secondAttribute="centerX" id="eKw-1d-eeZ"/>
+                                <constraint firstItem="u4x-Ff-czP" firstAttribute="leading" secondItem="QaI-So-iYb" secondAttribute="leading" id="eoz-h6-RWg"/>
+                                <constraint firstItem="X4H-Fh-dFX" firstAttribute="trailing" secondItem="4RW-LV-gXm" secondAttribute="trailing" id="fNa-Gf-2Rq"/>
+                                <constraint firstItem="udJ-Lp-A7n" firstAttribute="width" secondItem="QaI-So-iYb" secondAttribute="width" id="fOj-qz-pT0"/>
+                                <constraint firstItem="WNU-wF-T23" firstAttribute="width" secondItem="QaI-So-iYb" secondAttribute="width" id="ghS-Fi-b0h"/>
+                                <constraint firstItem="X4H-Fh-dFX" firstAttribute="top" secondItem="vHa-XQ-6ny" secondAttribute="top" id="gqE-Dk-JzW"/>
+                                <constraint firstAttribute="trailing" secondItem="4RW-LV-gXm" secondAttribute="trailing" id="ia6-0t-8qX"/>
+                                <constraint firstItem="Swe-Ry-qdg" firstAttribute="trailing" secondItem="4RW-LV-gXm" secondAttribute="trailing" id="jDs-ud-Ja6"/>
+                                <constraint firstItem="W4E-Jy-6SS" firstAttribute="leading" secondItem="QaI-So-iYb" secondAttribute="trailing" constant="30" id="paI-dH-D6C"/>
+                                <constraint firstItem="W4E-Jy-6SS" firstAttribute="leading" secondItem="4RW-LV-gXm" secondAttribute="leading" id="q25-8H-YjE"/>
+                                <constraint firstItem="vHa-XQ-6ny" firstAttribute="top" secondItem="WNU-wF-T23" secondAttribute="bottom" id="qpA-yw-FyY"/>
+                                <constraint firstItem="u4x-Ff-czP" firstAttribute="height" secondItem="QaI-So-iYb" secondAttribute="height" id="rnT-mp-Y59"/>
+                                <constraint firstItem="udJ-Lp-A7n" firstAttribute="height" secondItem="QaI-So-iYb" secondAttribute="height" id="tTa-Yp-lhQ"/>
+                                <constraint firstItem="jwK-E6-xjZ" firstAttribute="leading" secondItem="QaI-So-iYb" secondAttribute="leading" id="tXa-Wh-PbP"/>
+                                <constraint firstAttribute="bottom" secondItem="p4w-Gf-bLk" secondAttribute="bottom" id="tjz-Wm-URv"/>
+                                <constraint firstItem="QaI-So-iYb" firstAttribute="top" secondItem="AlX-o1-xtB" secondAttribute="top" id="xLq-Wy-6oH"/>
+                                <constraint firstItem="udJ-Lp-A7n" firstAttribute="top" secondItem="u4x-Ff-czP" secondAttribute="bottom" id="xRx-3R-ynL"/>
+                                <constraint firstItem="u4x-Ff-czP" firstAttribute="width" secondItem="QaI-So-iYb" secondAttribute="width" id="zBL-7s-hP4"/>
+                            </constraints>
+                        </view>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="L5h-Ny-1au">
+                            <rect key="frame" x="18" y="365" width="235" height="40"/>
+                            <color key="backgroundColor" red="0.1764705882352941" green="0.7803921568627451" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="40" id="Cj4-4L-fBx"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="16"/>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" title="确认"/>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="6"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                            <connections>
+                                <action selector="forkSettingSureAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="tVz-M3-Jtq"/>
+                            </connections>
+                        </button>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7Bu-fJ-Eb8">
+                            <rect key="frame" x="23.333333333333336" y="312" width="63.000000000000007" height="45"/>
+                            <color key="backgroundColor" red="0.87450980392156863" green="0.87450980392156863" blue="0.87450980392156863" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="63" id="XUH-Jp-vQU"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" image="speed_add">
+                                <color key="titleColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            </state>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="6"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                            <connections>
+                                <action selector="musicalScaleAdd:" destination="iN0-l3-epB" eventType="touchUpInside" id="Owv-Ey-QE1"/>
+                            </connections>
+                        </button>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ezm-DJ-9xp">
+                            <rect key="frame" x="184.33333333333334" y="312" width="63" height="45"/>
+                            <color key="backgroundColor" red="0.87450980392156863" green="0.87450980392156863" blue="0.87450980392156863" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="63" id="YVj-x6-Ebd"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" image="speed_minus">
+                                <color key="titleColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            </state>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="6"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                            <connections>
+                                <action selector="musicalScaleMinus:" destination="iN0-l3-epB" eventType="touchUpInside" id="BVO-ik-vxs"/>
+                            </connections>
+                        </button>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dJJ-CJ-edB">
+                            <rect key="frame" x="95.333333333333343" y="312" width="80" height="45"/>
+                            <subviews>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="4" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aKX-xG-USM">
+                                    <rect key="frame" x="35.666666666666657" y="14" width="9" height="17"/>
+                                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                    <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                            </subviews>
+                            <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                            <constraints>
+                                <constraint firstItem="aKX-xG-USM" firstAttribute="centerY" secondItem="dJJ-CJ-edB" secondAttribute="centerY" id="GGy-UY-htL"/>
+                                <constraint firstAttribute="width" constant="80" id="HRi-cq-610"/>
+                                <constraint firstAttribute="height" constant="45" id="KLx-Z3-S5m"/>
+                                <constraint firstItem="aKX-xG-USM" firstAttribute="centerX" secondItem="dJJ-CJ-edB" secondAttribute="centerX" id="TbK-1g-05a"/>
+                            </constraints>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="6"/>
+                                </userDefinedRuntimeAttribute>
+                                <userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
+                                    <real key="value" value="1"/>
+                                </userDefinedRuntimeAttribute>
+                                <userDefinedRuntimeAttribute type="color" keyPath="borderColor">
+                                    <color key="value" red="0.59215686274509804" green="0.59215686274509804" blue="0.59215686274509804" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                        </view>
+                    </subviews>
+                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                    <constraints>
+                        <constraint firstItem="AlX-o1-xtB" firstAttribute="top" secondItem="Bhh-r7-n9J" secondAttribute="bottom" constant="5" id="9OS-CT-d64"/>
+                        <constraint firstItem="a4p-9E-C7P" firstAttribute="centerY" secondItem="Bhh-r7-n9J" secondAttribute="centerY" id="AEO-mo-DXw"/>
+                        <constraint firstItem="7Bu-fJ-Eb8" firstAttribute="top" secondItem="AlX-o1-xtB" secondAttribute="bottom" constant="5" id="Akz-1N-oLz"/>
+                        <constraint firstAttribute="width" constant="271" id="Amp-lt-7Eg"/>
+                        <constraint firstItem="L5h-Ny-1au" firstAttribute="top" secondItem="dJJ-CJ-edB" secondAttribute="bottom" constant="8" id="BgG-y0-Lxg"/>
+                        <constraint firstItem="Bhh-r7-n9J" firstAttribute="leading" secondItem="ip2-Qv-Yar" secondAttribute="leading" constant="18" id="GBk-GF-qBt"/>
+                        <constraint firstItem="ezm-DJ-9xp" firstAttribute="leading" secondItem="dJJ-CJ-edB" secondAttribute="trailing" constant="9" id="GR3-Ks-0Go"/>
+                        <constraint firstItem="a4p-9E-C7P" firstAttribute="leading" secondItem="Bhh-r7-n9J" secondAttribute="trailing" constant="5" id="K1b-Nt-SsF"/>
+                        <constraint firstItem="AlX-o1-xtB" firstAttribute="leading" secondItem="ip2-Qv-Yar" secondAttribute="leading" constant="21" id="M4p-yy-EAe"/>
+                        <constraint firstItem="L5h-Ny-1au" firstAttribute="leading" secondItem="ip2-Qv-Yar" secondAttribute="leading" constant="18" id="Mws-fc-9fd"/>
+                        <constraint firstItem="Bhh-r7-n9J" firstAttribute="top" secondItem="M5Y-mG-3VG" secondAttribute="bottom" constant="8" id="NCA-EM-mAV"/>
+                        <constraint firstItem="dJJ-CJ-edB" firstAttribute="centerX" secondItem="ip2-Qv-Yar" secondAttribute="centerX" id="SVC-lY-Pb3"/>
+                        <constraint firstItem="dJJ-CJ-edB" firstAttribute="height" secondItem="7Bu-fJ-Eb8" secondAttribute="height" id="U8P-Nt-1Wp"/>
+                        <constraint firstItem="ezm-DJ-9xp" firstAttribute="centerY" secondItem="7Bu-fJ-Eb8" secondAttribute="centerY" id="X8V-jP-zcr"/>
+                        <constraint firstAttribute="bottom" secondItem="L5h-Ny-1au" secondAttribute="bottom" constant="25" id="Y5J-0y-qyS"/>
+                        <constraint firstItem="dJJ-CJ-edB" firstAttribute="leading" secondItem="7Bu-fJ-Eb8" secondAttribute="trailing" constant="9" id="bHD-pg-V8a"/>
+                        <constraint firstAttribute="trailing" secondItem="L5h-Ny-1au" secondAttribute="trailing" constant="18" id="djX-Wd-dqb"/>
+                        <constraint firstItem="ezm-DJ-9xp" firstAttribute="height" secondItem="7Bu-fJ-Eb8" secondAttribute="height" id="gRI-Lx-Of1"/>
+                        <constraint firstAttribute="trailing" secondItem="AlX-o1-xtB" secondAttribute="trailing" constant="21" id="oPn-IM-YQF"/>
+                        <constraint firstItem="M5Y-mG-3VG" firstAttribute="top" secondItem="ip2-Qv-Yar" secondAttribute="top" constant="14" id="pfS-Vq-c5m"/>
+                        <constraint firstItem="dJJ-CJ-edB" firstAttribute="centerY" secondItem="7Bu-fJ-Eb8" secondAttribute="centerY" id="tOS-rR-wqD"/>
+                        <constraint firstAttribute="height" constant="430" id="uMY-cw-3mk"/>
+                        <constraint firstItem="M5Y-mG-3VG" firstAttribute="centerX" secondItem="ip2-Qv-Yar" secondAttribute="centerX" id="zZu-Ob-TnJ"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="15"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+            </subviews>
+            <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
+            <constraints>
+                <constraint firstItem="ip2-Qv-Yar" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="01S-SR-k4J"/>
+                <constraint firstItem="ip2-Qv-Yar" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="mIs-j2-Rqf"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="frequenceLabel" destination="Bhh-r7-n9J" id="pCN-eE-vqp"/>
+                <outlet property="musicalScaleLabel" destination="aKX-xG-USM" id="Kg6-4A-9Ut"/>
+                <outlet property="pitchAImage" destination="NVg-Ou-bSm" id="fbe-xA-p5t"/>
+                <outlet property="pitchAhalfDesc" destination="t9g-qn-BnE" id="xiA-el-VlU"/>
+                <outlet property="pitchAhalfImage" destination="F6U-1L-QNJ" id="GZ9-GX-Hoy"/>
+                <outlet property="pitchAhalfLabel" destination="Pqy-zd-oA9" id="R7p-Ti-m3G"/>
+                <outlet property="pitchBImage" destination="4Lk-07-pwR" id="vTS-bX-ShU"/>
+                <outlet property="pitchBhalfDesc" destination="cSB-cy-6zq" id="sSn-V8-JJb"/>
+                <outlet property="pitchBhalfImage" destination="Csb-9e-UUh" id="fRa-AC-N9w"/>
+                <outlet property="pitchBhalfLabel" destination="iBu-od-lwB" id="qHz-C5-fEa"/>
+                <outlet property="pitchCImage" destination="7tD-sj-An5" id="zm2-WV-JRP"/>
+                <outlet property="pitchDImage" destination="hKC-PS-LdR" id="lEV-R1-oGp"/>
+                <outlet property="pitchDhalfDesc" destination="CI4-Gl-Mnk" id="C09-NT-iQC"/>
+                <outlet property="pitchDhalfImage" destination="kNP-fB-Sgb" id="cQt-hK-mwt"/>
+                <outlet property="pitchDhalfLabel" destination="sEP-7X-ajC" id="lR2-aN-Bs2"/>
+                <outlet property="pitchEImage" destination="33L-TD-Ua1" id="N8N-1r-sOn"/>
+                <outlet property="pitchEhalfDesc" destination="0Au-70-poU" id="lha-eq-cvs"/>
+                <outlet property="pitchEhalfImage" destination="TD9-gX-SSD" id="ptO-4M-BzX"/>
+                <outlet property="pitchEhalfLabel" destination="nYG-4s-3Me" id="OBR-Aj-XIU"/>
+                <outlet property="pitchFImage" destination="DUO-ro-JJn" id="fYk-Vc-bEt"/>
+                <outlet property="pitchGImage" destination="XKV-Rp-Jta" id="5NT-PX-fpY"/>
+                <outlet property="pitchGhalfDesc" destination="5a7-xm-jfY" id="scg-Ig-bsO"/>
+                <outlet property="pitchGhalfImage" destination="ejK-gv-bn7" id="eXg-KJ-KEG"/>
+                <outlet property="pitchGhalfLabel" destination="gUd-Pa-98x" id="dOV-eb-LX7"/>
+            </connections>
+            <point key="canvasLocation" x="101.53846153846153" y="-12.085308056872037"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="fork_pitchChoosed" width="18" height="18"/>
+        <image name="fork_pitchUnChoosed" width="18" height="18"/>
+        <image name="fork_warning" width="13" height="12"/>
+        <image name="speed_add" width="14" height="14"/>
+        <image name="speed_minus" width="14" height="14"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 22 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningNavView.h

@@ -0,0 +1,22 @@
+//
+//  TuningNavView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef void(^TuningNavAction)(BOOL isBack);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TuningNavView : UIView
+
++ (instancetype)shareInstance;
+
+- (void)navActionCallback:(TuningNavAction)callbcak;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 52 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningNavView.m

@@ -0,0 +1,52 @@
+//
+//  TuningNavView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "TuningNavView.h"
+
+@interface TuningNavView ()
+
+@property (nonatomic, copy) TuningNavAction callback;
+
+@end
+
+
+@implementation TuningNavView
+
++ (instancetype)shareInstance {
+    TuningNavView *view = [[[NSBundle mainBundle] loadNibNamed:@"TuningNavView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)navActionCallback:(TuningNavAction)callbcak {
+    if (callbcak) {
+        self.callback = callbcak;
+    }
+}
+
+- (IBAction)backAction:(id)sender {
+    if (self.callback) {
+        self.callback(YES);
+    }
+}
+
+- (IBAction)settingAction:(id)sender {
+    if (self.callback) {
+        self.callback(NO);
+    }
+}
+
+
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 92 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Widget/View/toneTuning/TuningNavView.xib

@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_0" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="TuningNavView">
+            <rect key="frame" x="0.0" y="0.0" width="390" height="65"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XRC-8p-p4e">
+                    <rect key="frame" x="0.0" y="21" width="390" height="44"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="back_button_white" translatesAutoresizingMaskIntoConstraints="NO" id="EN2-WR-x5g">
+                            <rect key="frame" x="14" y="12" width="12" height="20"/>
+                        </imageView>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="pFx-8o-VCU">
+                            <rect key="frame" x="0.0" y="0.0" width="44" height="44"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="44" id="QgR-J5-WSv"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="backAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="O0M-F8-PQy"/>
+                            </connections>
+                        </button>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="调音器" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kAY-IQ-kPT">
+                            <rect key="frame" x="167" y="11" width="56" height="22"/>
+                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
+                            <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="设置" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIK-YD-KvW">
+                            <rect key="frame" x="312" y="13.666666666666664" width="29" height="17"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tuning_setting" translatesAutoresizingMaskIntoConstraints="NO" id="YlL-14-R48">
+                            <rect key="frame" x="349" y="12.666666666666664" width="19" height="19"/>
+                        </imageView>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="BFU-Xz-2Xu">
+                            <rect key="frame" x="312" y="0.0" width="78" height="44"/>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="settingAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="10h-IE-KXJ"/>
+                            </connections>
+                        </button>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="trailing" secondItem="BFU-Xz-2Xu" secondAttribute="trailing" id="0QV-8r-cmb"/>
+                        <constraint firstItem="pFx-8o-VCU" firstAttribute="leading" secondItem="XRC-8p-p4e" secondAttribute="leading" id="0la-07-HRD"/>
+                        <constraint firstItem="BFU-Xz-2Xu" firstAttribute="top" secondItem="XRC-8p-p4e" secondAttribute="top" id="1ZM-4W-pkR"/>
+                        <constraint firstAttribute="bottom" secondItem="BFU-Xz-2Xu" secondAttribute="bottom" id="MLk-Cq-pRw"/>
+                        <constraint firstItem="FIK-YD-KvW" firstAttribute="leading" secondItem="BFU-Xz-2Xu" secondAttribute="leading" id="NVa-Nb-oCu"/>
+                        <constraint firstItem="kAY-IQ-kPT" firstAttribute="centerX" secondItem="XRC-8p-p4e" secondAttribute="centerX" id="NyJ-R5-o14"/>
+                        <constraint firstItem="YlL-14-R48" firstAttribute="centerY" secondItem="XRC-8p-p4e" secondAttribute="centerY" id="Zc8-0c-TcZ"/>
+                        <constraint firstItem="EN2-WR-x5g" firstAttribute="centerY" secondItem="XRC-8p-p4e" secondAttribute="centerY" id="Zdd-20-X1y"/>
+                        <constraint firstAttribute="height" constant="44" id="cm1-xm-D5Z"/>
+                        <constraint firstAttribute="bottom" secondItem="pFx-8o-VCU" secondAttribute="bottom" id="dGQ-eq-OMp"/>
+                        <constraint firstItem="YlL-14-R48" firstAttribute="centerY" secondItem="FIK-YD-KvW" secondAttribute="centerY" id="e8f-De-izn"/>
+                        <constraint firstItem="YlL-14-R48" firstAttribute="leading" secondItem="FIK-YD-KvW" secondAttribute="trailing" constant="8" id="euB-Go-WMd"/>
+                        <constraint firstAttribute="trailing" secondItem="YlL-14-R48" secondAttribute="trailing" constant="22" id="jwl-X5-VvP"/>
+                        <constraint firstItem="EN2-WR-x5g" firstAttribute="leading" secondItem="XRC-8p-p4e" secondAttribute="leading" constant="14" id="p0O-O5-qtS"/>
+                        <constraint firstItem="pFx-8o-VCU" firstAttribute="top" secondItem="XRC-8p-p4e" secondAttribute="top" id="u6y-gg-F2T"/>
+                        <constraint firstItem="kAY-IQ-kPT" firstAttribute="centerY" secondItem="XRC-8p-p4e" secondAttribute="centerY" id="wBj-40-9AW"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="XRC-8p-p4e" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="SyP-b7-HEW"/>
+                <constraint firstAttribute="bottom" secondItem="XRC-8p-p4e" secondAttribute="bottom" id="ZWl-Cr-0de"/>
+                <constraint firstAttribute="trailing" secondItem="XRC-8p-p4e" secondAttribute="trailing" id="m8p-XJ-5cc"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="96.92307692307692" y="-104.14691943127961"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="back_button_white" width="12" height="20"/>
+        <image name="tuning_setting" width="19" height="19"/>
+    </resources>
+</document>