It seems to be. This article is the 21st day article of IS17er Advent Calendar.
This time, in commemoration of the release of both iOS / Android versions of the study game app called FFMultiplier, which I had been developing for a long time, I felt that it was difficult to develop which one I felt during the development. I would like to make a thorough comparison.
Click here for the app
-iOS (9.0 or above) -Android (5.0 or above)
I think it was common for people who developed both to express their feelings, so this time I would like to focus on how to realize a certain function technically.
Swift
The timer in Swift basically uses Timer (formerly NSTimer)
. If you want to do the same thing every few seconds
timer
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.updateTime), userInfo: nil, repeats: true)
like this. With the introduction of #selector
, typos have been reduced, and basically it is very convenient because you can write safely without worrying about threads.
Java
Even in Java, timers are basically implemented using the Timer
class. However, as you can see when googled ʻAndroid timer and
Java Timer, there are several ways to pass the
TimerTask class to
Timer`, which may be annoying for beginners. ..
This time it was a countdown of the game, so at first I was thinking of using the same class called CountDownTimer
, but upon closer examination, it seems that this has the drawback that it does not measure exactly 1 second, I settled down on Timer
. If you create a separate class, the writing method itself is similar.
timer
timer.schedule(timerTask, 0, 1000);
However, in the case of Android, it should be noted that the processing of Timer
is executed in another thread, so the drawing processing cannot be written, so the main thread must be called from the processing. In this respect, I felt that the Android timer is a little difficult for beginners.
iOS The grid layout we see this time is a way to create a pure grid that does not include a scroll view like CollectionView in the background.
As a realization method on iOS
--Do it steadily with AutoLayout --Calculated from code and generated --Use the new function StackView --Create with CoollectionView to prevent scrolling
Is there a hit? This time, we adopted StackView. However, I think the drawback is that this feature is not backwards compatible and does not work before iOS 9. However, I was able to arrange it almost as I intended. It's nice that the default is centered and stretched to the whole.
Android For Android, of course, use LinearLayout. This is a function that has been around since the beginning, so it works on any aircraft, and there are many materials on setting ratios, so it's good that the learning cost is low. Even if it isn't, it can be realized quite easily because it has the same function called GridLayout.
It is up to you to set the centering and expanding to the full parent element, but it seems that Android, which was ahead of the game, will be better off in this area.
iOS
It's safe to say that layout files (that is, storyboards) and code strings are not as easy as iOS. Strictly speaking, there are some cases where it is a little inconvenient, such as the timing when AutoLayout is applied and the timing when it is connected to the code must be considered in the life cycle, but basically the learning cost is very low, which is attractive.
You can also associate one @IBAction
with multiple buttons and divide the processing by tag on the GUI.
However, when it comes to processing buttons in CustomView, it's a little tricky. Because it is not possible to directly associate from the parent view controller, it cannot be realized without some ingenuity in the case of processing that affects the elements of the view controller. I think I came up with it
--Create a Delegate --Pass ViewController itself as a property --Pass a closure with no return value --Associate as an Outlet and add a touch event from the code
In my case, I feel that I prefer to use the third method exclusively these days. That is, in CustomView
In Custom View
var action: (()->())!
It is a method of declaring it like this and passing the process when instantiating.
Android
On Android, there is basically a file called R that manages all at once, and it feels like registering an id from xml and fetching elements using findViewById
on the code.
Since it has to be written in code, it is a little troublesome, but instead, if you write it properly, you can associate it at any time you like.
Also, in the past, the ʻonClick event was always done with
setOnClickListener, but Android Studio has officially made it possible to set the ʻonClick
method from xml.
I think we can really say that there are advantages and disadvantages in this field.
iOS When you use fonts, especially your favorite fonts that aren't included by default, you can use them from your code or storyboard by putting them in your project and setting the build phase copy bundle resources on iOS. It's easy.
Android
Custom fonts are a bit tricky for Android. First you have to put the font you want to put in assets and then usually create a custom class for the element you want to change the font. Moreover, if you do it without thinking about it, the font file will be copied every time you use it, so you have to make something like FontHolder
and manage Typeface
.
There is a library called Calligraphy to solve this problem. This makes it easy to manage without creating custom classes.
I really want iOS to come here ... sweat
iOS Use ʻUIAlertController` to raise an alert. Simple elements such as text fields can be set from code. For example, OK, Cancel button, and one text field alert
Alert
let alert = UIAlertController(title: "register name", message: "please set your username", preferredStyle: .alert)
alert.addTextField {
textField in
textField.placeholder = "user name"
}
alert.addAction(UIAlertAction(title: "OK", style: .default) {
_ in
let textfield = alert.textFields?.first
if let name = textfield?.text {
self.storage.set(name, forKey: "playername")
}
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
It's easy.
Android Android is ʻAlert Dialog`. However, since text fields etc. are not prepared, I will create xml and apply it.
AlertDialog
LayoutInflater inflater = LayoutInflater.from(gameActivity);
View dialog = inflater.inflate(R.layout.input_dialog, null);
final EditText editText = (EditText)
dialog.findViewById(R.id.editNameText);
new AlertDialog.Builder(gameActivity).setTitle("please set your name").setView(dialog).setPositiveButton("ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
final String userName = editText.getText().toString();
sp.edit().putString("name", userName).commit();
}
}).show();
It's a little more troublesome, but you can add what you like to make it, so you can say that you have a lot of freedom.
iOS The pop-up type has a lot of restrictions such as having to have a parent element if it is the default one, so I think it is best to use a library to display the View on the top.
That's why I used this.
If you prepare a layout with Storyboard or xib, you can display it quite easily.
Android On the other hand, I think there are various ways to realize it on Android, but there is a function called PopupWindow.
This allows you to achieve what you want to do, although there are a few setting items.
However, it may be easier to customize the AlertDialog for this item ...
Basically, both are handled in the same way, but there are differences depending on the characteristics of the language in terms of usage that is somewhat easy to handle. For example, when registering
Firebase/setvalue
ref.child("hello").child(device_id).setValue(["id": id, "rank": rank as NSNumber], andPriority: -newScore.score)
In this way, you can register using Dictionary on iOS. On the other hand, Android can be used in the same way by using Map <String, Object>
, but this is used in the hash tree and is not purely standard, so instead define the class as follows. hand
Firebase/class
@IgnoreExtraProperties
public class DatabaseScore {
public String id;
public int rank;
public DatabaseScore() {}
public DatabaseScore(String id, int rank) {
this.id = id;
this.rank = rank;
}
public int getId() {
return id;
}
public String getRank() {
return rank;
}
}
By passing an instance of this, the variable name is automatically converted to a key and handled. Like this
Firebase/setvalue
ref.child("hello").child(id).setValue(databaseScore);
When reading the database, Swift is ʻobserve (.value, with: handler) and Android is ʻaddValueEventListener (valueEventListener)
. Each characteristic has come out.
It can be read in the same format as when it was registered.
There is a similar tendency to how to handle Realm with Firebase.
Let's compare simple reading and writing. First, on iOS, it looks like this:
Realm model
import RealmSwift
final class Score: Object {
dynamic var date = NSDate(timeIntervalSince1970: 1)
dynamic var score: Int = 0
}
First, define the model with this. You added the dynamic
modifier to what you wanted to save for Realm's internal processing. And
Realm write
let newScore = Score(value: ["date": NSDate(), "score": acceptedNum])
let realm = try! Realm()
try! realm.write {
realm.add(newScore)
}
If you enter the write thread with try
and add it like this
Realm load
let scores = realm.objects(Score.self).sorted(byProperty: "score", ascending: false)
In this way, it was possible to retrieve what was saved by the method called ʻobjects`.
Then let's do the same on Android. Then the model / write / read will be as follows.
Realm model
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
import java.util.Date;
public class ScoreModel extends RealmObject {
private int score;
private Date date;
public void setScore(int score) {
this.score = score;
}
public void setDate(Date date) {
this.date = date;
}
public Date getDate() {
return date;
}
public int getScore() {
return score;
}
}
Realm writing
final ScoreModel scoreModel = new ScoreModel();
scoreModel.setScore(correctCnt * 10);
scoreModel.setDate(cal.getTime());
Realm realm = Realm.getDefaultInstance();
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.copyToRealm(scoreModel);
}
});
Loading Realm
RealmResults<ScoreModel> results = realm.where(ScoreModel.class).findAllSorted("score", Sort.DESCENDING);
The flow is the same, but it is characteristic that it is necessary to set getter and setter in the model first, writing as asynchronous processing in the interface, and the result is returned in the type called RealmResults. I think.
It's hard to say which one is better, but I get the impression that Swift is more concise.
iOS It's 11,800 yen a year. The payment method is an Apple Store gift card or credit card, so if you don't have a credit card, you can have an event to go to the Apple Store every year.
No, it ’s expensive. .. ..
Android There is no registration fee other than $ 25. Honest! Payment is by credit card or debit card, but it seems that people who do not have a credit card can pay like a prepaid card at a convenience store by using the VISA service called V Preca.
Nowadays, smartphone apps have become the mainstay of application development, but it seems that it is still quite difficult to achieve similar things with both.
Especially in Android, there are many situations where you need to be aware of threads, and Java is not null-safe, so you need to be careful about that. On the other hand, if you don't master iOS properly, the degree of freedom will drop considerably.
In that sense, the emergence of tools such as Xamarin that can create both binaries with a single code will be a turning point in future mobile development. However, I think it will be very educational to study so that you can do the same thing with both, so please take this opportunity to challenge yourself! !!
Recommended Posts