iOS

Skills

exit the keyboard

- (void)touchesBegan:(NSSet<UITouch *> *)touches     withEvent:(UIEvent *)event {
    [self.view endEditing:YES];
}

Add Target to control component

[self.datePicker addTarget:self action:@selector(getInput:) forControlEvents:UIControlEventValueChanged];

DatePicker

set frame after DatePickerStyle to gurantee datepicker in center

PickerView

Implement UIPickerViewDelegate & UIPickerViewDataSource in Interface

@interface ViewController()<UIPickerViewDelegate, UIPickerViewDataSource>
@end

functions

component = cols index
row = rows index

  1. cols number

-(NSInteger)numberOfComponentsInPickerView:
2. rows number
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
3. title of every row
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
4. when select each row
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component

set delegate

self.pickerView.delegate = self;
self.pickerView.dataSource = self; 

ScrollView

Three Attributes

  1. contentSize - the size of scrolling content
  2. contentOffset - the offset between the pos with [0,0]
  3. contentInset - edge size

Important Delegate Methods

// use when scrolling
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;

// use when zooming
- (void)scrollViewDidZoom:(UIScrollView *)scrollView API_AVAILABLE(ios(3.2));

// start draging
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;

// will end draging
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset API_AVAILABLE(ios(5.0));

// end drag
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;

// start decelerating
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView;

// end decelerating
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
// zoom component
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;
// start zooming
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view;
// finish zooming
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale;

option + mouse = finger gesture

Get bottom line of the most bottom component

CGRectGetMaxY(component.frame)

#import "ViewController.h"

@interface ViewController()<UIScrollViewDelegate>
@property(strong, nonatomic) UIScrollView *scrollView;
@property(strong, nonatomic) UIPageControl *pageControl;
@end

@implementation ViewController
CGFloat width;
-(void)viewDidLoad {
[super viewDidLoad];

width = [[UIScreen mainScreen] bounds].size.width;
CGFloat height = [UIApplication sharedApplication].windows.firstObject.windowScene.statusBarManager.statusBarFrame.size.height;

self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, height, 414, 200)];
self.scrollView.delegate = self;
self.scrollView.backgroundColor = [UIColor blueColor];

for(int i = 0; i < 5; i++) {
    UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(i*width, 0, width, 200)];
    if (i%2 == 0) {
        imgView.image = [UIImage imageNamed:@"1"];
    } else {
        imgView.image = [UIImage imageNamed:@"I-797C.jpeg"];
    }

    [self.scrollView addSubview:imgView];
}

self.scrollView.contentSize = CGSizeMake(5*width, 0);
self.scrollView.bounces = NO;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.pagingEnabled = YES;



self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(width/2 - 100, 190, 200, 20)];
self.pageControl.numberOfPages = 5;
self.pageControl.currentPage = 0;
self.pageControl.currentPageIndicatorTintColor = [UIColor whiteColor];
self.pageControl.pageIndicatorTintColor = [UIColor grayColor];
self.pageControl.backgroundStyle = 2;

//self.pageControl
[self.view addSubview:self.scrollView];
[self.view addSubview:self.pageControl];

}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
CGFloat offset = self.scrollView.contentOffset.x;

self.pageControl.currentPage = offset / width;
}

@end

TableView

Structure

Function

Delete Row

UI & Implementation
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
[self.data removeObjectAtIndex:indexPath.row];

[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
return @"改变";
}

Insert Row

UI

button click

implementation
-(void) addNew:(UIButton *) button {
[self.tableView setEditing:YES];
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleInsert;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
    [self.data removeObjectAtIndex:indexPath.row];

    [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
    [self.data insertObject:@"iPhone" atIndex:indexPath.row];

    //[self.tableView reloadData];
    [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}

}

Move Row

UI & Implementation
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}


- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
NSString *temp = self.data[sourceIndexPath.row];
[self.data removeObjectAtIndex:sourceIndexPath.row];

[self.data insertObject:temp atIndex:destinationIndexPath.row];
}

Index design

Methods
- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return self.sectionTitles;
}

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
NSLog(@"%d = %@", index, title);

return index;

}

self design UITableViewCell

click here

