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
}