2015-03-19 09:51:35 mimitongwangla 阅读数 1360
  • flutter高仿微信项目实战

    Flutter案例视频教程,从入门到具体项目实战,Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。flutter高仿微信项目实战是一个使用flutter技术仿微信界面开发的完整案例,同时提供代码下载。

    2166 人正在学习 去看看 亢少军

仿微信基本功能。基于XMPP服务器的即时通信以及交友客户端。 

    ----第一期代码的功能如下---- 

    1.新用户注册 

    2.登陆并连接XMPP服务器 握手 保持在线 

    2.查找最新注册用户:(下拉刷新 上拉翻页,做的比较匆忙,没操作提示) 

    3.点击某个用户即可对他发送消息(聊天记录下一期更新) 

    4.首页显示收到的和发送的所有消息(排序和分组下一期更新) 

    ----第二期更新内容---- 

    1.修复了注册延时时间,添加了切换账号功能,修复了输入框适配问题 

    2.微信页面和聊天页面界面已基本重做 。首页消息按联系人分组按时间逆序显示,聊天气泡有待优化,收发消息全部采用 NSNotifaction 不用担心消息发了页面不刷新。自动滚到最新一条。键盘监听效果更加友好,采用TableViewInset来错位。 

    3.添加好友功能,添加完后可以在好友列表查看到,若是在是注册的太早要翻很久,或者新注册用户刷新不出来,可以通过webview直接添加,但是需到控制台查看互相的ID 

    4.User对象和Message对象采用FMDB+类java的Dao模式用静态方法固化,并且有个开发者自己写的对象to字典、字典to对象的两个超好用的方法,大家值得一看! 

    5.删除了XMPPManager单例中的一些废话和暂时用不着的内容,添加了很多注释,大家可以细细品读。 

    ----第三期更新内容---- 

    1. 加入离线图片的发送接收支持; 

    2. 优化了输入框,并以自定义输入面板的形式实现了shareMore(分享更多); 

    3. 发送图片的两种模式均以实现 ,通过http上传至服务器再转发URL(已注释掉),通过base64码直接编码图片再解码,均支持离线收发。 

    由于作者没有做屏幕适配,所以请在Retina 4-inch模拟器或者iPhone5中运行代码。 

    ----第四期代码的功能如下---- 

    1. 修复绝大部分bug 

    2. ios7体验更佳 

    3. 图片/文字畅聊不闪退 

    4. 支持按昵称模糊搜索,找到朋友更容易 

    5. 修复了域名丢失引起的连接失败问题,现在服务器24小时确保能连接 

    6. 更准确的提示信息,更好的用户体验 

    PS: 1~3期的用户数据已清除,如需尝试请重新注册 

     

    作者说:这次给大家带来的demo我已经写好了服务器端,也配置好了openfire服务器,大家如果想运行的话先确定服务器是否开启(API端口8080,openfire端口5225),ping一下 www.hcios.com 如果未开启请QQ,一般开启时间为9:00~18:00

    估计网上再也找不到比我写的这个更好的XMPP Demo了,上传此demo的目的为给更多的朋友了解即时通讯技术,在您看的爽的同时,请允许我要求您不要用此demo以商业用途传播 ,谢谢。

 

源码下载:http://code.662p.com/view/5849.html 


<ignore_js_op> 
<ignore_js_op> 
<ignore_js_op>

详细说明:http://ios.662p.com/thread-1073-1-1.html


2014-10-20 00:31:42 leafrenchleaf 阅读数 196
  • flutter高仿微信项目实战

    Flutter案例视频教程,从入门到具体项目实战,Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。flutter高仿微信项目实战是一个使用flutter技术仿微信界面开发的完整案例,同时提供代码下载。

    2166 人正在学习 去看看 亢少军
10月19日闲的蛋疼,做了个
仿微信的聊天工具
git地址:[url]https://github.com/killinux/mysocket/tree/master/websocket/project/Test[/url]
ui参考的网上例子http://ios.9tech.cn/news/2013/1111/38520.html
服务端用 tomcat7的websocket
客户端
1.可以用浏览器
2.sockroket的ios客户端,ios8,开发工具xcode6
ios客户端代码如下代码:
ViewController.m

//
// ViewController.m
// BubbleDemo
//
// Created by xiao7 on 14/10/19.
// Copyright (c) 2014年 killinux. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

