自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

大牛說:這是不可錯過的iOS開發(fā)技巧(三)

移動開發(fā) iOS
在iOS 7后,UIView新增加了一個tintColor屬性,這個屬性定義了一個非默認(rèn)的著色顏色值,其值的設(shè)置會影響到以視圖為根視圖的整個視圖層次結(jié)構(gòu)。它主要是應(yīng)用到諸如app圖標(biāo)、導(dǎo)航欄、按鈕等一些控件上,以獲取一些有意思的視覺效果。

Swift2出來了,還是得與時俱進(jìn)啊,不然就成老古董了。再者它開源了,又有事情要做了。當(dāng)個程序猿真是累啊,一直在追,可從來沒追上,剛有那么點(diǎn)念想了,人家又踩了腳油門。

這一期主要有三個內(nèi)容:

Tint Color

Build Configurations in Swift

鍵盤事件

Tint Color

在iOS 7后,UIView新增加了一個tintColor屬性,這個屬性定義了一個非默認(rèn)的著色顏色值,其值的設(shè)置會影響到以視圖為根視圖的整個視圖層次結(jié)構(gòu)。它主要是應(yīng)用到諸如app圖標(biāo)、導(dǎo)航欄、按鈕等一些控件上,以獲取一些有意思的視覺效果。

tintColor屬性的聲明如下:

  1. var tintColor: UIColor! 

默認(rèn)情況下,一個視圖的tintColor是為nil的,這意味著視圖將使用父視圖的tint color值。當(dāng)我們指定了一個視圖的tintColor后,這個色值會自動傳播到視圖層次結(jié)構(gòu)(以當(dāng)前視圖為根視圖)中所有的子視圖上。如果系統(tǒng)在視圖層次結(jié)構(gòu)中沒有找到一個非默認(rèn)的tintColor值,則會使用系統(tǒng)定義的顏色值(藍(lán)色,RGB值為[0,0.478431,1],我們可以在IB中看到這個顏色)。因此,這個值總是會返回一個顏色值,即我們沒有指定它。

與tintColor屬性相關(guān)的還有個tintAdjustmentMode屬性,它是一個枚舉值,定義了tint color的調(diào)整模式。其聲明如下:

  1. var tintAdjustmentMode: UIViewTintAdjustmentMode 

