地理坐標系使用三維球面來定義地球上的位置,單位即經緯度。但經緯度無法精確測量距離戒面積,也難以在平面地圖戒計算機屏幕上顯示數據。通過投影的方式可以將其轉換成平面的投影坐標系,不同的投影方式可能會帶來不同的變形及誤差,類似於把一個橘子的橘子皮剝開攤平到桌面。
GPS以及iOS系統定位獲得的坐標是地理坐標系WGS1984,Web地圖一般用的坐標細是投影坐標系WGS 1984 Web Mercator,國內出於相關法律法規要求,對國內所有GPS設備及地圖數據都進行了加密偏移處理,代號GCJ-02,這樣GPS定位獲得的坐標與地圖上的位置剛好對應上,特殊的是百度地圖在這基礎上又進行一次偏移,所以在處理系統定位坐標及相關地圖SDK坐標時需要轉換處理下,根據網絡資源,目前有一些公開的轉換算法。
蘋果地圖及谷歌地圖用的都是高德地圖的數據,所以這三種情況坐標處理方法一樣,即將WGS1984坐標轉換成偏移後的GCJ-02才可以在地圖上正確顯示位置。
const double a = 6378245.0; const double ee = 0.00669342162296594323; + (CLLocationCoordinate2D)transform:(CLLocationCoordinate2D) latLng { double wgLat = latLng.latitude; double wgLon = latLng.longitude; double mgLat; double mgLon; if ([self outOfChina:wgLat :wgLon ]) { return latLng; } double dLat = [self transformLat:wgLon-105.0 :wgLat - 35 ]; double dLon = [self transformLon:wgLon-105.0 :wgLat - 35 ]; double radLat = wgLat / 180.0 * M_PI; double magic = sin(radLat); magic = 1 - ee * magic * magic; double sqrtMagic = sqrt(magic); dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * M_PI); dLon = (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * M_PI); mgLat = wgLat + dLat; mgLon = wgLon + dLon; CLLocationCoordinate2D loc2D ; loc2D.latitude = mgLat; loc2D.longitude = mgLon; return loc2D; } + (BOOL) outOfChina:(double) lat :(double) lon { if (lon < 72.004 || lon > 137.8347) { return true; } if (lat < 0.8293 || lat > 55.8271) { return true; } return false; } + (double) transformLat:(double)x :(double) y { double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(fabs(x)); ret += (20.0 * sin(6.0 * x * M_PI) + 20.0 *sin(2.0 * x *M_PI)) * 2.0 / 3.0; ret += (20.0 * sin(y * M_PI) + 40.0 *sin(y / 3.0 *M_PI)) * 2.0 / 3.0; ret += (160.0 * sin(y / 12.0 * M_PI) + 320 *sin(y * M_PI / 30.0)) * 2.0 / 3.0; return ret; } + (double) transformLon:(double) x :(double) y { double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(fabs(x)); ret += (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0; ret += (20.0 * sin(x * M_PI) + 40.0 * sin(x / 3.0 * M_PI)) * 2.0 / 3.0; ret += (150.0 * sin(x / 12.0 *M_PI) + 300.0 *sin(x / 30.0 * M_PI)) * 2.0 / 3.0; return ret; }
使用百度地圖SDK,定位也使用sdk中提供的方法,則獲得坐標是百度在GCJ-02基礎上再一次偏移的坐標,如果要將定位坐標顯示在蘋果地圖上,則需要轉換成GCJ-02下的坐標,JZLocationConverter提供了三種坐標間的轉換方法,
iOS 地圖坐標系轉換
iOS經緯度偏移解決
使用block需要注意避免引用循環造成內存問題,如圖所示,對於strong屬性的property或者復用的cell,都是被self持有或者說強引用的,在使用block回調前,需要將self聲明為一個weak類型的weakSelf再進行使用,否則將造成引用循環。
但並不是所有block回調處理中都需要使用weakSelf,例如self並沒有強引用vc,在vc裡使用self,是不會造成引用循環的。
對於類似這種可能造成引用循環的地方,使用Instrumens的Leaks工具是檢測不出來的,比較便捷的方法是在controller檢測“dealloc”是否執行,當然也有可能是其它原因造成,但只要有引用循環發生則controller肯定無法釋放,“dealloc”也是無法執行的。
目前用到了一些第三方的UICollectionviewLayout類庫,例如CSStickyHeaderFlowLayout,提供header懸停及下拉放大的視差效果等
通過自定義UICollectionviewLayout可以靈活自定義很多布局效果,以經典的瀑布流為例。
其特點是cell的高度不一致,首尾間隔要保持一致,如此一來就需要自定義layout來調整cell的布局位置。
這裡cell高度隨機生成為40-140之間的數值。
重點是計算布局這裡,第一行Y值都相同,但後續每個cell的高度不同,需要去調整cell的位置,首先找到目前高度最小的那一列(排列第一行時,所有cell的Y值一致,將第一列當做高度最小的一列或者將頂部間距看成是一行高度一致的cell),將cell排列在此列下方,並更新此列的Y值,然後再繼續找高度最小的一列,將cell排列在其下方並更新此列Y值,以此類推,不斷尋找高度最小的那一列,將後續cell排列在其下方並更新這一列的Y值,即可完成布局,當然設置cell布局時還要將cell之間的間隔考慮進去。
contentSize中關鍵是高度,這裡取高度最大那一列的Y值即為collectionview的高度。
iOS 瀑布流基本實現
使用Instruments中的Core Animation工具能夠檢測圖層渲染和動畫的相關問題,包括圖層混合問題,即當多個圖層疊加在一起,顏色不同時,處理這樣的顏色混合情況會消耗GPU資源,監測發現這些區域會變紅,其它正常區域為綠色
對於UILabel的圖層混合問題,將其設置為與背景色一致並裁剪即可。(不會產生離屏渲染)
參考:Color Blended Layers