Third-party client of the blog Park-I blog Park officially released App Store,-iapp
Blog garden third-party client-I blog Park officially released App Store1. Preface
It has been almost seven months since I learned iOS from to. Although it was still intermittent, I had to stick to it. I am looking for an internship in the next year, so I came up with an idea: Write an app trainer. This time I did not get the background, And I directly used the open api () of the blog Park ). I have also created an app called magic-magic before and after. However, the backend uses the Bmob backend cloud (A Baas Service), but as the first app, the code is messy and is basically a third-party control. This I blog is completely independently developed by me (including the uidesign). The overall use is the MVC mode, and try not to use third-party controls (although it is still used. ).
First, release several gif preview images for the app:
I blog. Or scan the following QR code:
Problem 1: Implement RippleButton on the boot page (not the boot page) (the button with ripple animation, the pink button on the first GIF image)
Solution:
1. Use UIBesierPath to construct a circular path
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:pathFrame cornerRadius:self.layer.cornerRadius];
2. Assign the above path to the path attribute of circleShape (CAShapeLayer object), and add the circleShape to RippleButton (UIView type ).
CAShapeLayer *circleShape = [CAShapeLayer layer];circleShape.path = path.CGPath;
[self.layer addSublayer:circleShape];
3. Now, you can use Core Animation to operate the RippleButton layer. I will not elaborate on the details. It is nothing more than controlling the scale and alpha of the circle through Animation.
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];scaleAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];scaleAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.5, 2.5, 1)];CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];alphaAnimation.fromValue = @1;alphaAnimation.toValue = @0;CAAnimationGroup *animation = [CAAnimationGroup animation];animation.animations = @[scaleAnimation, alphaAnimation];animation.duration = 1.0f;animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];[circleShape addAnimation:animation forKey:nil];
4. However, if you only add one circleShape, there will be no effect of scattering multiple water waves. So I encapsulated the above 123 step code into the createRippleEffect function and added it to the timer.
-(Void) setupRippleTimer {_ weak _ typeof _ (self) weakSelf = self; NSTimeInterval repeatInterval = self. repeatInterval; dispatch_queue_t queue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); self. timer = dispatch_source_create (DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_source_set_timer (self. timer, DISPATCH_TIME_NOW, repeatInterval * NSEC_PER_SEC, 0); _ block NSInteger count = 0; dispatch_source_set_event_handler (self. timer, ^ {dispatch_async (dispatch_get_main_queue (), ^ {count ++; // The number of repeated water ripples. The default value is-1, indicating permanent if (self. repeatCount! =-1 & count> weakSelf. repeatCount) {[weakSelf stopRippleEffect]; return;} [weakSelfCreateRippleEffect] ;}) ;}
Question 2: UICollectionView is used for 48-hour reading and 10-day recommendation, and UICollectionViewLayout is customized to achieve the rotation effect of the wheel (for some code, referAWCollectionViewDialLayout)
Solution:
1. First, you must know the specific process of customizing UICollectionViewLayout.
For more information about how to implement custom UICollectionViewLayout, see this article!
2. The layoutAttributesForElementsInRect function is the core of the custom UICollectionViewLayout implementation process.
2.1 calculate which cells are in the current rect Based on the rect's y value:
// There are several cells in the rect area, and the attributes of each cell are returned-(NSArray <UICollectionViewLayoutAttributes *> *) returns :( CGRect) rect {NSMutableArray * layoutAttributes = [NSMutableArray array]. CGFloat minY = CGRectGetMinY (rect); CGFloat maxY = CGRectGetMaxY (rect); // get the firstIndex and lastIndex of cells in the rect area. These two are useless, it is mainly used to obtain activeIndex NSInteger firstIndex = flodorf (minY/self. itemHeight); NSInteger lastIndex = flograding (maxY/self. itemHeight); NSInteger activeIndex = (firstIndex + lastIndex)/2; // The cell in the center is set to active // maxVisiableOnScreeen indicates the maximum number of cells on the current screen. // angularSpacing indicates the number of cells at intervals, because the disk is used here, each cell is actually seen as a fan-shaped NSInteger maxVisiableOnScreeen = 180/self. angularSpacing + 2; // firstItem and lastItem indicate which cells are in the current rect NSInteger firstItem = fmax (0, activeIndex-(NSInteger) maxVisiableOnScreeen/2 ); NSInteger lastItem = fmin (self. cellCount, activeIndex + (NSInteger) maxVisiableOnScreeen/2); if (lastItem = self. cellCount) {firstItem = fmax (0, self. cellCount-(NSInteger) maxVisiableOnScreeen);} // calculate the UICollectionViewLayoutAttributes for (NSInteger I = firstItem; I <lastItem; I ++) of each cell in the rect) {NSIndexPath * indexPath = [NSIndexPath indexPathForItem: I inSection: 0]; attributes * attributes = [self attributes: indexPath]; [layoutAttributes addObject: attributes];} return layoutattriattributes ;}
2.2 calculate the UICollectionViewLayoutAttributes of each cell
-(UICollectionViewLayoutAttributes *) layoutAttributesForItemAtIndexPath :( NSIndexPath *) indexPath {// default offset is 0 CGFloat newIndex = (indexPath. item + self. offset); UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath: indexPath]; attributes. size = self. cellSize; CGFloat scaleFactor, deltaX; CGAffineTransform translationT; CGAffineTransform rotationT; switch (self. wheetAlignmentType) {case WheetAlignmentTypeLeft: scaleFactor = fmax (0.6, 1-fabs (newIndex * 0.25); deltaX = self. cellSize. width/2; attributes. center = CGPointMake (-self. radius + self. xOffset, self. collectionView. height/2 + self. collectionView. contentOffset. y); rotationT = CGAffineTransformMakeRotation (self. angularSpacing * newIndex * M_PI/180); translationT = CGAffineTransformMakeTranslation (self. radius + deltaX * scaleFactor, 0); break; case WheetAlignmentTypeRight: scaleFactor = fmax (0.6, 1-fabs (newIndex * 0.25); deltaX = self. cellSize. width/2; attributes. center = CGPointMake (self. radius-self. xOffset + ICDeviceWidth, self. collectionView. height/2 + self. collectionView. contentOffset. y); rotationT = CGAffineTransformMakeRotation (-self. angularSpacing * newIndex * M_PI/180); translationT = CGAffineTransformMakeTranslation (-self. radius-deltaX * scaleFactor, 0); break; case WheetAlignmentTypeCenter: // to be implemented break; default: break;} CGAffineTransform scaleT = weight (scaleFactor, scaleFactor); attributes. alpha = scaleFactor; // The alpha and scaleFactor are consistent. // scale down first and then reach the corresponding position in the translation process (because it is a slice, the x value of each cell is related to the corresponding position ), finally, rotation (forming an arc) attributes. transform = CGAffineTransformConcat (scaleT, CGAffineTransformConcat (translationT, rotationT); attributes. zIndex = indexPath. item; return attributes ;}
Problem 3: Implement TabBarItem with animation
Solution:
I submitted the code to Github-animated-tab-bar-Objective-C (PJXAnimatedTabBarController is a Objective-C version of RAMAnimatedTabBarController (Https://github.com/Ramotion/animated-tab-bar)).
It mainly refers to the custom UITabBarItem and the AutoLayout construction of the custom UITabBarItem. The code is well encapsulated, especially the animation implementation part. The structure is clear and conforms to the OOP idea.
Question 4: open web APIs in xml format used by the blog garden. Difficult to parse.
Solution:
Here I still use the MVC idea, use AFNetworking to get XML data, and then use XMLDictionary to parse XML data (essentially NSXMLParserDelegate) and convert it to Model (which needs to be implemented by myself ).
For details about NSXMLParserDelegate, refer to this article-Parsing XML files for iOS development.
Question 5: The design part is not very good at it. It takes a long time for every page to layout. It is as concise as possible, and it is highly technical.
Solution:
Basically, it is to look at the design, imitation, or thinking of other people's apps. It was also the first time to use Sketch.
4. Problems and TODO
- Share on Weibo and so on. You are ready to use umeng.
- The layout of the UIWebView interface is ugly. Not very familiar with CSS, JS, HTML5. I used to adapt an image for a long time. In fact, just add "img {max-width: 100% %; height: auto;}" to
- It is difficult to hide a custom TabBarItem.
- Offline reading
- ......
5. Postscript
Writing code by yourself does feel different. Many details are difficult but challenging. The code is currently frustrated. We will modify the specifications later and put them on Github.