cs193p_2021笔记[4]_Color_Image_Ges

2021-11-07  本文已影响0人  walkerwzy

cs193p_2021_笔记_1
cs193p_2021_笔记_2
cs193p_2021_笔记_3_Animation_Transition
cs193p_2021_笔记_4_Color_Image_Gesture
cs193p_2021_笔记_5_Property Wrapper
cs193p_2021_笔记_6_Persistence
cs193p_2021_笔记_7_Document Architecture
cs193p_2021_笔记_8

--

Color, UIColor & CGColor

Color:

UIColor:

For example, you can get the RGBA values from a UIColor.
Once you have desired UIColor, employ Color(uiColor:) to use it in one of the roles above.

CGColor:

Image V.S. UIImage

Image:

UIImage

Multithreading

A: Creating a Queue

There are numerous ways to create a queue, but we’re only going to look at two ...

DispatchQueue.main // the queue where all UI code must be posted
DispatchQueue.global(qos: QoS) // a non-UI queue with a certain quality of service qos (quality of service) is one of the following ...
    .userInteractive    // do this fast, the UI depends on it!
    .userInitiated  // the user just asked to do this, so do it now
    .utility    // this needs to happen, but the user didn’t just ask for it
    .background // maintenance tasks (cleanups, etc.)

B: Plopping a Closure onto a Queue

There are two basic ways to add a closure to a queue ...

let queue = DispatchQueue.main //or
let queue = DispatchQueue.global(qos:) 
queue.async { /* code to execute on queue */ }
queue.sync { /* code to execute on queue */ }

主线程里永远不要.sync, 那样会阻塞UI

DispatchQueue(global: .userInitiated).async {
    // 耗时代码
    // 不阻塞UI,也不能更新UI
    // 到主线程去更新UI
    DispatchQueue.main.async {
        // UI code can go here! we’re on the main queue! 
    }
}

Gestures

手势是iOS里的一等公民

// recognize
myView.gesture(theGesture) // theGesture must implement the Gesture protocol

// create
var theGesture: some Gesture {
    return TapGesture(count: 2)  // double tap
}

// discrete gestures
var theGesture: some Gesture {
      return TapGesture(count: 2)
        .onEnded { /* do something */ }
}

// 其实就是:
func theGesture() -> some Gesture {
    tapGesture(count: 2)
}

// “convenience versions”
myView.onTapGesture(count: Int) { /* do something */ } 
myView.onLongPressGesture(...) { /* do something */ }

// non-discrete gestures

var theGesture: some Gesture {
      DragGesture(...)
.onEnded { value in /* do something */ } 

non-discrete手势里传递的value是一个state:

唯一可以更新这个myGestureState的机会:

var theGesture: some Gesture {
     DragGesture(...)
        .updating($myGestureState) { value, myGestureState, transaction in 
            myGestureState = /* usually something related to value */
        }
        .onEnded { value in /* do something */ }
 }

注意$的用法

如果不需要去计算一个gestureState传出去的话,有个updating用简版:

.onChanged { value in
/* do something with value (which is the state of the fingers) */
}

事实上,目前来看gestureState只做了两件事:

  1. 把实时手势对应的值保存起来
  2. 在手势结束时复原(对于缩放,变为1,对于移动,变为0)
  3. 同时,它是只读的,只在.updating方法里有更新的机会

所以,如果你的UI和动画逻辑,用到了手势结束时的值(即需要它复原),那么你也可以直接在.onEnded方法里手动把它设回去,等同于你也实现了你的gestureState,并且没有它那些限制。

Drag and Drop

Item Provider

结合几个要点,一句话就能让你的元素能被拖动(drag):

Text(emoji).onDrag{ NSItemProvider(object: emoji as NSString)}

而接收(drop)则要复杂很多:

otherView.onDrop(of: [.plainText], isTarget: nil) {providers, location in return false }

itemprovider里加载对象有模板代码:

extension Array where Element == NSItemProvider {
  func loadObjects<T>(ofType theType: T.Type, firstOnly: Bool = false, using load: @escaping (T) -> Void) -> Bool where T: NSItemProviderReading {
    if let provider = first(where: { $0.canLoadObject(ofClass: theType)}) {
      provider.loadObject(ofClass: theType) { object, error in
        if let value = object as? T {
          DispatchQueue.main.async {
              load(value)
          }
        }
      }
      return true
    }
    return false
  }

// and
// where T: _ObjectiveCBridgeable, T._ObjectiveCType: NSItemProviderReading
  1. 提供了两段代码,可以看到其实就是对要加载的对象的约束不同,提供了对OC的兼容
  2. 模板代码演示了
    稳健地从拖拽对象加载内容(canload -> load)
  3. 真正的业务逻辑其实就是为拖进来的这个view选择一个位置存放(或读取它携带的数据)
  4. T.Type传的是类别的.self,比如String.self
上一篇 下一篇

猜你喜欢

热点阅读