有時間了來整理一下一些小工具。
之前沒事的時候做了一個Demo,能夠輕松實現可拖動的視圖。DraggableViewDemo。
下載地址:https://github.com/anjohnlv/DraggableViewDemo
使用方法:
1、下載并導入分類UIView+Draggable。
2、對需要拖動的控件設置draggingType。
支持繼承于UIView的所有控件如UIButton、UITableView、ContainerView等。設置拖動后將優先響應拖動,繼承于UIScrollView的控件如UITableView,UITextView等將無法滾動,拖動結束可通過設置draggingType=DraggingTypeDisabled來恢復UIScrollView的滾動事件。
支持autolayout,autosizing。被拖動的控件會自動移除邊距約束。
支持代碼,也支持interface builder。
可設置類型包括:
/**
拖拽方式
- DraggingTypeDisabled :不能拖拽
- DraggingTypeNormal: 正常拖拽
- DraggingTypeRevert: 釋放后還原
- DraggingTypePullOver: 自動靠邊,只會靠左右兩邊
- DraggingTypeAdsorb: 靠邊時自動吸附邊緣,可吸附四周
*/
typedef NS_ENUM(NSUInteger, DraggingType) {
DraggingTypeDisabled,
DraggingTypeNormal,
DraggingTypeRevert,
DraggingTypePullOver,
DraggingTypeAdsorb,
};
可設置拖動范圍
/**
是否可只能在subView的范圍內,默認是NO。
@warning 如果NO,超出subView范圍的部分無法響應拖拽。剪裁超出部分可直接使用superView.clipsToBounds=YES
*/
@property(nonatomic)BOOL draggingInBounds;
可主動操作View
/**
主動靠邊并吸附
*/
-(void)adsorbingAnimated:(BOOL)animated;
/**
主動靠邊
*/
-(void)pullOverAnimated:(BOOL)animated;
/**
主動還原位置
*/
-(void)revertAnimated:(BOOL)animated;
可監聽拖動事件以實現更多自定義功能
-(void)draggingDidBegan:(UIView *)view;
-(void)draggingDidChanged:(UIView *)view;
-(void)draggingDidEnded:(UIView *)view;
原理很簡單,就是給View加了一個UIPanGestureRecognizer
,然后根據拖動類型,對拖動狀態進行了不同的處理。
有三個比較有意思的地方:
第一是UIPanGestureRecognizer
會與UIScrollView
等自帶UIPanGestureRecognizer
手勢的控件沖突。于是我放棄了從View
上面遍歷UIPanGestureRecognizer
的方法來獲取或新建手勢。而是在運行時保存用于拖拽的手勢單獨處理。
-(UIPanGestureRecognizer *)panGesture {
return objc_getAssociatedObject(self, kPanKey);
}
-(void)setPanGesture:(UIPanGestureRecognizer *)panGesture {
objc_setAssociatedObject(self, kPanKey, panGesture, OBJC_ASSOCIATION_ASSIGN);
}
第二是如果用戶對需要拖動的View進行了autoLayout約束,在某些特定的時刻會出現很奇妙的現象。其實想也明白,既然要拖動View,肯定不能約束View的位置。于是我刪除了View的約束
[self removeConstraints:self.constraints];
for (NSLayoutConstraint *constraint in self.superview.constraints) {
if ([constraint.firstItem isEqual:self]) {
[self.superview removeConstraint:constraint];
}
}
后發現約束刪了,并不是沒了。而是變成了((0,0),(0,0))左上角,大小為0的約束。
如果設置
[self setTranslatesAutoresizingMaskIntoConstraints:YES];
又會報約束重復異常。其實把兩段加一起就好了。
第三是在設置View靠邊吸附的時候,想著吸附一半,另一半超出了superview就不顯示,是重新繪制呢還是設置superview.clipToBounds=YES;
呢?因為是使用的分類,重寫方法的話會影響所有的UIView,感覺不好,設置superview的clipToBounds的話,會影響到superview上的其他view。也不好。最后采用的方法是給superview添加了一個子viewA,大小為拖動的viewB吸附后的大小,然后把viewB,放到viewA上。并設置viewA.clipToBounds=YES;
。如此模擬除了吸附的效果。
說完了,如果有什么不對的地方歡迎斧正。