1. 程式人生 > >iOS開發中遇到的那些坑

iOS開發中遇到的那些坑

最近重新拿起了iOS的開發,使用OC和Swift混編,碰到了一些比較棘手的問題,希望大家以後不再入坑。

這篇文章的內容包含:

  • UITableViewCell的真實結構

  • 在iOS的環境下使用正則表示式

  • 如何優雅的隱藏tabbar

  • 如何修改navigationbar的顏色

  • 執行時修改使用autolayout的view

  • navigation controller子view偏移問題

  • CoreData的使用總結

1.UITableViewCell的真實結構

目標是想把UITableview在editing模式下的drag按鈕去掉,換成自己的樣式,但又想保留原生拖動排序的行為,所以研究了很多方法,最後還是找到了UITableViewCell的結構,並將拖動按鈕替換。不多說,上程式碼。

//打印出來的自定義的cell在editing模式下的結構

(lldb) po self

<QKZhi_iOS.EditingCell: 0x7c148800; baseClass = UITableViewCell; frame = (0 0; 320 56); autoresize = W; layer = <CALayer: 0x7d086b40>>

(lldb) po self.subviews

5 elements

- [0] : <UITableViewCellContentView: 0x7d086d10; frame = (38 0; 242 55.5); opaque = NO; gestureRecognizers = <NSArray: 0x7d0882b0>; layer = <CALayer: 0x7d086de0>>

- [1] : <_UITableViewCellSeparatorView: 0x7d087c40; frame = (15 55; 305 1); layer = <CALayer: 0x7d0852c0>>

- [2] : <_UITableViewCellSeparatorView: 0x7b163240; frame = (15 55.5; 305 0.5); layer = <CALayer: 0x7b162de0>>

- [3] : <UITableViewCellEditControl: 0x7b164700; frame = (0 0; 47 56); opaque = NO; layer = <CALayer: 0x7b164950>>

- [4] : <UITableViewCellReorderControl: 0x7b164d10; frame = (268 0; 52 56); opaque = NO; autoresize = LM; layer = <CALayer: 0x7b164f60>>

(lldb) po self.subviews.last

Optional<UIView>

- Some : <UITableViewCellReorderControl: 0x7b164d10; frame = (268 0; 52 56); opaque = NO; autoresize = LM; layer = <CALayer: 0x7b164f60>>

(lldb) po self.subviews.last?.subviews

Optional<Array<UIView>>

Some : 1 elements

- [0] : <UIImageView: 0x7b165130; frame = (15 24; 22 8.5); opaque = NO; userInteractionEnabled = NO; tag = 1; layer = <CALayer: 0x7b165210>>

(lldb)

可以看到此時contentView左右都向內縮進了一定的距離,最後有一個view叫UITableViewCellReorderControl,我想就是它了,然後看它的subviews,竟然包含了一個UIImageView,果斷替換之,程式碼如下。

override func layoutSubviews() {

super.layoutSubviews()

setupReorderControl()

}

func setupReorderControl() {

if (self.reorderControl != nil) {

return;

}

for view in self.subviews {

if view.description.containsString("UITableViewCellReorderControl") {

self.reorderControl = view

}

}

if ((self.reorderControl) != nil)

{

let imageOfReorder = self.reorderControl?.subviews[0] as? UIImageView

imageOfReorder?.removeFromSuperview()

}

}

此時就完成了將拖動按鈕隱藏的功能,但是注意,在這裡如果想通過設定reorderControl的frame去改變它的位置是不成功的,我想可能它的佈局使用autolayout,並沒有深入的再去研究。

2. 在iOS的環境下使用正則表示式

在iOS下使用正則似乎挺麻煩的,如果只是想用正則判斷格式,那麼可以使用NSPredicate:

let string = "abcd"

let predicate = NSPredicate.init(format: "self matches %@", "^a(b|c)d$")

predicate.evaluateWithObject(string)

如果要使用正則判斷格式,然後獲取group的子字串,就比較麻煩了,並沒有Swift的原生方法來做,必須使用NSRegularExpression,話不多說,上程式碼:

let reg = try! NSRegularExpression.init(pattern: "^a(b|c)d$", options: NSRegularExpressionOptions.CaseInsensitive)

let matches = reg.matchesInString(string, options: NSMatchingOptions.init(rawValue: 0), range: NSMakeRange(0, string.characters.count))

if matches?.count > 0 {

let returnString = (string as NSString).substringWithRange(matches![0].rangeAtIndex(1))

}

3. 如何優雅的隱藏tabbar

很多APP都使用TabBarController套NavigationController的方法來作為應用的框架,那麼隱藏TabBar就成了一個必要的功能,目前最簡單的方法還是使用hidesBottomBarWhenPushed來實現,最簡單的方法就是在要隱藏tab bar的Controller裡寫入下面的方法,來覆預設值。

- (BOOL) hidesBottomBarWhenPushed {

return (self.navigationController.topViewController == self);

}

4. 如何修改navigationbar的顏色

[self.navigationController.navigationBar setTranslucent:NO];

self.navigationController.navigationBar.barTintColor = [UIColor redColor];

//在此方法中還原顏色

-(void)viewWillDisappear:(BOOL)animated{

self.navigationController.navigationBar.barTintColor = [UIColor whiteColor];

[super viewWillDisappear:animated];

}