枚舉UIViewTintAdjustmentMode的定義如下:

  1. enum UIViewTintAdjustmentMode : Int { 
  2. case Automatic // 視圖的著色調(diào)整模式與父視圖一致 
  3. case Normal // 視圖的tintColor屬性返回完全未修改的視圖著色顏色 
  4. case Dimmed // 視圖的tintColor屬性返回一個去飽和度的、變暗的視圖著色顏色 

因此,當(dāng)tintAdjustmentMode屬性設(shè)置為Dimmed時,tintColor的顏色值會自動變暗。而如果我們在視圖層次結(jié)構(gòu)中沒有找到默認(rèn)值,則該值默認(rèn)是Normal。

與tintColor相關(guān)的還有一個tintColorDidChange方法,其聲明如下:

  1. func tintColorDidChange() 

這個方法會在視圖的tintColor或tintAdjustmentMode屬性改變時自動調(diào)用。另外,如果當(dāng)前視圖的父視圖的tintColor或tintAdjustmentMode屬性改變時,也會調(diào)用這個方法。我們可以在這個方法中根據(jù)需要去刷新我們的視圖。

示例

接下來我們通過示例來看看tintColor的強(qiáng)大功能(示例盜用了Sam Davies寫的一個例子,具體可以查看iOS7 Day-by-Day :: Day 6 :: Tint Color,我就負(fù)責(zé)搬磚,用swift實現(xiàn)了一下,代碼可以在這里下載)。

先來看看最終效果吧(以下都是盜圖,請見諒,太懶了):

這個界面包含的元素主要有UIButton, UISlider, UIProgressView, UIStepper, UIImageView, ToolBar和一個自定義的子視圖CustomView。接下來我們便來看看修改視圖的tintColor會對這些控件產(chǎn)生什么樣的影響。

在ViewController的viewDidLoad方法中,我們做了如下設(shè)置:

  1. override func viewDidLoad() { 
  2. super.viewDidLoad() 
  3.  
  4. println("\(self.view.tintAdjustmentMode.rawValue)"// 輸出:1 
  5. println("\(self.view.tintColor)"// 輸出:UIDeviceRGBColorSpace 0 0.478431 1 1 
  6.  
  7. self.view.tintAdjustmentMode = .Normal 
  8. self.dimTintSwitch?.on = false 
  9.  
  10. // 加載圖片 
  11. var shinobiHead = UIImage(named: "shinobihead"
  12. // 設(shè)置渲染模式 
  13. shinobiHead = shinobiHead?.imageWithRenderingMode(.AlwaysTemplate) 
  14.  
  15. self.tintedImageView?.image = shinobiHead 
  16. self.tintedImageView?.contentMode = .ScaleAspectFit 

首先,我們嘗試打印默認(rèn)的tintColor和tintAdjustmentMode,分別輸出了[UIDeviceRGBColorSpace 0 0.478431 1 1]和1,這是在我們沒有對整個視圖層次結(jié)構(gòu)設(shè)置任何tint color相關(guān)的值的情況下的輸出??梢钥吹剑m然我們沒有設(shè)置tintColor,但它仍然返回了系統(tǒng)的默認(rèn)值;而tintAdjustmentMode則默認(rèn)返回Normal的原始值。

接下來,我們顯式設(shè)置tintAdjustmentMode的值為Normal,同時設(shè)置UIImageView的圖片及渲染模式。

當(dāng)我們點(diǎn)擊”Change Color”按鈕時,會執(zhí)行以下的事件處理方法:

  1. @IBAction func changeColorHandler(sender: AnyObject) { 
  2.  
  3. let hue = CGFloat(arc4random() % 256) / 256.0 
  4. let saturation = CGFloat(arc4random() % 128) / 256.0 + 0.5 
  5. let brightness = CGFloat(arc4random() % 128) / 256.0 + 0.5 
  6.  
  7. let color = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1.0
  8. self.view.tintColor = color 
  9. updateViewConstraints() 
  10.  
  11. private func updateProgressViewTint() { 
  12. self.progressView?.progressTintColor = self.view.tintColor 

這段代碼主要是隨機(jī)生成一個顏色值,并賦值給self.view的tintColor屬性,同時去更新進(jìn)度條的tintColor值。

注:有些控件的特定組成部件的tint color由特定的屬性控制,例如進(jìn)度就有2個tint color:一個用于進(jìn)度條本身,另一個用于背景。

點(diǎn)擊”Change Color”按鈕,可得到以下效果:

可以看到,我們在示例中并有沒手動去設(shè)置UIButton, UISlider, UIStepper, UIImageView, ToolBar等子視圖的顏色值,但隨著self.view的tintColor屬性顏色值的變化,這些控件的外觀也同時跟著改變。也就是說self.view的tintColor屬性顏色值的變化,影響到了以self.view為根視圖的整個視圖層次結(jié)果中所有子視圖的外觀。

看來tintColor還是很強(qiáng)大的嘛。

在界面中還有個UISwitch,這個是用來開啟關(guān)閉dim tint的功能,其對應(yīng)處理方法如下:

  1. @IBAction func dimTimtHandler(sender: AnyObject) { 
  2. if let isOn = self.dimTintSwitch?.on { 
  3.  
  4. self.view.tintAdjustmentMode = isOn ? .Dimmed : .Normal 
  5.  
  6. updateViewConstraints() 

當(dāng)tintAdjustmentMode設(shè)置Dimmed時,其實際的效果是整個色值都變暗(此處無圖可盜)。

另外,我們在子視圖CustomView中重寫了tintColorDidChange方法,以監(jiān)聽tintColor的變化,以更新我們的自定義視圖,其實現(xiàn)如下:

  1. override func tintColorDidChange() { 
  2. tintColorLabel.textColor = self.tintColor 
  3. tintColorBlock.backgroundColor = self.tintColor 

所以方框和”Tint color label”顏色是跟著子視圖的tintColor來變化的,而子視圖的tintColor又是繼承自父視圖的。

在這個示例中,比較有意思的是還是對圖片的處理。對圖像的處理比較簡單粗暴,對一個像素而言,如果它的alpha值為1的話,就將它的顏色設(shè)置為tint color;如果不為1的話,則設(shè)置為透明的。示例中的忍者頭像就是這么處理的。不過我們需要設(shè)置圖片的imageWithRenderingMode屬性為AlwaysTemplate,這樣渲染圖片時會將其渲染為一個模板而忽略它的顏色信息,如代碼所示:

var shinobiHead = UIImage(named: "shinobihead")

// 設(shè)置渲染模式

shinobiHead = shinobiHead?.imageWithRenderingMode(.AlwaysTemplate)

題外話

插個題外話,跟主題關(guān)系不大。

在色彩理論(color theory)中,一個tint color是一種顏色與白色的混合。與之類似的是shade color和tone color。shade color是將顏色與黑色混合,tone color是將顏色與灰色混合。它們都是基于Hues色調(diào)的。這幾個色值的效果如下圖所示:

[[139242]]

一些基礎(chǔ)的理論知識可以參考Hues, Tints, Tones and Shades: What’s the Difference?或更專業(yè)的一些文章。

小結(jié)

如果我們想指定整個App的tint color,則可以通過設(shè)置window的tint color。這樣同一個window下的所有子視圖都會繼承此tint color。

當(dāng)彈出一個alert或者action sheet時,iOS7會自動將后面視圖的tint color變暗。此時,我們可以在自定義視圖中重寫tintColorDidChange方法來執(zhí)行我們想要的操作。

有些復(fù)雜控件,可以有多個tint color,不同的tint color控件不同的部分。如上面提到的UIProgressView,又如navigation bars, tab bars, toolbars, search bars, scope bars等,這些控件的背景著色顏色可以使用barTintColor屬性來處理。

#p#

Build Configurations in Swift

在Objective-C中,我們經(jīng)常使用預(yù)處理指令來幫助我們根據(jù)不同的平臺執(zhí)行不同的代碼,以讓我們的代碼支持不同的平臺,如:

  1. #if TARGET_OS_IPHONE 
  2.  
  3. #define MAS_VIEW UIView 
  4.  
  5. #elif TARGET_OS_MAC 
  6.  
  7. #define MAS_VIEW NSView 
  8.  
  9. #endif 

在swift中,由于對C語言支持沒有Objective-C來得那么友好(暫時不知swift 2到C的支持如何),所以我們無法像在Objective-C中那樣自如而舒坦地使用預(yù)處理指令。

不過,swift也提供了自己的方式來支持條件編譯,即使用build configurations(構(gòu)建配置)。Build configurations已經(jīng)包含了字面量true和false,以及兩個平臺測試函數(shù)os()和arch()。

其中os()用于測試系統(tǒng)類型,可傳入的參數(shù)包含OSX, iOS, watchOS,所以上面的代碼在swift可改成:

  1. #if os(iOS) 
  2. typealias MAS_VIEW = UIView 
  3. #elseif os(OSX) 
  4. typealias MAS_VIEW = NSView 
  5. #endif 

注:在WWDC 2014的“Sharing code between iOS and OS X”一節(jié)(session 233)中,Elizabeth Reid將這種方式稱為Shimming

遺憾的是,os()只能檢測系統(tǒng)類型,而無法檢測系統(tǒng)的版本,所以這些工作只能放在運(yùn)行時去處理。關(guān)于如何檢測系統(tǒng)的版本,Mattt Thompson老大在它的Swift System Version Checking一文中給了我們答案。

我們再來看看arch()。arch()用于測試CPU的架構(gòu),可傳入的值包括x86_64, arm, arm64, i386。需要注意的是arch(arm)對于ARM 64的設(shè)備來說,不會返回true。而arch(i386)在32位的iOS模擬器上編譯時會返回true。

如果我們想自定義一些在調(diào)試期間使用的編譯配置選項,則可以使用-D標(biāo)識來告訴編譯器,具體操作是在”Build Setting”–>“Swift Compiler-Custom Flags”–>“Other Swift Flags”–>“Debug”中添加所需要的配置選項。如我們想添加常用的DEGUB選項,則可以在此加上”-D DEBUG”。這樣我們就可以在代碼中來執(zhí)行一些debug與release時不同的操作,如

  1. #if DEBUG 
  2. let totalSeconds = totalMinutes 
  3. #else 
  4. let totalSeconds = totalMinutes * 60 
  5. #endif 
  6.  
  7. 一個簡單的條件編譯聲明如下所示: 
  8.  
  9. #if build configuration 
  10. statements 
  11. #else 
  12. statements 
  13. #endif 

當(dāng)然,statements中可以包含0個或多個有效的swift的statements,其中可以包括表達(dá)式、語句、和控制流語句。另外,我們也可以使用&&和||操作符來組合多個build configuration,同時,可以使用!操作符來對build configuration取反,如下所示:

  1. #if build configuration && !build configuration 
  2. statements 
  3. #elseif build configuration 
  4. statements 
  5. #else 
  6. statements 
  7. #endif 

需要注意的是,在swift中,條件編譯語句必須在語法上是有效的,因為即使這些代碼不會被編譯,swift也會對其進(jìn)行語法檢查。

#p#

鍵盤事件

在涉及到表單輸入的界面中,我們通常需要監(jiān)聽一些鍵盤事件,并根據(jù)實際需要來執(zhí)行相應(yīng)的操作。如,鍵盤彈起時,要讓我們的UIScrollView自動收縮,以能看到整個UIScrollView的內(nèi)容。為此,在UIWindow.h中定義了如下6個通知常量,來配合鍵盤在不同時間點(diǎn)的事件處理:

  1. UIKeyboardWillShowNotification // 鍵盤顯示之前 
  2. UIKeyboardDidShowNotification // 鍵盤顯示完成后 
  3. UIKeyboardWillHideNotification // 鍵盤隱藏之前 
  4. UIKeyboardDidHideNotification // 鍵盤消息之后 
  5. UIKeyboardWillChangeFrameNotification // 鍵盤大小改變之前 
  6. UIKeyboardDidChangeFrameNotification // 鍵盤大小改變之后 
  7.  
  8. 這幾個通知的object對象都是nil。而userInfo字典都包含了一些鍵盤的信息,主要是鍵盤的位置大小信息,我們可以通過使用以下的key來獲取字典中對應(yīng)的值: 
  9.  
  10. // 鍵盤在動畫開始前的frame 
  11. let UIKeyboardFrameBeginUserInfoKey: String 
  12.  
  13. // 鍵盤在動畫線束后的frame 
  14. let UIKeyboardFrameEndUserInfoKey: String 
  15.  
  16. // 鍵盤的動畫曲線 
  17. let UIKeyboardAnimationCurveUserInfoKey: String 
  18.  
  19. // 鍵盤的動畫時間 
  20. let UIKeyboardAnimationDurationUserInfoKey: String 

在此,我感興趣的是鍵盤事件的調(diào)用順序和如何獲取鍵盤的大小,以適當(dāng)?shù)恼{(diào)整視圖的大小。

從定義的鍵盤通知的類型可以看到,實際上我們關(guān)注的是三個階段的鍵盤的事件:顯示、隱藏、大小改變。在此我們設(shè)定兩個UITextField,它們的鍵盤類型不同:一個是普通鍵盤,一個是數(shù)字鍵盤。我們監(jiān)聽所有的鍵盤事件,并打印相關(guān)日志(在此就不貼代碼了),直接看結(jié)果。

1) 當(dāng)我們讓textField1獲取輸入焦點(diǎn)時,打印的日志如下:

  1. keyboard will change 
  2. keyboard will show 
  3. keyboard did change 
  4. keyboard did show 

2) 在不隱藏鍵盤的情況下,讓textField2獲取焦點(diǎn),打印的日志如下:

  1. keyboard will change 
  2. keyboard will show 
  3. keyboard did change 
  4. keyboard did show 

3) 再收起鍵盤,打印的日志如下:

  1. keyboard will change 
  2. keyboard will hide 
  3. keyboard did change 
  4. keyboard did hide 

從上面的日志可以看出,不管是鍵盤的顯示還是隱藏,都會發(fā)送大小改變的通知,而且是在show和hide的對應(yīng)事件之前。而在大小不同的鍵盤之間切換時,除了發(fā)送change事件外,還會發(fā)送show事件(不發(fā)送hide事件)。

另外還有兩點(diǎn)需要注意的是:

如果是在兩個大小相同的鍵盤之間切換,則不會發(fā)送任何消息

如果是普通鍵盤中類似于中英文鍵盤的切換,只要大小改變了,都會發(fā)送一組或多組與上面2)相同流程的消息

了解了事件的調(diào)用順序,我們就可以根據(jù)自己的需要來決定在哪個消息處理方法中來執(zhí)行操作。為此,我們需要獲取一些有用的信息。這些信息是封裝在通知的userInfo中,通過上面常量key來獲取相關(guān)的值。通常我們關(guān)心的是UIKeyboardFrameEndUserInfoKey,來獲取動畫完成后,鍵盤的frame,以此來計算我們的scroll view的高度。另外,我們可能希望scroll view高度的變化也是通過動畫來過渡的,此時UIKeyboardAnimationCurveUserInfoKey和UIKeyboardAnimationDurationUserInfoKey就有用了。

我們可以通過以下方式來獲取這些值:

  1. if let dict = notification.userInfo { 
  2.  
  3. var animationDuration: NSTimeInterval = 0 
  4. var animationCurve: UIViewAnimationCurve = .EaseInOut 
  5. var keyboardEndFrame: CGRect = CGRectZero 
  6.  
  7. dict[UIKeyboardAnimationCurveUserInfoKey]?.getValue(&animationCurve) 
  8. dict[UIKeyboardAnimationDurationUserInfoKey]?.getValue(&animationDuration) 
  9. dict[UIKeyboardFrameEndUserInfoKey]?.getValue(&keyboardEndFrame) 
  10.  
  11. ...... 

實際上,userInfo中還有另外三個值,只不過這幾個值從iOS 3.2開始就已經(jīng)廢棄不用了。所以我們不用太關(guān)注。

***說下表單。一個表單界面看著比較簡單,但交互和UI總是能想出各種方法來讓它變得復(fù)雜,而且其實里面設(shè)計到的細(xì)節(jié)還是很多的。像我們金融類的App,通常都會涉及到大量的表單輸入,所以如何做好,還是需要花一番心思的??臻e時,打算總結(jié)一下,寫一篇文章。

#p#

零碎

自定義UIPickerView的行

UIPickerView的主要內(nèi)容實際上并不多,主要是一個UIPickerView類和對應(yīng)的UIPickerViewDelegate,UIPickerViewDataSource協(xié)議,分別表示代理和數(shù)據(jù)源。在此不細(xì)說這些,只是解答我們遇到的一個小需求。

通常,UIPickerView是可以定義多列內(nèi)容的,比如年、月、日三列,這些列之間相互不干擾,可以自已滾自己的,不礙別人的事。不過,我們有這么一個需求,也是有三列,但這三列需要一起滾。嗯,這個就需要另行處理了。

  1. 在UIPickerViewDelegate中,聲明了下面這樣一個代理方法: 
  2.  
  3. - (UIView *)pickerView:(UIPickerView *)pickerView 
  4. viewForRow:(NSInteger)row 
  5. forComponent:(NSInteger)component 
  6. reusingView:(UIView *)view 

我們通過這個方法就可以來自定義行的視圖。時間不早,廢話就不多說了,直接上代碼吧:

  1. - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view { 
  2.  
  3. PickerViewCell *pickerCell = (PickerViewCell *)view; 
  4.  
  5. if (!pickerCell) { 
  6.  
  7. NSInteger column = 3
  8.  
  9. pickerCell = [[PickerViewCell alloc] initWithFrame:(CGRect){CGPointZero, [UIScreen mainScreen].bounds.size.width, 45.0f} column:column]; 
  10.  
  11. [pickerCell setLabelTexts:@[...]]; 
  12.  
  13. return pickerCell; 

我們定義了一個PickerViewCell視圖,里面根據(jù)我們的傳入的column參數(shù)來等分放置column個UILabel,并通過setLabelTexts來設(shè)置每個UILabel的文本。當(dāng)然,我們也可以在PickerViewCell去定義UILabel的外觀顯示。就是這么簡單。

不過,還有個需要注意的就是,雖然看上去是顯示了3列,但實際上是按1列來處理的,所以下面的實現(xiàn)應(yīng)該是返回1:

  1. - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { 
  2. return 1

Constructing an object of class type ‘**’ with a metatype value must use a ‘required’ initializer.

Swift中”[AnyObject]? does not have a member named generator” 問題的處理

有個小需求,需要遍歷當(dāng)前導(dǎo)航控制器棧的所有ViewController。UINavigationController類自身的viewControllers屬性返回的是一個[AnyObject]!數(shù)組,不過由于我的導(dǎo)航控制器本身有可能是nil,所以我獲取到的ViewController數(shù)組如下:

  1. var myViewControllers: [AnyObject]? = navigationController?.viewControllers 
  2.  
  3. 獲取到的myViewControllers是一個[AnyObject]?可選類型,這時如果我直接去遍歷myViewControllers,如下代碼所示 
  4.  
  5. for controller in myViewControllers { 
  6. ... 

編譯器會報錯,提示如下:

  1. [AnyObject]? does not have a member named "Generator" 

實際上,不管是[AnyObject]?還是其它的諸如[String]?類型,都會報這個錯。其原因是可選類型只是個容器,它與其所包裝的值是不同的類型,也就是說[AnyObject]是一個數(shù)組類型,但[AnyObject]?并不是數(shù)組類型。我們可以迭代一個數(shù)組,但不是迭代一個非集合類型。

在stackoverflow上有這樣一個有趣的比方,我犯懶就直接貼出來了:

To understand the difference, let me make a real life example: you buy a new TV on ebay, the package is shipped to you, the first thing you do is to check if the package (the optional) is empty (nil). Once you verify that the TV is inside, you have to unwrap it, and put the box aside. You cannot use the TV while it's in the package. Similarly, an optional is a container: it is not the value it contains, and it doesn't have the same type. It can be empty, or it can contain a valid value.

所有,這里的處理應(yīng)該是:

  1. if let controllers = myViewControllers { 
  2. for controller in controllers { 
  3. ...... 
責(zé)任編輯:chenqingxiang 來源: 南峰子的技術(shù)博客
相關(guān)推薦

2015-07-06 10:09:33

iosFoundationNSHashTable

2015-07-07 10:15:56

iOSUIVisualEffweak

2019-07-23 09:00:00

vuejavascript前端

2015-10-21 13:42:54

iOS開發(fā)watch OS2

2014-07-23 10:08:34

Angular前端項目

2015-06-10 10:56:50

iOS開發(fā)技巧

2024-01-09 18:01:38

2024-08-13 08:00:00

2021-10-27 08:00:00

DevSecOps開發(fā)安全

2015-07-28 20:34:01

Android開發(fā)框架

2016-10-25 14:27:16

開源Ruby on RaiWeb框架

2015-10-14 10:54:20

iOS開發(fā)讀書

2016-12-01 08:36:18

編程云環(huán)境云戰(zhàn)略

2025-03-24 15:00:13

鴻蒙HarmonyOS

2020-03-05 12:12:54

數(shù)據(jù)Python開發(fā)

2021-04-21 07:51:06

Vue 開發(fā)VS CodeVetur

2015-08-27 11:16:14

ios開發(fā)技巧

2018-10-23 10:35:20

react.jsReact面試題前端

2021-10-18 22:07:05

裝機(jī)顯卡硬件

2021-01-05 05:15:02

Github 前端倉庫
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號