NSString *websocket_url = @"ws://192.168.0.102:8080/webs/websocket/test";
NSString *myName = @"haoning";
NSString *toName = @"all";
- (void)viewDidLoad {
[super viewDidLoad];
//----init data---begin----
// NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"weixin",@"name",@"这是一个测试",@"content", nil];
// NSDictionary *dict1 = [NSDictionary dictionaryWithObjectsAndKeys:@"haoning",@"name",@"hello",@"content", nil];
// NSDictionary *dict7 = [NSDictionary dictionaryWithObjectsAndKeys:@"weixin",@"name",@",长数据测试。",@"content", nil];
// _resultArray = [NSMutableArray arrayWithObjects:dict,dict1, nil];
// [_resultArray addObject:dict7];

_resultArray = [[NSMutableArray alloc] init];
//----init data---end----
//websocket---begin---
_mywebSocket.delegate = nil;
[_mywebSocket close];
_mywebSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:websocket_url]]];
_mywebSocket.delegate = self;
[_mywebSocket open];
NSLog(@"open success!");
//websocket---end----
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//pragma websocket
- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
{
NSLog(@"Websocket Connected");
self.title = @"Connected!";
}

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
{
NSLog(@":( Websocket Failed With Error %@", error);
_mywebSocket = nil;
}

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
{
NSLog(@"websocket Received \"%@\"", message);
NSArray *messageArray = [message componentsSeparatedByString:@","];
if(messageArray.count<3){
NSLog(@"error parameter not right:%@",message);
// NSDictionary *dict8 = [NSDictionary dictionaryWithObjectsAndKeys:@"haoning",@"name",message,@"content", nil];
// [_resultArray addObject:dict8];
}else{
NSDictionary *dict8 = [NSDictionary dictionaryWithObjectsAndKeys:messageArray[0],@"name",messageArray[2],@"content", nil];
[_resultArray addObject:dict8];
}
[tableViewList reloadData];
}

- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
{
NSLog(@"WebSocket closed");
self.title = @"Connection Closed! (see logs)";
_mywebSocket = nil;
}
//发送消息
- (IBAction)sendBubbleMessage:(id)sender {
NSString *thistext = messageTxt.text;
NSLog(@"sendBubbleMessage:%@",thistext);
// NSDictionary *dict8 = [NSDictionary dictionaryWithObjectsAndKeys:@"haoning",@"name",thistext,@"content", nil];
// [_resultArray addObject:dict8];
// [tableViewList reloadData];
NSString *sendMessage =[myName stringByAppendingFormat:@",%@,%@",toName,thistext];
[_mywebSocket send:sendMessage];
messageTxt.text=nil;
}

//泡泡文本
- (UIView *)bubbleView:(NSString *)text from:(BOOL)fromSelf withPosition:(int)position{

//计算大小
UIFont *font = [UIFont systemFontOfSize:14];
CGSize size = [text sizeWithFont:font constrainedToSize:CGSizeMake(180.0f, 20000.0f) lineBreakMode:NSLineBreakByWordWrapping];

// build single chat bubble cell with given text
UIView *returnView = [[UIView alloc] initWithFrame:CGRectZero];
returnView.backgroundColor = [UIColor clearColor];

//背影图片
UIImage *bubble = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:fromSelf?@"SenderAppNodeBkg_HL":@"ReceiverTextNodeBkg" ofType:@"png"]];

UIImageView *bubbleImageView = [[UIImageView alloc] initWithImage:[bubble stretchableImageWithLeftCapWidth:floorf(bubble.size.width/2) topCapHeight:floorf(bubble.size.height/2)]];
//NSLog(@"%f,%f",size.width,size.height);
UILabel *bubbleText = [[UILabel alloc] initWithFrame:CGRectMake(fromSelf?15.0f:22.0f, 20.0f, size.width+10, size.height+10)];
bubbleText.backgroundColor = [UIColor clearColor];
bubbleText.font = font;
bubbleText.numberOfLines = 0;
bubbleText.lineBreakMode = NSLineBreakByWordWrapping;
bubbleText.text = text;
bubbleImageView.frame = CGRectMake(0.0f, 14.0f, bubbleText.frame.size.width+30.0f, bubbleText.frame.size.height+20.0f);
if(fromSelf)
returnView.frame = CGRectMake(320-position-(bubbleText.frame.size.width+30.0f), 0.0f, bubbleText.frame.size.width+30.0f, bubbleText.frame.size.height+30.0f);
else
returnView.frame = CGRectMake(position, 0.0f, bubbleText.frame.size.width+30.0f, bubbleText.frame.size.height+30.0f);

[returnView addSubview:bubbleImageView];
[returnView addSubview:bubbleText];