static UITabelView

click here

Refreshing

1.

UIRefreshControl *control = [[UIRefreshControl alloc] init];
control.attributedTitle = [[NSAttributedString alloc] initWithString:@"refreshing..."];
control.tintColor = [UIColor redColor];

[control addTarget:self action:@selector(refreshTableView) forControlEvents:UIControlEventValueChanged];

self.tableView.refreshControl = control;

2.

-(void) refreshTableView {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [self.data insertObject:@"123" atIndex:0];

    [self.tableView reloadData];
    if ([self.tableView.refreshControl isRefreshing])
        [self.tableView.refreshControl endRefreshing];
});
} 

CollectionView

click here

self-design collectionViewCell

click here

ViewController

launch

click here

process

MobileDevelopment

Introduction This note will be used to keep track of all the knowledge gained during Android development

Development Component

Service

Step 1: Create Service class whihc extends Service

Instruction

Step 2: Start & stop service

普通的Service的生命周期很简单,分别为onCreate、onStartCommand、onDestroy这三个。当我们startService()的时候,首次创建Service会回调onCreate()方法,然后回调onStartCommand()方法,再次startService()的时候,就只会执行onStartCommand()。服务一旦开启后,我们就需要通过stopService()方法或者stopSelf()方法关闭服务,这时就会回调onDestroy()

MainActivity.class

1
2
3
4
5
6
7
// start
Intent sIntent = new Intent(this, Myservice.class)
startService(sIntent)

// stop
stopService(sIntent)

MyService.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// startService would invoke onStartCommand method in MyService class
// In general, this method would create sub-thread to running service
public int onStartCommand(Intent intent, int flags, int startId) {
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.i(TAG, "generate random number running");
}
};
Thread thread = new Thread(runnable);
// execute linearly not multi-thread, would stoke program
// thread.run();
// achieve multi-thread
thread.start();
/*
START_STICKY: automatically restart after being killed and calls onStartCommand again, it can accept the new task instead of continuing with the previous task
START_NOT_STICKY: Do not restart after being killed, do not keep the startup state, can stop at any time
START_REDELIVER_INTENT: If there is an unfinished Intent, it is restarted after being killed, and all intents are sent after the restart. After stopSelf releases the maintained Intent.
*/
return START_NOT_STICKY;
}


// stopService
public void onDestroy() {
super.onDestroy();
}

Step 3: Bind & Unbind service

MainActivity.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// bind
private void bindMyService() {
if (myServiceConnection == null) {
myServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.i(TAG, "onServiceConnected");
MyService.MyBinder binder = (MyService.MyBinder) iBinder; myService = ((MyService.MyBinder) iBinder).getService();
}

@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i(TAG, "onServiceDisconnected");
}
};
}

// Intent, new Service Connection
// BIND_AUTO_CREATE: check if there are connections before stoping services

bindService(sIntent, myServiceConnection, BIND_AUTO_CREATE);
}

// unbind
private void unbindMyService() {
unbindService(myServiceConnection);
myServiceConnection = null;
}

MyService.class

bind
Define Binder class

1
2
3
4
5
public class MyBinder extends Binder {
public MyService getService() {
return MyService.this;
}
}

Define MyBinder instance

1
private Binder myBinder = new MyBinder();

Define onBind Method return binder

1
2
3
4
5
@Override
// you can get this binder in creating serviceConnection in MainActivity.class
public IBinder onBind(Intent intent) {
return myBinder;
}

Unbind

1
2
3
4
@Override
public void unbindService(ServiceConnection conn) {
super.unbindService(conn);
}

Demo

ListView

Define Item in the listview

define attribute of every row

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Item {
private String name;
private int img_url;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Item(String name) {
this.name = name;
}

public int getImg_url() {
return img_url;
}

public void setImg_url(int img_url) {
this.img_url = img_url;
}

}

Define xml in each item

show in each row

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">

<ImageView
android:id="@+id/item_image"
android:layout_width="130dp"
android:layout_height="130dp" />


<TextView
android:id="@+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textColor="#0000EE"
android:layout_marginLeft="10dp"
android:layout_marginTop="30dp"
android:textSize="25sp"
android:gravity="center"/>


