当前位置:网站首页>Image editor for their AutoLayout environment
Image editor for their AutoLayout environment
2022-07-05 21:43:00 【Full stack programmer webmaster】
Hello everyone , I meet you again , I'm the king of the whole stack , I've prepared for you today Idea Registration code .
hi,all:
After some hesitation . I decided to make this small by myself APP The source code of is released to you :
The starting point is to study with everyone iOS Development . For reference only .
The previous code was managed with gitlab
Upper , Today I will pull To github Come up. , You can download by yourself :git clone [email protected]:lihux/twentyThousandTomatoes.git No installation git Or children's shoes that can't be used ,
Please stab github Address :https://github.com/lihux/twentyThousandTomatoes. Go in and choose
Choose download zip Download it .
In most APP( Especially social , Such as qq) There are often scenes of changing avatars : Click user
Load Avatar , Load the system image , After the user clicks and selects a picture . Be able to zoom and
Drag the , The part of the picture enclosed by the circular clipping box has been changed .
For example, the following figure is qq Avatar selection and editing interface :
chart 1.qq Photo editing interface
The picture can be enlarged in the interface 、 narrow , Drag the , The white circle area indicates that when you click OK, you will
The range of tailoring . Pay attention to the animation above ,qq It is always possible to ensure that the circle is completely covered by the picture , Suppose you drag
Or zoom in and out so that the black area outside the picture enters the circle . The picture will bounce back on its own initiative, just enough to completely cover
The state of . Whereas CSDN To upload pictures 2M The limitation of , above gif The picture is very short . Interested students can open
QQ Experience it for yourself ( In the function of changing personal avatar ).
Now we also want to implement an interface with similar functions . And it is in autolayout In the environment . At the same time
Hold horizontal and vertical screens . This is more than QQ The image selection page of is a little more complicated :QQ Only support vertical screen , No need
Consider the situation of horizontal screen and the problem of horizontal and vertical screen switching .
The following is a detailed discussion .
One 、 Expected effect
Users choose from photo albums or cameras / Take a picture , Load into the image editing interface , Users can drag
dynamic 、 Zoom in and out . Make the screenshot in the circular selection box to the appropriate image as the user's Avatar . The effect picture is shown in the following figure
in :
The user is dragging 、 When zooming in and out, make sure that all the ring areas are covered by the pictures . Only in this way can we ensure that
The photos can just cover the whole circular area . At the same time , Because we support horizontal screen layout . So make sure
Vertical screen switch horizontal screen ( Or vice versa ) after . The ring is still in the right area .
chart 2. Vertical screen effect
chart 3. Horizontal screen effect
The whole interface meets the above user interaction requirements . And when the user clicks ok , Put the circle
Cut out the picture of the shape area , Realize the function of picture editing .
Two 、 Implementation details
2.1 The basic idea
On the implementation , This page can be divided into two parts : One is scrollview Set up :contentSize、
contentInset、zoomScale wait ; Another is the implementation of the clipping box ( White circle 、 The periphery is translucent
layer ), And how the clipping box changes when switching between horizontal and vertical screens ; And these two pieces are not completely independent :scrollview
A lot of interaction depends on clipping boxes : The minimum zoom cannot be smaller than the cut box 、 The movement cannot exceed the range of the clipping box
Wai et al .
Can feel .scrollview The properties of depend on the properties of the clipping box .
The clipping box is on the horizontal or vertical screen
The size and position remain unchanged . therefore , We naturally get such an idea : First determine the shear
box . The horizontal and vertical screens are all right , Then determine through the clipping box scrollview.
2.2 Implementation of clipping box
From Figure 2, we can see that the clipping box is a special interface : The inside of the circular dotted box is completely transparent
Of (clearColor or alpha = 0), And the filling part of the periphery is translucent (blackColor and
alpha = 0.2). Pass routinely view Nesting settings for alpha、backgroundColor and layer.cornerRadius
You can't . because view Of alpha Properties are “ genetic ”: Father view Of alpha Will act directly on all
The son of view Up , At this time, we need to consider drawing directly in a view Cut after loading
Frame cutting drawing .
We are storyboard Add a view( be called :maskView). Add constraints to make it consistent with scrollview
size 、 The size is completely consistent . Put this view Of class Change it to TTPhotoMaskView: One of us
custom view, In its drawRect In the method . Draw a clipping box , Draw schematic legend as follows :
chart 4. Cut box drawing
1. Draw two closed lines , One is square . Just cover the whole view The boundary of the . Another one is round
Shaped dotted line clipping box ;
2. Use the parity principle to fill the two closed curves . Make the area between the box and the circular box
fill ( black .alpha=0.2), The inside of the circular box is not filled ( transparent ).
The detailed implementation code is as follows :
<span style="font-size:18px;">-(void)drawRect:(CGRect)rect
{
CGFloat width = rect.size.width;
CGFloat height = rect.size.height;
//pickingFieldWidth: Diameter of circular frame
CGFloat pickingFieldWidth = width < height ? (width - kWidthGap) : (height - kHeightGap); CGContextRef contextRef = UIGraphicsGetCurrentContext(); CGContextSaveGState(contextRef); CGContextSetRGBFillColor(contextRef, 0, 0, 0, 0.35); CGContextSetLineWidth(contextRef, 3); // Calculate the circumscribed square of the circular box frame: self.pickingFieldRect = CGRectMake((width - pickingFieldWidth) / 2, (height - pickingFieldWidth) / 2, pickingFieldWidth, pickingFieldWidth); // Create a circular box UIBezierPath: UIBezierPath *pickingFieldPath = [UIBezierPath bezierPathWithOvalInRect:self.pickingFieldRect]; // Create a peripheral generous box UIBezierPath: UIBezierPath *bezierPathRect = [UIBezierPath bezierPathWithRect:rect]; // Put the circular box path Add to the generosity box path Up , In order to fill the area with the odd and even filling rule : [bezierPathRect appendPath:pickingFieldPath]; // Filling uses the law of parity bezierPathRect.usesEvenOddFillRule = YES; [bezierPathRect fill]; CGContextSetLineWidth(contextRef, 2); CGContextSetRGBStrokeColor(contextRef, 255, 255, 255, 1); CGFloat dash[2] = {4,4}; [pickingFieldPath setLineDash:dash count:2 phase:0]; [pickingFieldPath stroke]; CGContextRestoreGState(contextRef); self.layer.contentsGravity = kCAGravityCenter;}</span>
Now let's consider how to deal with the horizontal and vertical screens : Our clipping box is directly through UIView Of drawRect
The method is directly hand drawn , Therefore, you can't take the initiative to layout by yourself (autolayout) Layout the clipping box again .
The solution is to draw a circular clipping box again when the screen switches horizontally and vertically . stay iOS8 No longer make
use willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration To get the screen rotation event .iOS8 Later use new
willTransitionToTraitCollection:(UITraitCollection *)newCollection
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
To replace .
So we are in this method , Force crop box redrawing (maskview):
<span style="font-size:18px;">#pragma mark - UIContentContainer protocol- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator{ [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator]; [self.maskView setNeedsDisplay];}</span>
In this way, our clipping box is finished smoothly , Next let's set up scrollview, Make it satisfy me
Our interaction expectations .
2.3scrollview Set up
First, let's take a look at the whole view Hierarchical structure :scrollview There is one that fills the whole scrollview Of
imageView As scrollview Of content view. stay scrollView Covered with a clipping box view
(mask view), These three view Are maintained by constraints and roots view Of bounds Agreement .
chart 5.view Hierarchical structure
above-mentioned ,scrollview The setting of various properties of depends on the hand drawn clipping box . And round
Position of the clipping box 、 The size may change after each screen rotation , So we have to do it every time maskView
Of drawRect After the method call, adjust it again scrollview Properties of .
So we have maskView
Add an agent in , Set this proxy to maskview Where viewController. Every time when redrawing
Notify through proxy method after occurrence viewcontroller adjustment scrollview The properties of :
<span style="font-size:18px;">// [email protected] TTPhotoMaskViewDelegate <NSObject>- (void)pickingFieldRectChangedTo:(CGRect) rect;@[email protected] TTPhotoMaskView : [email protected] (nonatomic, weak) id <TTPhotoMaskViewDelegate> delegate;@end</span>
stay maskView Of drawRect Add... To the method : among pickingFieldRect That is, the ring clipping box
Of “frame”, Including its relative to maskView Of origin and size Information .
<span style="font-size:18px;"> if ([self.delegate respondsToSelector:@selector(pickingFieldRectChangedTo:)]) { [self.delegate pickingFieldRectChangedTo:self.pickingFieldRect]; }</span>
Next is in our viewController To realize pickingFieldRectChangedTo Method ,
adjustment scrollView:
<span style="font-size:18px;">#pragma mark - TTPhotoMaskViewDelegate- (void)pickingFieldRectChangedTo:(CGRect)rect{ self.pickingFieldRect = rect; CGFloat topGap = rect.origin.y; CGFloat leftGap = rect.origin.x; self.scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(topGap, leftGap, topGap, leftGap); //step 1: setup contentInset self.scrollView.contentInset = UIEdgeInsetsMake(topGap, leftGap, topGap, leftGap); CGFloat maskCircleWidth = rect.size.width; CGSize imageSize = self.originImage.size; //setp 2: setup contentSize: self.scrollView.contentSize = imageSize; CGFloat minimunZoomScale = imageSize.width < imageSize.height ? maskCircleWidth / imageSize.width : maskCircleWidth / imageSize.height; CGFloat maximumZoomScale = 5; //step 3: setup minimum and maximum zoomScale self.scrollView.minimumZoomScale = minimunZoomScale; self.scrollView.maximumZoomScale = maximumZoomScale; self.scrollView.zoomScale = self.scrollView.zoomScale < minimunZoomScale ? minimunZoomScale : self.scrollView.zoomScale; //step 4: setup current zoom scale if needed: if (self.needAdjustScrollViewZoomScale) { CGFloat temp = self.view.bounds.size.width < self.view.bounds.size.height ? self.view.bounds.size.width : self.view.bounds.size.height; minimunZoomScale = imageSize.width < imageSize.height ? temp / imageSize.width : temp / imageSize.height; self.scrollView.zoomScale = minimunZoomScale; self.needAdjustScrollViewZoomScale = NO; }}</span>
Next, analyze the function of each step above , First, take an official Apple document (Scroll
View Programming Guide for iOS) Let's have a brief look at the pictures on contentSize and contentInset
Meaning and function of :
chart 6.UIScrollView Of contentSize and contentInset Attribute diagram
contentSize It's you scrollView What to show in (content) Size , The detailed value should be rooted
According to the content Depends on the size of , Here we want to show the content of an image completely without compression , So here
stay step 2 Lieutenant general contentSize Set as picture (image.size) Of size Same size .
contentInset It can be understood as the up, down, left and right of the display content “ message ” The distance between . The default value is (0,0.
0.0),contentInset Add contentSize It's a scrollView Can slide
All areas .
Here we don't want content( picture ) The sliding area of exceeds the position of the circular clipping box , can
By cleverly speaking, cut the box ring and view The distance between the upper, lower, left and right edges of is taken as scrollView Of contentInset.
This is it. step 1 Do the things , It ensures that the circular clipping box can always fill the picture when the finger drags on it
The content of .
scrollView Support for zooming in and out is very easy. You just need to set the maximum and minimum times of zoom ,
Then in the proxy function (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
Returns the to be scaled view Can .
The main thing to be determined here scrollview Minimum zoom size . To fill with
It's enough to zoom in to the smallest dimension of the picture ( Length or width ) Tangent to the circular clipping box . It's possible
The minimum value of enough expansion and contraction . Because if you are shrinking the picture, you can't fill the clipping box :
chart 7. Shrink to the minimum . The clipping box must be tangent to the shorter side
step 4 Only in the viewDidLoad Run when , That is, when I first entered the picture editing page ,
It needs to be forcibly adjusted scrollview The current zoomScale, Make the picture display in a proper size
come out .
thus , The whole function is finished , Carry out the procedure . Take a look at the effect , We have achieved our expectations :
chart 8. Screen rotation effect
chart 9. Drag and zoom
3、 ... and 、 summary
Load pictures into scrollview, Shrink it 、 Drag and crop, part of which is the image editor
The main function of , Seemingly simple functional requirements . On closer examination, there are pits everywhere , We must think deeply
Every detail of . Make good use of UIView Of drawRect Method . Use a combination of scrollview The characteristics of
Realization .
This demo sample mainly has the following two points that deserve attention :
1. Implementation of circular clipping box , And in autolayout Processing of the clipping box behind the rotating screen in the environment ;
2.scrollView Property settings for . It must be combined with the actual size of the loaded image 、 Position of circular clipping box
And size information to dynamically adjust scrollView Of contentSize、contentInset And other property .
Copyright notice : This article is an original blog article , Blog , Without consent , Shall not be reproduced .
Publisher : Full stack programmer stack length , Reprint please indicate the source :https://javaforall.cn/117582.html Link to the original text :https://javaforall.cn
边栏推荐
- xlrd常见操作
- Arcgis\qgis no plug-in loading (no offset) mapbox HD image map
- Deployment of Jenkins under win7
- Explain various hot issues of Technology (SLB, redis, mysql, Kafka, Clickhouse) in detail from the architecture
- EasyExcel的讀寫操作
- [daily training] 729 My schedule I
- HYSBZ 2243 染色 (树链拆分)
- MMAP
- MQ----activeMq
- Simple interest mode - lazy type
猜你喜欢
"Grain mall" -- Summary and induction
uni-app 蓝牙通信
MySQL deep paging optimization with tens of millions of data, and online failure is rejected!
EasyExcel的读写操作
Two ways to realize video recording based on avfoundation
Explain various hot issues of Technology (SLB, redis, mysql, Kafka, Clickhouse) in detail from the architecture
力扣------经营摩天轮的最大利润
Summarize the reasons for 2XX, 3xx, 4xx, 5xx status codes
Zhang Lijun: penetrating uncertainty depends on four "invariants"
matlab绘制hsv色轮图
随机推荐
R language [data management]
Comprehensive optimization of event R & D workflow | Erda version 2.2 comes as "7"
2022-07-03-cka- latest feedback from fans
selenium 查找b或p标签的内容
校招期间 准备面试算法岗位 该怎么做?
Xlrd common operations
怎么利用Tensorflow2进行猫狗分类识别
Some things make feelings nowhere to put
Deployment of Jenkins under win7
使用Aspect制作全局异常处理类
int GetMonth( ) const throw( ); What does throw () mean?
JMeter installation under win7
2.2.5 basic sentences of R language drawing
What should I do to prepare for the interview algorithm position during school recruitment?
华为云ModelArts文本分类–外卖评论
Implementing Lmax disruptor queue from scratch (IV) principle analysis of multithreaded producer multiproducersequencer
Simple interest mode - lazy type
MMAP learning
华为游戏多媒体服务调用屏蔽指定玩家语音方法,返回错误码3010
Realize the function of verifying whether the user has completed login when browsing the page