return returnView;
}

//泡泡语音
- (UIView *)yuyinView:(NSInteger)logntime from:(BOOL)fromSelf withIndexRow:(NSInteger)indexRow withPosition:(int)position{

//根据语音长度
int yuyinwidth = 66+fromSelf;

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = indexRow;
if(fromSelf)
button.frame =CGRectMake(320-position-yuyinwidth, 10, yuyinwidth, 54);
else
button.frame =CGRectMake(position, 10, yuyinwidth, 54);

//image偏移量
UIEdgeInsets imageInsert;
imageInsert.top = -10;
imageInsert.left = fromSelf?button.frame.size.width/3:-button.frame.size.width/3;
button.imageEdgeInsets = imageInsert;

[button setImage:[UIImage imageNamed:fromSelf?@"SenderVoiceNodePlaying":@"ReceiverVoiceNodePlaying"] forState:UIControlStateNormal];
UIImage *backgroundImage = [UIImage imageNamed:fromSelf?@"SenderVoiceNodeDownloading":@"ReceiverVoiceNodeDownloading"];
backgroundImage = [backgroundImage stretchableImageWithLeftCapWidth:20 topCapHeight:0];
[button setBackgroundImage:backgroundImage forState:UIControlStateNormal];

UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(fromSelf?-30:button.frame.size.width, 0, 30, button.frame.size.height)];
label.text = [NSString stringWithFormat:@"%d''",logntime];
label.textColor = [UIColor grayColor];
label.font = [UIFont systemFontOfSize:13];
label.textAlignment = NSTextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[button addSubview:label];

return button;
}

#pragma UITableView

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _resultArray.count;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dict = [_resultArray objectAtIndex:indexPath.row];
UIFont *font = [UIFont systemFontOfSize:14];
CGSize size = [[dict objectForKey:@"content"] sizeWithFont:font constrainedToSize:CGSizeMake(180.0f, 20000.0f) lineBreakMode:NSLineBreakByWordWrapping];

return size.height+44;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}else{
for (UIView *cellView in cell.subviews){
[cellView removeFromSuperview];
}
}
NSDictionary *dict = [_resultArray objectAtIndex:indexPath.row];

//创建头像
UIImageView *photo ;
if ([[dict objectForKey:@"name"]isEqualToString:@"haoning"]) {
photo = [[UIImageView alloc]initWithFrame:CGRectMake(320-60, 10, 50, 50)];
[cell addSubview:photo];
photo.image = [UIImage imageNamed:@"photo1"];

if ([[dict objectForKey:@"content"] isEqualToString:@"0"]) {
[cell addSubview:[self yuyinView:1 from:YES withIndexRow:indexPath.row withPosition:65]];

}else{
[cell addSubview:[self bubbleView:[dict objectForKey:@"content"] from:YES withPosition:65]];
}

}else{
photo = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 50, 50)];
[cell addSubview:photo];
photo.image = [UIImage imageNamed:@"photo"];

if ([[dict objectForKey:@"content"] isEqualToString:@"0"]) {
[cell addSubview:[self yuyinView:1 from:NO withIndexRow:indexPath.row withPosition:65]];
}else{
[cell addSubview:[self bubbleView:[dict objectForKey:@"content"] from:NO withPosition:65]];
}
}

return cell;

}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

}

@end



ViewController.h

//
// ViewController.h
// BubbleDemo
//
// Created by xiao7 on 14/10/19.
// Copyright (c) 2014年 killinux. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "SocketRocket/SRWebSocket.h"
@interface ViewController : UIViewController<SRWebSocketDelegate>
{

IBOutlet UITableView *tableViewList;
IBOutlet UITextField *messageTxt;

}
@property (nonatomic, strong) NSMutableArray *resultArray;
@property (nonatomic, strong) SRWebSocket *mywebSocket;
@end

[img]http://dl2.iteye.com/upload/attachment/0102/1799/a98e13ff-9ba8-3328-8b7f-0228835b7f55.png[/img]
演示
chrome上
[img]http://dl2.iteye.com/upload/attachment/0102/1801/febdd1cf-8511-3df9-86f2-4ff25c499010.png[/img]
ios8系统上
[img]http://dl2.iteye.com/upload/attachment/0102/1804/24fbb834-6277-3f56-a4d9-066176eaca0d.png[/img]

