<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                > 原文出處: http://southpeak.github.io/blog/2015/05/31/ioszhi-shi-xiao-ji-di-er-qi-2015-dot-05-dot-31/ 作者: 南峰子 換了個廠子,還不到1個月。哎,著實是累啊,基本上是996.5的節奏,只會更多。加班把我快加吐了,但人在江湖,身不由已啊。為了討口飯吃,命也不要了。誰讓咱只是個臭寫代碼的呢。不過加班是多,只是長得太丑,所有沒辦法,沒時間也得抽時間來學習。不然,飯都沒得吃了,還得養家糊口呢。 本期總結的內容不是很多,主要有以下幾個問題: 1. 使用UIVisualEffectView為視圖添加特殊效果 2. Nullability Annotations 3. weak的生命周期 ## 使用UIVisualEffectView為視圖添加特殊效果 在iOS 8后,蘋果開放了不少創建特效的接口,其中就包括創建毛玻璃(blur)的接口。 通常要想創建一個特殊效果(如blur效果),可以創建一個UIVisualEffectView視圖對象,這個對象提供了一種簡單的方式來實現復雜的視覺效果。這個可以把這個對象看作是效果的一個容器,實際的效果會影響到該視圖對象底下的內容,或者是添加到該視圖對象的contentView中的內容。 我們舉個例子來看看如果使用UIVisualEffectView: ~~~ let bgView: UIImageView = UIImageView(image: UIImage(named: "visual")) bgView.frame = self.view.bounds self.view.addSubview(bgView) let blurEffect: UIBlurEffect = UIBlurEffect(style: .Light) let blurView: UIVisualEffectView = UIVisualEffectView(effect: blurEffect) blurView.frame = CGRectMake(50.0, 50.0, self.view.frame.width - 100.0, 200.0) self.view.addSubview(blurView) ~~~ 這段代碼是在當前視圖控制器上添加了一個UIImageView作為背景圖。然后在視圖的一小部分中使用了blur效果。其效果如下所示: ![image](https://box.kancloud.cn/2015-08-21_55d6ccb7b8c85.jpg) 我們可以看到UIVisualEffectView還是非常簡單的。需要注意是的,不應該直接添加子視圖到UIVisualEffectView視圖中,而是應該添加到UIVisualEffectView對象的contentView中。 另外,盡量避免將UIVisualEffectView對象的alpha值設置為小于1.0的值,因為創建半透明的視圖會導致系統在離屏渲染時去對UIVisualEffectView對象及所有的相關的子視圖做混合操作。這不但消耗CPU/GPU,也可能會導致許多效果顯示不正確或者根本不顯示。 我們在上面看到,初始化一個UIVisualEffectView對象的方法是UIVisualEffectView(effect: blurEffect),其定義如下: ~~~ init(effect effect: UIVisualEffect) ~~~ 這個方法的參數是一個UIVisualEffect對象。我們查看官方文檔,可以看到在UIKit中,定義了幾個專門用來創建視覺特效的,它們分別是UIVisualEffect、UIBlurEffect和UIVibrancyEffect。它們的繼承層次如下所示: ~~~ NSObject | -- UIVisualEffect | -- UIBlurEffect | -- UIVibrancyEffect ~~~ UIVisualEffect是一個繼承自NSObject的創建視覺效果的基類,然而這個類除了繼承自NSObject的屬性和方法外,沒有提供任何新的屬性和方法。其主要目的是用于初始化UIVisualEffectView,在這個初始化方法中可以傳入UIBlurEffect或者UIVibrancyEffect對象。 一個UIBlurEffect對象用于將blur(毛玻璃)效果應用于UIVisualEffectView視圖下面的內容。如上面的示例所示。不過,這個對象的效果并不影響UIVisualEffectView對象的contentView中的內容。 UIBlurEffect主要定義了三種效果,這些效果由枚舉UIBlurEffectStyle來確定,該枚舉的定義如下: ~~~ enum UIBlurEffectStyle : Int { case ExtraLight case Light case Dark } ~~~ 其主要是根據色調(hue)來確定特效視圖與底部視圖的混合。 與UIBlurEffect不同的是,UIVibrancyEffect主要用于放大和調整UIVisualEffectView視圖下面的內容的顏色,同時讓UIVisualEffectView的contentView中的內容看起來更加生動。通常UIVibrancyEffect對象是與UIBlurEffect一起使用,主要用于處理在UIBlurEffect特效上的一些顯示效果。接上面的代碼,我們看看在blur的視圖上添加一些新的特效,如下代碼所示: ~~~ let vibrancyView: UIVisualEffectView = UIVisualEffectView(effect: UIVibrancyEffect(forBlurEffect: blurEffect)) vibrancyView.setTranslatesAutoresizingMaskIntoConstraints(false) blurView.contentView.addSubview(vibrancyView) var label: UILabel = UILabel() label.setTranslatesAutoresizingMaskIntoConstraints(false) label.text = "Vibrancy Effect" label.font = UIFont(name: "HelveticaNeue-Bold", size: 30) label.textAlignment = .Center label.textColor = UIColor.whiteColor() vibrancyView.contentView.addSubview(label) ~~~ 其效果如下圖所示: ![image](https://box.kancloud.cn/2015-08-21_55d6ccb82db9f.jpg) vibrancy特效是取決于顏色值的。所有添加到contentView的子視圖都必須實現tintColorDidChange方法并更新自己。需要注意的是,我們使用UIVibrancyEffect(forBlurEffect:)方法創建UIVibrancyEffect時,參數blurEffect必須是我們想加效果的那個blurEffect,否則可能不是我們想要的效果。 另外,UIVibrancyEffect還提供了一個類方法notificationCenterVibrancyEffect,其聲明如下: ~~~ class func notificationCenterVibrancyEffect() -> UIVibrancyEffect! ~~~ 這個方法創建一個用于通知中心的Today擴展的vibrancy特效。 ### 參考 1. [UIVisualEffectView Class Reference](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIVisualEffectView/) 2. [UIVisualEffect Class Reference](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIVisualEffect_class/index.html#//apple_ref/occ/cl/UIVisualEffect) 3. [UIBlurEffect Class Reference](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIVisualEffect_class/index.html#//apple_ref/occ/cl/UIVisualEffect) 4. [UIVibrancyEffect Class Reference](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIVibrancyEffect/) 5. [UIVisualEffect – Swift Tutorial](http://swiftoverload.com/tag/uivisualeffectview/) 6. [iOS 8: UIVisualEffect](http://idrawcode.tumblr.com/post/101925733632/ios-8-uivisualeffect) ## Pointer is missing a nullability type specifier (**nonnull or?**nullable)問題的處理 — Nullability Annotations 最近在用Xcode 6.3寫代碼,一些涉及到對象的代碼會報如下編譯器警告: ~~~ Pointer is missing a nullability type specifier (__nonnull or __nullable) ~~~ 于是google了一下,發現這是Xcode 6.3的一個新特性,即**nullability annotations**。 ### Nullability Annotations 我們都知道在swift中,可以使用!和?來表示一個對象是optional的還是non-optional,如view?和view!。而在Objective-C中則沒有這一區分,view即可表示這個對象是optional,也可表示是non-optioanl。這樣就會造成一個問題:在Swift與Objective-C混編時,Swift編譯器并不知道一個Objective-C對象到底是optional還是non-optional,因此這種情況下編譯器會隱式地將Objective-C的對象當成是non-optional。 為了解決這個問題,蘋果在Xcode 6.3引入了一個Objective-C的新特性:nullability annotations。這一新特性的核心是兩個新的類型注釋:**__nullable**和**__nonnull**。從字面上我們可以猜到,**__nullable**表示對象可以是NULL或nil,而**__nonnull**表示對象不應該為空。當我們不遵循這一規則時,編譯器就會給出警告。 我們來看看以下的實例, ~~~ @interface TestNullabilityClass () @property (nonatomic, copy) NSArray * items; - (id)itemWithName:(NSString * __nonnull)name; @end @implementation TestNullabilityClass ... - (void)testNullability { [self itemWithName:nil]; // 編譯器警告:Null passed to a callee that requires a non-null argument } - (id)itemWithName:(NSString * __nonnull)name { return nil; } @end ~~~ 不過這只是一個警告,程序還是能編譯通過并運行。 事實上,在任何可以使用const關鍵字的地方都可以使用__nullable和__nonnull,不過這兩個關鍵字僅限于使用在指針類型上。而在方法的聲明中,我們還可以使用不帶下劃線的nullable和nonnull,如下所示: ~~~ - (nullable id)itemWithName:(NSString * nonnull)name ~~~ 在屬性聲明中,也增加了兩個相應的特性,因此上例中的items屬性可以如下聲明: ~~~ @property (nonatomic, copy, nonnull) NSArray * items; ~~~ 當然也可以用以下這種方式: ~~~ @property (nonatomic, copy) NSArray * __nonnull items; ~~~ 推薦使用nonnull這種方式,這樣可以讓屬性聲明看起來更清晰。 ### Nonnull區域設置(Audited Regions) 如果需要每個屬性或每個方法都去指定nonnull和nullable,是一件非常繁瑣的事。蘋果為了減輕我們的工作量,專門提供了兩個宏:NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END。在這兩個宏之間的代碼,所有簡單指針對象都被假定為nonnull,因此我們只需要去指定那些nullable的指針。如下代碼所示: ~~~ NS_ASSUME_NONNULL_BEGIN @interface TestNullabilityClass () @property (nonatomic, copy) NSArray * items; - (id)itemWithName:(nullable NSString *)name; @end NS_ASSUME_NONNULL_END ~~~ 在上面的代碼中,items屬性默認是nonnull的,itemWithName:方法的返回值也是nonnull,而參數是指定為nullable的。 不過,為了安全起見,蘋果還制定了幾條規則: 1. typedef定義的類型的nullability特性通常依賴于上下文,即使是在Audited Regions中,也不能假定它為nonnull。 2. 復雜的指針類型(如id *)必須顯示去指定是nonnull還是nullable。例如,指定一個指向nullable對象的nonnull指針,可以使用”__nullable id * __nonnull”。 3. 我們經常使用的NSError **通常是被假定為一個指向nullable NSError對象的nullable指針。 ### 兼容性 因為Nullability Annotations是Xcode 6.3新加入的,所以我們需要考慮之前的老代碼。實際上,蘋果已以幫我們處理好了這種兼容問題,我們可以安全地使用它們: 1. 老代碼仍然能正常工作,即使對nonnull對象使用了nil也沒有問題。 2. 老代碼在需要和swift混編時,在新的swift編譯器下會給出一個警告。 3. nonnull不會影響性能。事實上,我們仍然可以在運行時去判斷我們的對象是否為nil。 事實上,我們可以將nonnull/nullable與我們的斷言和異常一起看待,其需要處理的問題都是同一個:違反約定是一個程序員的錯誤。特別是,返回值是我們可控的東西,如果返回值是nonnull的,則我們不應該返回nil,除非是為了向后兼容。 ### 參考 1. [Nullability and Objective-C](https://developer.apple.com/swift/blog/?id=25) ## weak的生命周期 我們都知道weak表示的是一個弱引用,這個引用不會增加對象的引用計數,并且在所指向的對象被釋放之后,weak指針會被設置的為nil。weak引用通常是用于處理循環引用的問題,如代理及block的使用中,相對會較多的使用到weak。 之前對weak的實現略有了解,知道它的一個基本的生命周期,但具體是怎么實現的,了解得不是太清晰。今天又翻了翻《Objective-C高級編程》關于__weak的講解,在此做個筆記。 我們以下面這行代碼為例: **代碼清單1:示例代碼** ~~~ { id __weak obj1 = obj; } ~~~ 當我們初始化一個weak變量時,runtime會調用objc_initWeak函數。這個函數在Clang中的聲明如下: ~~~ id objc_initWeak(id *object, id value); ~~~ 其具體實現如下: ~~~ id objc_initWeak(id *object, id value) { *object = 0; return objc_storeWeak(object, value); } ~~~ 示例代碼輪換成編譯器的模擬代碼如下: ~~~ id obj1; objc_initWeak(&obj1, obj); ~~~ 因此,這里所做的事是先將obj1初始化為0(nil),然后將obj1的地址及obj作為參數傳遞給objc_storeWeak函數。 objc_initWeak函數有一個前提條件:就是object必須是一個沒有被注冊為__weak對象的有效指針。而value則可以是null,或者指向一個有效的對象。 如果value是一個空指針或者其指向的對象已經被釋放了,則object是zero-initialized的。否則,object將被注冊為一個指向value的__weak對象。而這事應該是objc_storeWeak函數干的。objc_storeWeak的函數聲明如下: ~~~ id objc_storeWeak(id *location, id value); ~~~ 其具體實現如下: ~~~ id objc_storeWeak(id *location, id newObj) { id oldObj; SideTable *oldTable; SideTable *newTable; ...... // Acquire locks for old and new values. // Order by lock address to prevent lock ordering problems. // Retry if the old value changes underneath us. retry: oldObj = *location; oldTable = SideTable::tableForPointer(oldObj); newTable = SideTable::tableForPointer(newObj); ...... if (*location != oldObj) { OSSpinLockUnlock(lock1); #if SIDE_TABLE_STRIPE > 1 if (lock1 != lock2) OSSpinLockUnlock(lock2); #endif goto retry; } if (oldObj) { weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); } if (newObj) { newObj = weak_register_no_lock(&newTable->weak_table, newObj,location); // weak_register_no_lock returns NULL if weak store should be rejected } // Do not set *location anywhere else. That would introduce a race. *location = newObj; ...... return newObj; } ~~~ 我們撇開源碼中各種鎖操作,來看看這段代碼都做了些什么。在此之前,我們先來了解下weak表和SideTable。 weak表是一個弱引用表,實現為一個weak_table_t結構體,存儲了某個對象相關的的所有的弱引用信息。其定義如下(具體定義在[objc-weak.h](http://www.opensource.apple.com/source/objc4/objc4-646/runtime/objc-weak.h)中): ~~~ struct weak_table_t { weak_entry_t *weak_entries; size_t num_entries; ...... }; ~~~ 其中weak_entry_t是存儲在弱引用表中的一個內部結構體,它負責維護和存儲指向一個對象的所有弱引用hash表。其定義如下: ~~~ struct weak_entry_t { DisguisedPtr<objc_object> referent; union { struct { weak_referrer_t *referrers; uintptr_t out_of_line : 1; ...... }; struct { // out_of_line=0 is LSB of one of these (don't care which) weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; }; }; }; ~~~ 其中referent是被引用的對象,即示例代碼中的obj對象。下面的union即存儲了所有指向該對象的弱引用。由注釋可以看到,當out_of_line等于0時,hash表被一個數組所代替。另外,所有的弱引用對象的地址都是存儲在weak_referrer_t指針的地址中。其定義如下: ~~~ typedef objc_object ** weak_referrer_t; ~~~ SideTable是一個用C++實現的類,它的具體定義在[NSObject.mm](http://opensource.apple.com/source/objc4/objc4-532.2/runtime/NSObject.mm)中,我們來看看它的一些成員變量的定義: ~~~ class SideTable { private: static uint8_t table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE]; public: RefcountMap refcnts; weak_table_t weak_table; ...... } ~~~ RefcountMap refcnts,大家應該能猜到這個做什么用的吧?看著像是引用計數什么的。哈哈,貌似就是啊,這東東存儲了一個對象的引用計數的信息。當然,我們在這里不去探究它,我們關注的是weak_table。這個成員變量指向的就是一個對象的weak表。 了解了weak表和SideTable,讓我們再回過頭來看看objc_storeWeak。首先是根據weak指針找到其指向的老的對象: ~~~ oldObj = *location; ~~~ 然后獲取到與新舊對象相關的SideTable對象: ~~~ oldTable = SideTable::tableForPointer(oldObj); newTable = SideTable::tableForPointer(newObj); ~~~ 下面要做的就是在老對象的weak表中移除指向信息,而在新對象的weak表中建立關聯信息: ~~~ if (oldObj) { weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); } if (newObj) { newObj = weak_register_no_lock(&newTable->weak_table, newObj,location); // weak_register_no_lock returns NULL if weak store should be rejected } ~~~ 接下來讓弱引用指針指向新的對象: ~~~ *location = newObj; ~~~ 最后會返回這個新對象: ~~~ return newObj; ~~~ objc_storeWeak的基本實現就是這樣。當然,在objc_initWeak中調用objc_storeWeak時,老對象是空的,所有不會執行weak_unregister_no_lock操作。 而當weak引用指向的對象被釋放時,又是如何去處理weak指針的呢?當釋放對象時,其基本流程如下: 1. 調用objc_release 2. 因為對象的引用計數為0,所以執行dealloc 3. 在dealloc中,調用了_objc_rootDealloc函數 4. 在_objc_rootDealloc中,調用了object_dispose函數 5. 調用objc_destructInstance 6. 最后調用objc_clear_deallocating 我們重點關注一下最后一步,objc_clear_deallocating的具體實現如下: ~~~ void objc_clear_deallocating(id obj) { ...... SideTable *table = SideTable::tableForPointer(obj); // clear any weak table items // clear extra retain count and deallocating bit // (fixme warn or abort if extra retain count == 0 ?) OSSpinLockLock(&table->slock); if (seen_weak_refs) { arr_clear_deallocating(&table->weak_table, obj); } ...... } ~~~ 我們可以看到,在這個函數中,首先取出對象對應的SideTable實例,如果這個對象有關聯的弱引用,則調用arr_clear_deallocating來清除對象的弱引用信息。我們來看看arr_clear_deallocating具體實現: ~~~ PRIVATE_EXTERN void arr_clear_deallocating(weak_table_t *weak_table, id referent) { { weak_entry_t *entry = weak_entry_for_referent(weak_table, referent); if (entry == NULL) { ...... return; } // zero out references for (int i = 0; i < entry->referrers.num_allocated; ++i) { id *referrer = entry->referrers.refs[i].referrer; if (referrer) { if (*referrer == referent) { *referrer = nil; } else if (*referrer) { _objc_inform("__weak variable @ %p holds %p instead of %p\n", referrer, *referrer, referent); } } } weak_entry_remove_no_lock(weak_table, entry); weak_table->num_weak_refs--; } } ~~~ 這個函數首先是找出對象對應的weak_entry_t鏈表,然后挨個將弱引用置為nil。最后清理對象的記錄。 通過上面的描述,我們基本能了解一個weak引用從生到死的過程。從這個流程可以看出,一個weak引用的處理涉及各種查表、添加與刪除操作,還是有一定消耗的。所以如果大量使用__weak變量的話,會對性能造成一定的影響。那么,我們應該在什么時候去使用weak呢?《Objective-C高級編程》給我們的建議是只在避免循環引用的時候使用__weak修飾符。 另外,在clang中,還提供了不少關于weak引用的處理函數。如objc_loadWeak, objc_destroyWeak, objc_moveWeak等,我們可以在蘋果的開源代碼中找到相關的實現。等有時間,我再好好研究研究。 ### 參考 1. 《Objective-C高級編程》1.4: __weak修飾符 2. [Clang 3.7 documentation – Objective-C Automatic Reference Counting (ARC)](http://clang.llvm.org/docs/AutomaticReferenceCounting.html) 3. [apple opensource – NSObject.mm](http://opensource.apple.com/source/objc4/objc4-532.2/runtime/NSObject.mm) ## 零碎 ### CAGradientLayer CAGradientLayer類是用于在其背景色上繪制一個顏色漸變,以填充層的整個形狀,包括圓角。這個類繼承自CALayer類,使用起來還是很方便的。 與Quartz 2D中的漸變處理類似,一個漸變有一個起始位置(startPoint)和一個結束位置(endPoint),在這兩個位置之間,我們可以指定一組顏色值(colors,元素是CGColorRef對象),可以是兩個,也可以是多個,每個顏色值會對應一個位置(locations)。另外,漸變還分為軸向漸變和徑向漸變。 我們寫個實例來看看CAGradientLayer的具體使用: ~~~ CAGradientLayer *layer = [CAGradientLayer layer]; layer.startPoint = (CGPoint){0.5f, 0.0f}; layer.endPoint = (CGPoint){0.5f, 1.0f}; layer.colors = [NSArray arrayWithObjects:(id)[UIColor blueColor].CGColor, (id)[UIColor redColor].CGColor, (id)[UIColor greenColor].CGColor, nil]; layer.locations = @[@0.0f, @0.6f, @1.0f]; layer.frame = self.view.layer.bounds; [self.view.layer insertSublayer:layer atIndex:0]; ~~~ #### 參考 1. [CAGradientLayer Class Reference](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CAGradientLayer_class/) ### Xcode中Ineligible Devices的處理 換了臺新電腦,裝了個Xcode 6.3,整了個新證書和profile,然后打開Xcode,連上手機。額,然后發現設備居然被標識為Ineligible Devices,沒認出來。情況類似于下圖: ![image](https://box.kancloud.cn/2015-08-21_55d6ccb865d8b.png) 電腦是受信任的,證書和profile也都是OK的。試了幾次重啟Xcode和重新連接手機,無效。設備就是選不了。最后是在Product->Destination里面才選中這個設備的。不過在工具欄還是不能選擇,郁悶,求解。 ### iOS 7后隱藏UITextField的光標 新項目只支持iOS 7后,很多事情變得簡單多了,就像隱藏UITextField的光標一樣,就簡單的一句話: ~~~ textFiled.tintColor = [UIColor clearColor]; ~~~ 通常我們用UIPickerView作為我們的UITextField的inputView時,我們是需要隱藏光標的。當然,如果想換個光標顏色,也是這么處理。 這么處理的有個遺留問題是:通常我們使用UIPickerView作為UITextField的inputView時, 并不希望去執行各種菜單操作(全選、復制、粘帖),但只是去設置UITextField的tintColor時,我們仍然可以執行這邊操作,所以需要加額外的處理。這個問題,我們可以這樣處理:在textFieldShouldBeginEditing:中,我們把UITextField的userInteractionEnabled設置為NO,然后在textFieldShouldEndEditing:,將將這個值設置回來。如下: ~~~ - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { textField.userInteractionEnabled = NO; return YES; } - (BOOL)textFieldShouldEndEditing:(UITextField *)textField { textField.userInteractionEnabled = YES; return YES; } ~~~ 這樣就OK了。當然這只是我們當前使用的一種處理方式,還有其它的方法,直接google或者stackoverflow吧。 ### iOS 7后UIAlertView中文字左對齊問題 在iOS 7之前,如果我們想要讓UIAlertView中的文字居左顯示的話,可以使用以下這段代碼來處理: ~~~ for (UIView *view in alert.subviews) { if([[view class] isSubclassOfClass:[UILabel class]]) { ((UILabel*)view).textAlignment = NSTextAlignmentLeft; } } ~~~ 但很遺憾的是,在iOS 7之后,蘋果不讓我們這么干了。我們去取UIAlertView的subviews時,獲得的只是一個空數組,我們沒有辦法獲取到我們想要的label。怎么辦?三條路:告訴產品經理和UED說這個實現不了(當然,這個是會被鄙視的,人家會說你能力差);自己寫;找第三方開源代碼。嘿嘿,不過由于最近時間緊,所以我決定跟他們說實現不了,哈哈。不過在github上找了一個開源的,[Custom iOS AlertView](https://github.com/wimagguc/ios-custom-alertview),star的數量也不少,看來不錯,回頭好好研究研究。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看