5. 執行時修改使用autolayout的view

方案是執行時給view新增constraint。

NSLayoutConstraint *c = [NSLayoutConstraint constraintWithItem:view1

attribute:NSLayoutAttributeTop

relatedBy:NSLayoutRelationEqual

toItem: view2

attribute: NSLayoutAttributeTop

multiplier:1

constant:2];

view1(的頂部) = view2(的頂部) * 1 + 2

這個程式碼的意思是把view1的頂部放在view2的頂部向下兩個畫素的地方(因為此時view1是view2 的subview,所以達到的效果就是view1在view2頂部向下2個畫素的地方)

如下是所有屬性:

NSLayoutAttributeLeft    檢視的左邊

NSLayoutAttributeRight    檢視的右邊

NSLayoutAttributeTop    檢視的上邊

NSLayoutAttributeBottom    檢視的下邊

NSLayoutAttributeLeading    檢視的前邊

NSLayoutAttributeTrailing    檢視的後邊

NSLayoutAttributeWidth    檢視的寬度

NSLayoutAttributeHeight    檢視的高度

NSLayoutAttributeCenterX    檢視的中點的X值

NSLayoutAttributeCenterY    檢視中點的Y值

NSLayoutAttributeBaseline    檢視的基準線

NSLayoutAttributeNotAnAttribute    無屬性

6. navigation controller子view偏移問題

場景是這樣的:頁面使用UItableview佈局,由於頁面比較複雜,在push進去再出來時第一個cell會向下偏移。解決方案:

//在viewDidLoad中加入:

if ([self respondsToSelector:@selector(edgesForExtendedLayout)]){

self.edgesForExtendedLayout = UIRectEdgeNone;

}

7. CoreData的使用總結

//

//  CoreDataHelper.swift

//  TestCode

//

//  Created by lk on 16/7/6.

//  Copyright ? 2016年 lk. All rights reserved.

//

import Foundation

import CoreData

@objc class CoreDataHelper: NSObject {

static let KEY_OF_MAX_ORDER_NUMBER_OF_I = "MIK"

static let KEY_OF_MAX_ORDER_NUMBER_OF_O = "MOK"

var context : NSManagedObjectContext!

static let sharedInstance = CoreDataHelper()

private func fetchQuery(request request : NSFetchRequest) -> [NSManagedObject] {

do{

let fetchResults = try context?.executeFetchRequest(request) as! [NSManagedObject]!

return fetchResults

}catch let error as NSError {

print("查詢失敗: \\(error)")

}

return []

}

private func save() -> Void {

//儲存

do{

try context.save()

} catch let error as NSError {

print("儲存失敗: \\(error)")

}

}

//TODO: - 這裡可能存在溢位漏洞,以後再說

private func addIncrement(key _key : String, newNumber : Int) -> Void {

if _key == CoreDataHelper. KEY_OF_MAX_ORDER_NUMBER_OF_O || _key == CoreDataHelper.KEY_OF_MAX_ORDER_NUMBER_OF_I {

NSUserDefaults.standardUserDefaults().setInteger(newNumber, forKey: _key)

}

}

//Mark: - 重新計算最大值

func recaculateOptionalMaxOrder() -> Void {

let topOptinal = getOptionals(count: 1)

if topOptinal.count > 0 {

NSUserDefaults.standardUserDefaults().setInteger(topOptinal[0].order!.integerValue, forKey: CoreDataHelper.KEY_OF_MAX_ORDER_NUMBER_OF_O)

}

}

//MARK: - 新增

func addXXX(code code: String, name: String) -> Void {

if (context == nil) {

print("新增失敗,context為nil")

return

}

let s = NSEntityDescription.insertNewObjectForEntityForName("S", inManagedObjectContext: context) as! S

//物件賦值

s.code = code

s.name = name

s.numericalCode = info.code

s.type = info.type

save()

}

//MARK: - 刪除

func removeAll() -> Void {

let fetchRequest = NSFetchRequest(entityName: "S")

let fetchResults = fetchQuery(request: fetchRequest)

for info:NSManagedObject in fetchResults {

//刪除物件

context.deleteObject(info)

}

save()

}

//MARK: - 查詢

func getObject(code code : String) -> S? {

if (context == nil) {

print("查詢失敗,context為nil")

return nil;

}

let fetchRequest = NSFetchRequest(entityName: "S")

let predicate = NSPredicate(format: "code == %@ || numericalCode == %@", code, code)

fetchRequest.predicate = predicate

let fetchResults = fetchQuery(request: fetchRequest)

if let ses = fetchResults as? [S] {

if ses.count > 0{

return ses[0]

}else{

return nil

}

}

return nil

}

//MARK: - 搜尋

func searchStocks(filterString : String) -> [Stock] {

var attribute = "code";

let num = Int(filterString)

if filterString.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) <= 0 {

return []

} else if num == nil {

//TODO

attribute = "name"

}

let fetchRequest = NSFetchRequest(entityName: "S")

fetchRequest.predicate = NSPredicate.init(format: "%K CONTAINS %@", attribute, filterString)

let fetchResults = fetchQuery(request: fetchRequest)

print("結果:")

print(fetchResults)

var ses = [S]()

for res in fetchResults {

let s = res as! S

ses.append(s)

}

return ses

}