html的客户端

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<script type="text/javascript">
String.prototype.startWith=function(s){
if(s==null||s==""||this.length==0||s.length>this.length)
return false;
if(this.substr(0,s.length)==s)
return true;
else
return false;
return true;
}
Date.prototype.format = function(format) {
var o = {
"M+" : this.getMonth() + 1, //month
"d+" : this.getDate(), //day
"h+" : this.getHours(), //hour
"m+" : this.getMinutes(), //minute
"s+" : this.getSeconds(), //second
"q+" : Math.floor((this.getMonth() + 3) / 3), //quarter
"S" : this.getMilliseconds()
}
if (/(y+)/.test(format))
format = format.replace(RegExp.$1, (this.getFullYear() + "")
.substr(4 - RegExp.$1.length));
for ( var k in o)
if (new RegExp("(" + k + ")").test(format))
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k]
: ("00" + o[k]).substr(("" + o[k]).length));
return format;
}
var ws = null;
function log(text) {
/* document.getElementById("log").innerHTML = (new Date).getTime() + ": "
+ text + "<br>" + document.getElementById("log").innerHTML; */
document.getElementById("log").innerHTML = new Date().format('yyyy-MM-dd hh:mm:ss') + ","
+ text + "<br>" + document.getElementById("log").innerHTML;
}
function enterSend(){
if(event.keyCode == 13){
document.getElementById("sendbtn").click();
}
}
function startServer() {

var url = document.getElementById("serverip").value;// "ws://192.168.0.102:8887";
if ('WebSocket' in window) {
ws = new WebSocket(url);
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket(url);
} else {
log('浏览器不支持');
return;
}
ws.onopen = function() {
log('唷嘻,连上了');
};
// 收到服务器发送的文本消息, event.data表示文本内容
ws.onmessage = function(event) {
var thisdata = event.data;
if(thisdata.startWith("open")){
//alert(thisdata);
document.getElementById("username").value=thisdata.split(" ")[1];
}else{
//log(event.data);
var showData=event.data.split(",");
log(showData[0]+"说:"+showData[2]);
}
};
ws.onclose = function() {
log('Closed! 刷新页面尝试连接.');
}
//document.getElementById("conbtn").disabled = "true";
//document.getElementById("stopbtn").removeAttribute('disabled');
}
function sendMessage() {
var textMessage = document.getElementById("textMessage").value;
var username = document.getElementById("username").value;
var toUser = "";
if (ws != null && textMessage != "") {
ws.send(username+","+toUser+","+textMessage);
}
document.getElementById("textMessage").value="";
}
function stopconn() {
ws.close();
//document.getElementById("conbtn").removeAttribute('disabled');
//document.getElementById("stopbtn").disabled = "true";
}
</script>
<body onload="startServer()">

<input id="serverip" type="text" size="20"
value="ws://192.168.0.102:8080/webs/websocket/test" />
<!-- ws://192.168.0.102:8887 182.254.155.153 -->
<!-- <input id="conbtn" type="button" onclick="startServer()" value="open" />
<input id="stopbtn" type="button" onclick="stopconn()" value="stop" disabled="disabled"/> -->
</br>
您的名字:<input id="username" type="text" /></br></br>
<input id="textMessage" type="text" size="20" onkeydown="enterSend()" style="border:1;width:400px" />
<input id="sendbtn" type="button" onclick="sendMessage()" value="Send">
<div id="log"></div>
</body>
</html>



java的tomcat7的后台:

package com.hao;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;