</LinearLayout>

Define Adapter

Connect the background data to the foreground style

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ItemAdapter extends ArrayAdapter<Item> {
private int resourceId;
public ItemAdapter(Context context,int textViewResourceId, List<Item> objects){
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
Item item = getItem(position);
View view;
ViewHolder viewHolder;

if (convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId, null);

viewHolder = new ViewHolder();
viewHolder.itemImage = (ImageView) view.findViewById(R.id.item_image);
viewHolder.itemName = (TextView) view.findViewById(R.id.item_name);

view.setTag(viewHolder);
}else{
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}


viewHolder.itemImage.setImageResource(item.getImg_url());
viewHolder.itemName.setText(item.getName());
return view;
}

class ViewHolder{
ImageView itemImage;
TextView itemName;
}

}

MainActivity(Omit definition MainActivity.XML)

set Data and click listener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
...
// define data source
List<Item> itemList = new ArrayList<Item>();
Item alpha = new Item("Alpha",R.drawable.alpha, "https://en.wikipedia.org/wiki/Alpha","API 1");
itemList.add(alpha);

// add and set adapter
ItemAdapter adapter = new ItemAdapter(MainActivity.this,R.layout.item, itemList);

ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);


// add click listener
listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){

@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
Item info = itemList.get(position);
Bundle bundle = new Bundle();

bundle.putString("itemName", info.getName());
bundle.putString("itemImage", info.getImg_url() + "");
bundle.putString("itemLink", info.getLink());

Intent intent = new Intent(MainActivity.this,ItemPage.class);
intent.putExtras(bundle);
startActivity(intent);
}
});
...

ToolBar

Hide actionbar to show toolbar

Change theme of this activity in AndroidManifest.xml, and this theme is define in styles.xml

1
2
3
4
// AndroidManifest.xml
<activity android:name=".activity_name"
android:theme="@style/AppTheme.NoActionBar"
></activity>
1
2
3
4
5
// styles.xml
<style name="AppTheme.NoActionBar">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
</style>

Define XML

1
2
3
4
5
6
7
8
9
10
11
// there is a back button in ToolBar
<androidx.appcompat.widget.Toolbar
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:id="@+id/toolbar"
android:background="?colorPrimary"
android:layout_width="fill_parent"
android:layout_height="?actionBarSize"
android:minHeight="?actionBarSize"
app:popupTheme="?actionBarPopupTheme"
app:navigationIcon="?homeAsUpIndicator"
xmlns:app="http://schemas.android.com/apk/res-auto" />

MainActivity

1
2
3
4
5
6
7
8
9
10
11
12
...
mToolbar = (androidx.appcompat.widget.Toolbar) findViewById(R.id.toolbar);
// set support to this toolbar
setSupportActionBar((Toolbar) mToolbar);
// set click listener
((Toolbar) mToolbar).setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
...

WebView

Define XML

1
2
3
4
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

open permission in AndroidManifest.xml

1
<uses-permission android:name="android.permission.INTERNET" />

MainActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
webView = (WebView) findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl(link);


// not jump to external browser
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return super.shouldOverrideUrlLoading(view, url);
}
});

Intent

post

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// add attribute
Bundle bundle = new Bundle();

bundle.putString("itemName", info.getName());
bundle.putString("itemImage", info.getImg_url() + "");
bundle.putString("itemLink", info.getLink());

Intent intent = new Intent(MainActivity.this,ItemPage.class);
// first way post
// intent.putExtra("itemLink",info.getLink());
// second way post
intent.putExtras(bundle);
startActivity(intent);
// if finish() cannot back again
// finish();

get

1
2
3
4
5
6
// first way get
// getIntent().getStringExtra("link");
// second way get
Bundle b=getIntent().getExtras();
//获取Bundle的信息
String link = b.getString("itemLink");

Version Problem between AndroidX and android.support.v7.XXX

CSDN Version Control

Android Studio emulator rotate screen(portrait & landsacpe)

Instruction

Demo

iOS 开发之多线程 GCD

Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。GCD 可用于多核的并行运算;GCD 会自动利用更多的 CPU 内核(比如双核、四核);GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程);

|