public class HelloWorldWebSocketServlet extends WebSocketServlet {
public static Map<String,MyMessageInbound> mmiList = new HashMap<String,MyMessageInbound>();

protected StreamInbound createWebSocketInbound(String subProtocol,
HttpServletRequest arg1) {
return new MyMessageInbound();
}
public int getUserCount(){
return mmiList.size();
}
private class MyMessageInbound extends MessageInbound {
WsOutbound myoutbound;
String mykey;
@Override
public void onOpen(WsOutbound outbound) {
try {
System.out.println("Open Client.");
this.myoutbound = outbound;
mykey ="open "+System.currentTimeMillis();;
mmiList.put(mykey, this);
System.out.println("mmiList size:"+mmiList.size());
outbound.writeTextMessage(CharBuffer.wrap(mykey));
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void onClose(int status) {
System.out.println("Close Client.");
//mmiList.remove(this);
mmiList.remove(mykey);
}

@Override
protected void onBinaryMessage(ByteBuffer arg0) throws IOException {

}

@Override
protected void onTextMessage(CharBuffer message) throws IOException {
// TODO Auto-generated method stub
System.out.println("onText--->" + message.toString());
// for (int i=0;i< mmiList.size();i++ ) {
// MyMessageInbound mmib = (MyMessageInbound) mmiList.get(i);
// CharBuffer buffer = CharBuffer.wrap(message);
// mmib.myoutbound.writeTextMessage(buffer);
// mmib.myoutbound.flush();
// }
for (Map.Entry<String, MyMessageInbound> entry : mmiList.entrySet()) {
//System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
MyMessageInbound mmib = (MyMessageInbound) entry.getValue();
// String str = entry.getKey()+message.toString()
CharBuffer buffer = CharBuffer.wrap(message);
mmib.myoutbound.writeTextMessage(buffer);
mmib.myoutbound.flush();
}

/*Socket socket;
String msg = "";
try {
// 向服务器利用Socket发送信息
socket = new Socket("192.168.0.102", 5000);
// socket = new Socket("127.0.0.1",5000);
PrintWriter output = new PrintWriter(socket.getOutputStream());

output.write(message.toString());
output.flush();

// 这里是接收到Server的信息
DataInputStream input = new DataInputStream(
socket.getInputStream());
byte[] b = new byte[1024];
input.read(b);
// Server返回的信息
msg = new String(b).trim();

output.close();
input.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 往浏览器发送信息
CharBuffer cb = CharBuffer.wrap(new StringBuilder(msg));
getWsOutbound().writeTextMessage(cb);*/
}
}

public static void main(String[] args) {
Socket socket;
String message = "haoning";
String msg = "";
try {
// 向服务器利用Socket发送信息
socket = new Socket("192.168.0.102", 5000);
// socket = new Socket("127.0.0.1",5000);
PrintWriter output = new PrintWriter(socket.getOutputStream());

output.write(message.toString());
output.flush();

// 这里是接收到Server的信息
DataInputStream input = new DataInputStream(socket.getInputStream());
byte[] b = new byte[1024];
input.read(b);
// Server返回的信息
msg = new String(b).trim();

output.close();
input.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}


web.xml

<servlet>
<servlet-name>wsSnake</servlet-name>
<servlet-class>com.hao.HelloWorldWebSocketServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>wsSnake</servlet-name>
<url-pattern>/websocket/test</url-pattern>
</servlet-mapping>
2016-02-24 14:47:53 qq_30107193 阅读数 230
  • flutter高仿微信项目实战

    Flutter案例视频教程,从入门到具体项目实战,Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。flutter高仿微信项目实战是一个使用flutter技术仿微信界面开发的完整案例,同时提供代码下载。

    2166 人正在学习 去看看 亢少军

iOS 中有摇动要实现的方法:

- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);

- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);

- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);


我们需要在ViewController的viewDidLoad方法中加入

  //可以晃动

 [[UIApplication sharedApplication] setApplicationSupportsShakeToEdit:YES];

    //成为第一响应

 [self becomeFirstResponder];

//然后去实现那几个方法就可以了


- (void) motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event


{

    

    //检测到摇动

    NSLog(@"开始摇动");

    

    //振动效果

    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);

    //upImageView上移

    CABasicAnimation *upAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"];

    upAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

    upAnimation.fromValue = @([UIScreen mainScreen].bounds.size.height/4);

    upAnimation.toValue = @(0);

    upAnimation.duration = 1;

    upAnimation.repeatCount = 1;

    upAnimation.autoreverses = YES;

    [self.upImageView.layer addAnimation:upAnimation forKey:@"upAnimation"];

    //downImageView下移

    CABasicAnimation *downAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"];

    downAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

    downAnimation.fromValue = @([UIScreen mainScreen].bounds.size.height/4*3);

    downAnimation.toValue = @([UIScreen mainScreen].bounds.size.height);

    downAnimation.duration = 1;

    downAnimation.repeatCount = 1;

    downAnimation.autoreverses = YES;

    

    

    

    [self.downImageView.layer addAnimation:downAnimation forKey:@"downAnimation"];

}




- (void) motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event


{

    

    //摇动取消

    NSLog(@"取消摇动");

    

}




- (void) motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event


{

    

    //摇动结束

     NSLog(@"摇动结束");

    [self.activity stopAnimating ];

    

    if (event.subtype == UIEventSubtypeMotionShake) {

        

        //something happens

        

    }

    

}




2017-02-14 17:11:53 samuelandkevin 阅读数 2178
  • flutter高仿微信项目实战

    Flutter案例视频教程,从入门到具体项目实战,Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。flutter高仿微信项目实战是一个使用flutter技术仿微信界面开发的完整案例,同时提供代码下载。

    2166 人正在学习 去看看 亢少军

    最近,由于项目需要,本人基于Masonry和YYKit仿微信表情键盘造了个轮子:YHExpressionKeyBoard。

    先来个简单的效果图:

  


    项目文件结构简要介绍:




 一、初始化方式:


//表情键盘

YHExpressionKeyboard *v = [[YHExpressionKeyboard alloc] initWithViewController:self aboveView:scrV];


 二、键盘代理

 #pragma mark - @protocol YHExpressionKeyboardDelegate

- (void)didSelectExtraItem:(NSString *)itemName{

    UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:itemName message:nilpreferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction *ok = [UIAlertActionactionWithTitle:@"确定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *action) {

      

    }];

    [alert addAction:ok];

    [selfpresentViewController:alert animated:YEScompletion:nil];

   

}


三、表情键盘主要是用了UICollectionView实现,如果实现UICollectionView表情横排横滚动,可以参考我的另外一篇文章:iOS- UICollectionView实现表情数据横排横滚动

   输入框最多显示4行,框高的变化动画是Masonry的约束动画实现。



最后,祝大家情人节快乐

YHExpressionKeyBoard 传送门:

https://github.com/samuelandkevin/YHExpressionKeyBoard




2016-08-07 11:06:57 qq_35114086 阅读数 3395
  • flutter高仿微信项目实战

    Flutter案例视频教程,从入门到具体项目实战,Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。flutter高仿微信项目实战是一个使用flutter技术仿微信界面开发的完整案例,同时提供代码下载。

    2166 人正在学习 去看看 亢少军
写在前面的话
(大伙在使用过程中遇到BUG请@我一下,我会去认真迭代这款高仿APP)

 项目简介:
  •  1.此版本是有史以来Github上最牛逼的高仿微信项目没有之一,采用MVVM和MVC两种开发架构思想,纯代码开发,这是你们在培训机构学不到的.仅供大家学习使用,不得用于商业用途.最终解释权归作者二哥所有.
  •  2.如果各位下客能帮我点STAR,半个月STAR500+,我会陆陆续续发布待实现功能,其实已经做完,一个月STAR1000+我会把微信主要功能全部实现发布出来,两个月STAR2000+我会发布纯Swift版,纯Swift版采用纯代码开发已经做的差不多了.就看大伙的手能不能点STAR了.希望大家不要下完就跑了.作为作者的二哥会很心痛的.
  • 3.我之前接触过很多项目,就有一个项目中的朋友圈整个控制器4千行,尼玛4千行了这项目怎么迭代,二哥现在300行解决了朋友圈的问题,还在优化中...

高仿微信计划:
测试账号:

nacker 123456
h18    123456

 1.采用技术点

* pod用于第三方库的管理

* 环信SDK(V3.1.4 2016-07-08)作为此APP DEMO的IM功能

* Masonry用于界面布局

* FMDB用于数据存储

* MJRefresh用于上拉下拉刷新

* MJExtension用于字典转模型

* SDWebImage用于图片展示下载

* ReactiveCocoa用作响应式编程提高代码可读性

* MLLabel用于Label的图文混排

* IM部分采用MVC架构,朋友圈采用MVVM架构

2.已经实现功能

* 微信首页(列表数据展示、cell侧滑编辑、点击进入聊天详情界面、发送文字图片和语音)

* 通讯录(联系人字母排序、添加联系人)

* 发现(朋友圈、下拉刷新)

* 我(界面、退出功能)

3.待实现功能

* 搜索好友

* 朋友圈细节完善

* 扫一扫

* 相册、钱包

* 其他细节实现

* 摇一摇

* 发送朋友圈信息

* 其他

部分截图


LZEasemob3.png

整体架构图



LZ1.gif

聊天界面


 


LZ2.gif

通讯录


 


LZ3.gif

发现



LZ4.gif

登录


 
感恩
感谢那些开源作者们,我在这里就不一一点名感谢了.有了你们在整个项目的开发进度上提升了不少.也让我学会了很多




期待

* 如果在使用过程中遇到BUG,希望你能Issues我,谢谢(或者尝试下载最新的框架代码看看BUG修复没有)
* 如果在使用过程中发现功能不够用,希望你能Issues我,我非常想为这个框架增加更多好用的功能,谢谢
* 如果你想为LZEasemob3输出代码,请拼命Pull Requests我
 
项目git源码地址 喜欢请star:https://github.com/nacker/LZEasemob3
没有更多推荐了,返回首页